summaryrefslogtreecommitdiff
path: root/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime
diff options
context:
space:
mode:
authorAndrew Lee <alee14498@protonmail.com>2020-04-19 17:19:32 -0400
committerAndrew Lee <alee14498@protonmail.com>2020-04-19 17:19:32 -0400
commitc55fba8ab2a1c9d3df65eda4a5a1e957f4aa1f78 (patch)
treeee4d51c7c1d633e11f46453ef1edd3c77c4ef9f7 /Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime
downloadProject-Sandbox-c55fba8ab2a1c9d3df65eda4a5a1e957f4aa1f78.tar.gz
Project-Sandbox-c55fba8ab2a1c9d3df65eda4a5a1e957f4aa1f78.tar.bz2
Project-Sandbox-c55fba8ab2a1c9d3df65eda4a5a1e957f4aa1f78.zip
Inital commit
Diffstat (limited to 'Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime')
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/AssemblyInfo.cs.cs11
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/AssemblyInfo.cs.cs.meta11
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/FastAction.cs146
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/FastAction.cs.meta12
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/MaterialReferenceManager.cs644
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/MaterialReferenceManager.cs.meta12
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_Asset.cs26
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_Asset.cs.meta12
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_Character.cs51
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_Character.cs.meta11
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_CharacterInfo.cs73
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_CharacterInfo.cs.meta11
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_ColorGradient.cs68
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_ColorGradient.cs.meta12
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_CoroutineTween.cs246
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_CoroutineTween.cs.meta12
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_DefaultControls.cs385
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_DefaultControls.cs.meta12
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_Dropdown.cs1059
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_Dropdown.cs.meta11
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_EditorResourceManager.cs142
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_EditorResourceManager.cs.meta11
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_FontAsset.cs1948
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_FontAsset.cs.meta11
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_FontAssetCommon.cs456
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_FontAssetCommon.cs.meta10
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_FontAssetUtilities.cs360
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_FontAssetUtilities.cs.meta11
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_FontFeatureTable.cs55
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_FontFeatureTable.cs.meta11
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_FontFeaturesCommon.cs223
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_FontFeaturesCommon.cs.meta11
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_InputField.cs4149
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_InputField.cs.meta11
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_InputValidator.cs15
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_InputValidator.cs.meta12
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_LineInfo.cs52
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_LineInfo.cs.meta12
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_ListPool.cs21
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_ListPool.cs.meta12
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_MaterialManager.cs626
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_MaterialManager.cs.meta10
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_MeshInfo.cs668
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_MeshInfo.cs.meta12
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_ObjectPool.cs51
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_ObjectPool.cs.meta12
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_PackageResourceImporter.cs212
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_PackageResourceImporter.cs.meta16
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_RichTextTagStack.cs278
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_RichTextTagStack.cs.meta12
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_RichTextTagsCommon.cs113
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_RichTextTagsCommon.cs.meta11
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_ScrollbarEventHandler.cs31
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_ScrollbarEventHandler.cs.meta12
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SelectionCaret.cs23
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SelectionCaret.cs.meta12
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_Settings.cs440
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_Settings.cs.meta12
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_ShaderUtilities.cs563
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_ShaderUtilities.cs.meta10
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_Sprite.cs31
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_Sprite.cs.meta10
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SpriteAnimator.cs147
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SpriteAnimator.cs.meta12
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SpriteAsset.cs503
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SpriteAsset.cs.meta11
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SpriteAssetImportFormats.cs61
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SpriteAssetImportFormats.cs.meta12
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SpriteCharacter.cs74
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SpriteCharacter.cs.meta11
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SpriteGlyph.cs61
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SpriteGlyph.cs.meta11
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_Style.cs95
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_Style.cs.meta12
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_StyleSheet.cs131
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_StyleSheet.cs.meta13
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SubMesh.cs580
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SubMesh.cs.meta12
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SubMeshUI.cs807
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SubMeshUI.cs.meta12
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_Text.cs7594
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_Text.cs.meta12
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_TextElement.cs62
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_TextElement.cs.meta11
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_TextElement_Legacy.cs25
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_TextElement_Legacy.cs.meta12
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_TextInfo.cs256
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_TextInfo.cs.meta12
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_TextParsingUtilities.cs136
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_TextParsingUtilities.cs.meta11
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_TextUtilities.cs2279
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_TextUtilities.cs.meta12
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_UpdateManager.cs238
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_UpdateManager.cs.meta12
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_UpdateRegistery.cs178
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_UpdateRegistery.cs.meta12
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMPro_EventManager.cs150
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMPro_EventManager.cs.meta10
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMPro_ExtensionMethods.cs224
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMPro_ExtensionMethods.cs.meta10
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMPro_MeshUtilities.cs357
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMPro_MeshUtilities.cs.meta10
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMPro_Private.cs4051
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMPro_Private.cs.meta10
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMPro_UGUI_Private.cs4314
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMPro_UGUI_Private.cs.meta10
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TextContainer.cs370
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TextContainer.cs.meta12
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TextMeshPro.cs551
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TextMeshPro.cs.meta39
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TextMeshProUGUI.cs656
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TextMeshProUGUI.cs.meta11
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/Unity.TextMeshPro.asmdef13
-rw-r--r--Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/Unity.TextMeshPro.asmdef.meta7
114 files changed, 37755 insertions, 0 deletions
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/AssemblyInfo.cs.cs b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/AssemblyInfo.cs.cs
new file mode 100644
index 0000000..472129d
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/AssemblyInfo.cs.cs
@@ -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
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/AssemblyInfo.cs.cs.meta b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/AssemblyInfo.cs.cs.meta
new file mode 100644
index 0000000..b8b0156
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/AssemblyInfo.cs.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 1c147d10db452eb4b854a35f84472017
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/FastAction.cs b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/FastAction.cs
new file mode 100644
index 0000000..15012e3
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/FastAction.cs
@@ -0,0 +1,146 @@
+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)
+ {
+ if (lookup.TryGetValue(rhs, out LinkedListNode<System.Action> 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)
+ {
+ if (lookup.TryGetValue(rhs, out LinkedListNode<System.Action<A>> 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)
+ {
+ if (lookup.TryGetValue(rhs, out LinkedListNode<System.Action<A, B>> 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)
+ {
+ if (lookup.TryGetValue(rhs, out LinkedListNode<System.Action<A, B, C>> 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;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/FastAction.cs.meta b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/FastAction.cs.meta
new file mode 100644
index 0000000..cfbd1a6
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/FastAction.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 871f8edd56e84b8fb295b10cc3c78f36
+timeCreated: 1435956061
+licenseType: Store
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/MaterialReferenceManager.cs b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/MaterialReferenceManager.cs
new file mode 100644
index 0000000..dc1ec2f
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/MaterialReferenceManager.cs
@@ -0,0 +1,644 @@
+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)
+ {
+ if (m_FontAssetReferenceLookup.ContainsKey(font.hashCode))
+ return true;
+
+ return false;
+ }
+
+
+ /// <summary>
+ /// Function to check if the sprite asset is already referenced.
+ /// </summary>
+ /// <param name="font"></param>
+ /// <returns></returns>
+ public bool Contains(TMP_SpriteAsset sprite)
+ {
+ if (m_FontAssetReferenceLookup.ContainsKey(sprite.hashCode))
+ return true;
+
+ return false;
+ }
+
+
+
+ /// <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;
+
+ if (m_FontAssetReferenceLookup.TryGetValue(hashCode, out fontAsset))
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+
+
+ /// <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;
+
+ if (m_SpriteAssetReferenceLookup.TryGetValue(hashCode, out spriteAsset))
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+
+ /// <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;
+
+ if (m_ColorGradientReferenceLookup.TryGetValue(hashCode, out gradientPreset))
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+
+ /// <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;
+
+ if (m_FontMaterialReferenceLookup.TryGetValue(hashCode, out material))
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+
+ /// <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 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, MaterialReference[] materialReferences, Dictionary<int, int> materialReferenceIndexLookup)
+ {
+ int materialID = material.GetInstanceID();
+
+ if (materialReferenceIndexLookup.TryGetValue(materialID, out int index))
+ {
+ return index;
+ }
+ else
+ {
+ index = materialReferenceIndexLookup.Count;
+
+ // Add new reference index
+ materialReferenceIndexLookup[materialID] = index;
+
+ 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, MaterialReference[] materialReferences, Dictionary<int, int> materialReferenceIndexLookup)
+ {
+ int materialID = material.GetInstanceID();
+
+ if (materialReferenceIndexLookup.TryGetValue(materialID, out int index))
+ {
+ return index;
+ }
+ else
+ {
+ index = materialReferenceIndexLookup.Count;
+
+ // Add new reference index
+ materialReferenceIndexLookup[materialID] = index;
+
+ 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;
+ }
+ }
+ }
+}
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/MaterialReferenceManager.cs.meta b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/MaterialReferenceManager.cs.meta
new file mode 100644
index 0000000..31dbeee
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/MaterialReferenceManager.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 11a6a034ab84493cbed6af5ae7aae78b
+timeCreated: 1449743129
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_Asset.cs b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_Asset.cs
new file mode 100644
index 0000000..d84ee7f
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_Asset.cs
@@ -0,0 +1,26 @@
+using UnityEngine;
+
+namespace TMPro
+{
+
+ // Base class inherited by the various TextMeshPro Assets.
+ [System.Serializable]
+ public class TMP_Asset : ScriptableObject
+ {
+ /// <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;
+
+ }
+}
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_Asset.cs.meta b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_Asset.cs.meta
new file mode 100644
index 0000000..44fe8a4
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_Asset.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 3bda1886f58f4e0ab1139400b160c3ee
+timeCreated: 1459318952
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_Character.cs b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_Character.cs
new file mode 100644
index 0000000..0c29264
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_Character.cs
@@ -0,0 +1,51 @@
+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.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.glyph = null;
+ this.glyphIndex = glyphIndex;
+ this.scale = 1.0f;
+ }
+ }
+}
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_Character.cs.meta b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_Character.cs.meta
new file mode 100644
index 0000000..11a7d3a
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_Character.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 4ac5b6a65aaeb59478e3b78660e9f134
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_CharacterInfo.cs b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_CharacterInfo.cs
new file mode 100644
index 0000000..14e2361
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_CharacterInfo.cs
@@ -0,0 +1,73 @@
+using UnityEngine;
+using UnityEngine.TextCore;
+
+namespace TMPro
+{
+ public struct TMP_Vertex
+ {
+ public Vector3 position;
+ public Vector2 uv;
+ public Vector2 uv2;
+ public Vector2 uv4;
+ public Color32 color;
+
+ //public Vector3 normal;
+ //public Vector4 tangent;
+ }
+
+ /// <summary>
+ /// Structure containing information about individual text elements (character or sprites).
+ /// </summary>
+ public struct TMP_CharacterInfo
+ {
+ public char character; // Should be changed to an int to handle UTF 32
+ /// <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 ascender;
+ public float baseLine;
+ public float descender;
+
+ public float xAdvance;
+ public float aspectRatio;
+ public float scale;
+ public Color32 color;
+ public Color32 underlineColor;
+ public Color32 strikethroughColor;
+ public Color32 highlightColor;
+ public FontStyles style;
+ public bool isVisible;
+ //public bool isIgnoringAlignment;
+ }
+}
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_CharacterInfo.cs.meta b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_CharacterInfo.cs.meta
new file mode 100644
index 0000000..8280ad9
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_CharacterInfo.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 90fe1c65e6bb3bc4e90862df7297719e
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_ColorGradient.cs b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_ColorGradient.cs
new file mode 100644
index 0000000..e1d0907
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_ColorGradient.cs
@@ -0,0 +1,68 @@
+using UnityEngine;
+using System.Collections;
+
+namespace TMPro
+{
+ public enum ColorMode
+ {
+ Single,
+ HorizontalGradient,
+ VerticalGradient,
+ FourCornersGradient
+ }
+
+ [System.Serializable]
+ 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;
+ }
+ }
+}
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_ColorGradient.cs.meta b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_ColorGradient.cs.meta
new file mode 100644
index 0000000..28414b6
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_ColorGradient.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 54d21f6ece3b46479f0c328f8c6007e0
+timeCreated: 1468187202
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_CoroutineTween.cs b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_CoroutineTween.cs
new file mode 100644
index 0000000..6d72340
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_CoroutineTween.cs
@@ -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;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_CoroutineTween.cs.meta b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_CoroutineTween.cs.meta
new file mode 100644
index 0000000..6c35b49
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_CoroutineTween.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 658c1fb149e7498aa072b0c0f3bf13f0
+timeCreated: 1464850953
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_DefaultControls.cs b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_DefaultControls.cs
new file mode 100644
index 0000000..abf3d9a
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_DefaultControls.cs
@@ -0,0 +1,385 @@
+using UnityEngine;
+using System.Collections;
+using UnityEngine.UI;
+
+
+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_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 = CreateUIElementRoot("Text (TMP)", s_ThickElementSize);
+
+ TextMeshProUGUI lbl = go.AddComponent<TextMeshProUGUI>();
+ lbl.text = "New Text";
+ SetDefaultTextValues(lbl);
+
+ 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
+ textArea.AddComponent<RectMask2D>();
+
+ 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;
+
+ 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;
+ }
+ }
+}
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_DefaultControls.cs.meta b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_DefaultControls.cs.meta
new file mode 100644
index 0000000..6046ed2
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_DefaultControls.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 322392995be44d23a3c86cfd972f838f
+timeCreated: 1446378357
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_Dropdown.cs b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_Dropdown.cs
new file mode 100644
index 0000000..6424a5a
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_Dropdown.cs
@@ -0,0 +1,1059 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using UnityEngine.UI;
+using UnityEngine.Events;
+using UnityEngine.EventSystems;
+using UnityEngine.UI.CoroutineTween;
+
+namespace TMPro
+{
+ [AddComponentMenu("UI/Dropdown - TextMeshPro", 35)]
+ [RequireComponent(typeof(RectTransform))]
+ /// <summary>
+ /// A standard dropdown that presents a list of options when clicked, of which one can be chosen.
+ /// </summary>
+ /// <remarks>
+ /// The dropdown component is a Selectable. When an option is chosen, the label and/or image of the control changes to show the chosen option.
+ ///
+ /// When a dropdown event occurs a callback is sent to any registered listeners of onValueChanged.
+ /// </remarks>
+ public class TMP_Dropdown : Selectable, IPointerClickHandler, ISubmitHandler, ICancelHandler
+ {
+ protected internal class DropdownItem : MonoBehaviour, IPointerEnterHandler, ICancelHandler
+ {
+ [SerializeField]
+ private TMP_Text m_Text;
+ [SerializeField]
+ private Image m_Image;
+ [SerializeField]
+ private RectTransform m_RectTransform;
+ [SerializeField]
+ private Toggle m_Toggle;
+
+ public TMP_Text text { get { return m_Text; } set { m_Text = value; } }
+ public Image image { get { return m_Image; } set { m_Image = value; } }
+ public RectTransform rectTransform { get { return m_RectTransform; } set { m_RectTransform = value; } }
+ public Toggle toggle { get { return m_Toggle; } set { m_Toggle = value; } }
+
+ public virtual void OnPointerEnter(PointerEventData eventData)
+ {
+ EventSystem.current.SetSelectedGameObject(gameObject);
+ }
+
+ public virtual void OnCancel(BaseEventData eventData)
+ {
+ TMP_Dropdown dropdown = GetComponentInParent<TMP_Dropdown>();
+ if (dropdown)
+ dropdown.Hide();
+ }
+ }
+
+ [Serializable]
+ /// <summary>
+ /// Class to store the text and/or image of a single option in the dropdown list.
+ /// </summary>
+ public class OptionData
+ {
+ [SerializeField]
+ private string m_Text;
+ [SerializeField]
+ private Sprite m_Image;
+
+ /// <summary>
+ /// The text associated with the option.
+ /// </summary>
+ public string text { get { return m_Text; } set { m_Text = value; } }
+
+ /// <summary>
+ /// The image associated with the option.
+ /// </summary>
+ public Sprite image { get { return m_Image; } set { m_Image = value; } }
+
+ public OptionData() { }
+
+ public OptionData(string text)
+ {
+ this.text = text;
+ }
+
+ public OptionData(Sprite image)
+ {
+ this.image = image;
+ }
+
+ /// <summary>
+ /// Create an object representing a single option for the dropdown list.
+ /// </summary>
+ /// <param name="text">Optional text for the option.</param>
+ /// <param name="image">Optional image for the option.</param>
+ public OptionData(string text, Sprite image)
+ {
+ this.text = text;
+ this.image = image;
+ }
+ }
+
+ [Serializable]
+ /// <summary>
+ /// Class used internally to store the list of options for the dropdown list.
+ /// </summary>
+ /// <remarks>
+ /// The usage of this class is not exposed in the runtime API. It's only relevant for the PropertyDrawer drawing the list of options.
+ /// </remarks>
+ public class OptionDataList
+ {
+ [SerializeField]
+ private List<OptionData> m_Options;
+
+ /// <summary>
+ /// The list of options for the dropdown list.
+ /// </summary>
+ public List<OptionData> options { get { return m_Options; } set { m_Options = value; } }
+
+
+ public OptionDataList()
+ {
+ options = new List<OptionData>();
+ }
+ }
+
+ [Serializable]
+ /// <summary>
+ /// UnityEvent callback for when a dropdown current option is changed.
+ /// </summary>
+ public class DropdownEvent : UnityEvent<int> { }
+
+ // Template used to create the dropdown.
+ [SerializeField]
+ private RectTransform m_Template;
+
+ /// <summary>
+ /// The Rect Transform of the template for the dropdown list.
+ /// </summary>
+ public RectTransform template { get { return m_Template; } set { m_Template = value; RefreshShownValue(); } }
+
+ // Text to be used as a caption for the current value. It's not required, but it's kept here for convenience.
+ [SerializeField]
+ private TMP_Text m_CaptionText;
+
+ /// <summary>
+ /// The Text component to hold the text of the currently selected option.
+ /// </summary>
+ public TMP_Text captionText { get { return m_CaptionText; } set { m_CaptionText = value; RefreshShownValue(); } }
+
+ [SerializeField]
+ private Image m_CaptionImage;
+
+ /// <summary>
+ /// The Image component to hold the image of the currently selected option.
+ /// </summary>
+ public Image captionImage { get { return m_CaptionImage; } set { m_CaptionImage = value; RefreshShownValue(); } }
+
+ [Space]
+
+ [SerializeField]
+ private TMP_Text m_ItemText;
+
+ /// <summary>
+ /// The Text component to hold the text of the item.
+ /// </summary>
+ public TMP_Text itemText { get { return m_ItemText; } set { m_ItemText = value; RefreshShownValue(); } }
+
+ [SerializeField]
+ private Image m_ItemImage;
+
+ /// <summary>
+ /// The Image component to hold the image of the item
+ /// </summary>
+ public Image itemImage { get { return m_ItemImage; } set { m_ItemImage = value; RefreshShownValue(); } }
+
+ [Space]
+
+ [SerializeField]
+ private int m_Value;
+
+ [Space]
+
+ // Items that will be visible when the dropdown is shown.
+ // We box this into its own class so we can use a Property Drawer for it.
+ [SerializeField]
+ private OptionDataList m_Options = new OptionDataList();
+
+ /// <summary>
+ /// The list of possible options. A text string and an image can be specified for each option.
+ /// </summary>
+ /// <remarks>
+ /// This is the list of options within the Dropdown. Each option contains Text and/or image data that you can specify using UI.Dropdown.OptionData before adding to the Dropdown list.
+ /// This also unlocks the ability to edit the Dropdown, including the insertion, removal, and finding of options, as well as other useful tools
+ /// </remarks>
+ /// /// <example>
+ /// <code>
+ /// //Create a new Dropdown GameObject by going to the Hierarchy and clicking Create>UI>Dropdown - TextMeshPro. Attach this script to the Dropdown GameObject.
+ ///
+ /// using UnityEngine;
+ /// using UnityEngine.UI;
+ /// using System.Collections.Generic;
+ /// using TMPro;
+ ///
+ /// public class Example : MonoBehaviour
+ /// {
+ /// //Use these for adding options to the Dropdown List
+ /// TMP_Dropdown.OptionData m_NewData, m_NewData2;
+ /// //The list of messages for the Dropdown
+ /// List<TMP_Dropdown.OptionData> m_Messages = new List<TMP_Dropdown.OptionData>();
+ ///
+ ///
+ /// //This is the Dropdown
+ /// TMP_Dropdown m_Dropdown;
+ /// string m_MyString;
+ /// int m_Index;
+ ///
+ /// void Start()
+ /// {
+ /// //Fetch the Dropdown GameObject the script is attached to
+ /// m_Dropdown = GetComponent<TMP_Dropdown>();
+ /// //Clear the old options of the Dropdown menu
+ /// m_Dropdown.ClearOptions();
+ ///
+ /// //Create a new option for the Dropdown menu which reads "Option 1" and add to messages List
+ /// m_NewData = new TMP_Dropdown.OptionData();
+ /// m_NewData.text = "Option 1";
+ /// m_Messages.Add(m_NewData);
+ ///
+ /// //Create a new option for the Dropdown menu which reads "Option 2" and add to messages List
+ /// m_NewData2 = new TMP_Dropdown.OptionData();
+ /// m_NewData2.text = "Option 2";
+ /// m_Messages.Add(m_NewData2);
+ ///
+ /// //Take each entry in the message List
+ /// foreach (TMP_Dropdown.OptionData message in m_Messages)
+ /// {
+ /// //Add each entry to the Dropdown
+ /// m_Dropdown.options.Add(message);
+ /// //Make the index equal to the total number of entries
+ /// m_Index = m_Messages.Count - 1;
+ /// }
+ /// }
+ ///
+ /// //This OnGUI function is used here for a quick demonstration. See the [[wiki:UISystem|UI Section]] for more information about setting up your own UI.
+ /// void OnGUI()
+ /// {
+ /// //TextField for user to type new entry to add to Dropdown
+ /// m_MyString = GUI.TextField(new Rect(0, 40, 100, 40), m_MyString);
+ ///
+ /// //Press the "Add" Button to add a new entry to the Dropdown
+ /// if (GUI.Button(new Rect(0, 0, 100, 40), "Add"))
+ /// {
+ /// //Make the index the last number of entries
+ /// m_Index = m_Messages.Count;
+ /// //Create a temporary option
+ /// TMP_Dropdown.OptionData temp = new TMP_Dropdown.OptionData();
+ /// //Make the option the data from the TextField
+ /// temp.text = m_MyString;
+ ///
+ /// //Update the messages list with the TextField data
+ /// m_Messages.Add(temp);
+ ///
+ /// //Add the Textfield data to the Dropdown
+ /// m_Dropdown.options.Insert(m_Index, temp);
+ /// }
+ ///
+ /// //Press the "Remove" button to delete the selected option
+ /// if (GUI.Button(new Rect(110, 0, 100, 40), "Remove"))
+ /// {
+ /// //Remove the current selected item from the Dropdown from the messages List
+ /// m_Messages.RemoveAt(m_Dropdown.value);
+ /// //Remove the current selection from the Dropdown
+ /// m_Dropdown.options.RemoveAt(m_Dropdown.value);
+ /// }
+ /// }
+ /// }
+ /// </code>
+ /// </example>
+ public List<OptionData> options
+ {
+ get { return m_Options.options; }
+ set { m_Options.options = value; RefreshShownValue(); }
+ }
+
+ [Space]
+
+ // Notification triggered when the dropdown changes.
+ [SerializeField]
+ private DropdownEvent m_OnValueChanged = new DropdownEvent();
+
+ /// <summary>
+ /// A UnityEvent that is invoked when a user has clicked one of the options in the dropdown list.
+ /// </summary>
+ /// <remarks>
+ /// Use this to detect when a user selects one or more options in the Dropdown. Add a listener to perform an action when this UnityEvent detects a selection by the user. See https://unity3d.com/learn/tutorials/topics/scripting/delegates for more information on delegates.
+ /// </remarks>
+ /// <example>
+ /// <code>
+ /// //Create a new Dropdown GameObject by going to the Hierarchy and clicking Create>UI>Dropdown - TextMeshPro. Attach this script to the Dropdown GameObject.
+ /// //Set your own Text in the Inspector window
+ ///
+ /// using UnityEngine;
+ /// using UnityEngine.UI;
+ /// using TMPro;
+ ///
+ /// public class Example : MonoBehaviour
+ /// {
+ /// TMP_Dropdown m_Dropdown;
+ /// public Text m_Text;
+ ///
+ /// void Start()
+ /// {
+ /// //Fetch the Dropdown GameObject
+ /// m_Dropdown = GetComponent<TMP_Dropdown>();
+ /// //Add listener for when the value of the Dropdown changes, to take action
+ /// m_Dropdown.onValueChanged.AddListener(delegate {
+ /// DropdownValueChanged(m_Dropdown);
+ /// });
+ ///
+ /// //Initialize the Text to say the first value of the Dropdown
+ /// m_Text.text = "First Value : " + m_Dropdown.value;
+ /// }
+ ///
+ /// //Output the new value of the Dropdown into Text
+ /// void DropdownValueChanged(TMP_Dropdown change)
+ /// {
+ /// m_Text.text = "New Value : " + change.value;
+ /// }
+ /// }
+ /// </code>
+ /// </example>
+ public DropdownEvent onValueChanged { get { return m_OnValueChanged; } set { m_OnValueChanged = value; } }
+
+ private GameObject m_Dropdown;
+ private GameObject m_Blocker;
+ private List<DropdownItem> m_Items = new List<DropdownItem>();
+ private TweenRunner<FloatTween> m_AlphaTweenRunner;
+ private bool validTemplate = false;
+
+ private static OptionData s_NoOptionData = new OptionData();
+
+ /// <summary>
+ /// The Value is the index number of the current selection in the Dropdown. 0 is the first option in the Dropdown, 1 is the second, and so on.
+ /// </summary>
+ /// <example>
+ /// <code>
+ /// //Create a new Dropdown GameObject by going to the Hierarchy and clicking Create>UI>Dropdown - TextMeshPro. Attach this script to the Dropdown GameObject.
+ /// //Set your own Text in the Inspector window
+ ///
+ /// using UnityEngine;
+ /// using UnityEngine.UI;
+ /// using TMPro;
+ ///
+ /// public class Example : MonoBehaviour
+ /// {
+ /// //Attach this script to a Dropdown GameObject
+ /// TMP_Dropdown m_Dropdown;
+ /// //This is the string that stores the current selection m_Text of the Dropdown
+ /// string m_Message;
+ /// //This Text outputs the current selection to the screen
+ /// public Text m_Text;
+ /// //This is the index value of the Dropdown
+ /// int m_DropdownValue;
+ ///
+ /// void Start()
+ /// {
+ /// //Fetch the DropDown component from the GameObject
+ /// m_Dropdown = GetComponent<TMP_Dropdown>();
+ /// //Output the first Dropdown index value
+ /// Debug.Log("Starting Dropdown Value : " + m_Dropdown.value);
+ /// }
+ ///
+ /// void Update()
+ /// {
+ /// //Keep the current index of the Dropdown in a variable
+ /// m_DropdownValue = m_Dropdown.value;
+ /// //Change the message to say the name of the current Dropdown selection using the value
+ /// m_Message = m_Dropdown.options[m_DropdownValue].text;
+ /// //Change the on screen Text to reflect the current Dropdown selection
+ /// m_Text.text = m_Message;
+ /// }
+ /// }
+ /// </code>
+ /// </example>
+ public int value
+ {
+ get
+ {
+ return m_Value;
+ }
+ set
+ {
+ SetValue(value);
+ }
+ }
+
+ /// <summary>
+ /// Set index number of the current selection in the Dropdown without invoking onValueChanged callback.
+ /// </summary>
+ /// <param name="input">The new index for the current selection.</param>
+ public void SetValueWithoutNotify(int input)
+ {
+ SetValue(input, false);
+ }
+
+ void SetValue(int value, bool sendCallback = true)
+ {
+ if (Application.isPlaying && (value == m_Value || options.Count == 0))
+ return;
+
+ m_Value = Mathf.Clamp(value, 0, options.Count - 1);
+ RefreshShownValue();
+
+ if (sendCallback)
+ {
+ // Notify all listeners
+ UISystemProfilerApi.AddMarker("Dropdown.value", this);
+ m_OnValueChanged.Invoke(m_Value);
+ }
+ }
+
+ public bool IsExpanded { get { return m_Dropdown != null; } }
+
+ protected TMP_Dropdown() { }
+
+ protected override void Awake()
+ {
+ #if UNITY_EDITOR
+ if (!Application.isPlaying)
+ return;
+ #endif
+
+ m_AlphaTweenRunner = new TweenRunner<FloatTween>();
+ m_AlphaTweenRunner.Init(this);
+
+ if (m_CaptionImage)
+ m_CaptionImage.enabled = (m_CaptionImage.sprite != null);
+
+ if (m_Template)
+ m_Template.gameObject.SetActive(false);
+ }
+
+ protected override void Start()
+ {
+ base.Start();
+
+ RefreshShownValue();
+ }
+
+ #if UNITY_EDITOR
+ protected override void OnValidate()
+ {
+ base.OnValidate();
+
+ if (!IsActive())
+ return;
+
+ RefreshShownValue();
+ }
+ #endif
+
+ protected override void OnDisable()
+ {
+ //Destroy dropdown and blocker in case user deactivates the dropdown when they click an option (case 935649)
+ ImmediateDestroyDropdownList();
+
+ if (m_Blocker != null)
+ DestroyBlocker(m_Blocker);
+ m_Blocker = null;
+ }
+
+ /// <summary>
+ /// Refreshes the text and image (if available) of the currently selected option.
+ /// </summary>
+ /// <remarks>
+ /// If you have modified the list of options, you should call this method afterwards to ensure that the visual state of the dropdown corresponds to the updated options.
+ /// </remarks>
+ public void RefreshShownValue()
+ {
+ OptionData data = s_NoOptionData;
+
+ if (options.Count > 0)
+ data = options[Mathf.Clamp(m_Value, 0, options.Count - 1)];
+
+ if (m_CaptionText)
+ {
+ if (data != null && data.text != null)
+ m_CaptionText.text = data.text;
+ else
+ m_CaptionText.text = "";
+ }
+
+ if (m_CaptionImage)
+ {
+ if (data != null)
+ m_CaptionImage.sprite = data.image;
+ else
+ m_CaptionImage.sprite = null;
+ m_CaptionImage.enabled = (m_CaptionImage.sprite != null);
+ }
+ }
+
+ /// <summary>
+ /// Add multiple options to the options of the Dropdown based on a list of OptionData objects.
+ /// </summary>
+ /// <param name="options">The list of OptionData to add.</param>
+ /// /// <remarks>
+ /// See AddOptions(List<string> options) for code example of usages.
+ /// </remarks>
+ public void AddOptions(List<OptionData> options)
+ {
+ this.options.AddRange(options);
+ RefreshShownValue();
+ }
+
+ /// <summary>
+ /// Add multiple text-only options to the options of the Dropdown based on a list of strings.
+ /// </summary>
+ /// <remarks>
+ /// Add a List of string messages to the Dropdown. The Dropdown shows each member of the list as a separate option.
+ /// </remarks>
+ /// <param name="options">The list of text strings to add.</param>
+ /// <example>
+ /// <code>
+ /// //Create a new Dropdown GameObject by going to the Hierarchy and clicking Create>UI>Dropdown - TextMeshPro. Attach this script to the Dropdown GameObject.
+ ///
+ /// using System.Collections.Generic;
+ /// using UnityEngine;
+ /// using UnityEngine.UI;
+ /// using TMPro;
+ ///
+ /// public class Example : MonoBehaviour
+ /// {
+ /// //Create a List of new Dropdown options
+ /// List<string> m_DropOptions = new List<string> { "Option 1", "Option 2"};
+ /// //This is the Dropdown
+ /// TMP_Dropdown m_Dropdown;
+ ///
+ /// void Start()
+ /// {
+ /// //Fetch the Dropdown GameObject the script is attached to
+ /// m_Dropdown = GetComponent<TMP_Dropdown>();
+ /// //Clear the old options of the Dropdown menu
+ /// m_Dropdown.ClearOptions();
+ /// //Add the options created in the List above
+ /// m_Dropdown.AddOptions(m_DropOptions);
+ /// }
+ /// }
+ /// </code>
+ /// </example>
+ public void AddOptions(List<string> options)
+ {
+ for (int i = 0; i < options.Count; i++)
+ this.options.Add(new OptionData(options[i]));
+
+ RefreshShownValue();
+ }
+
+ /// <summary>
+ /// Add multiple image-only options to the options of the Dropdown based on a list of Sprites.
+ /// </summary>
+ /// <param name="options">The list of Sprites to add.</param>
+ /// <remarks>
+ /// See AddOptions(List<string> options) for code example of usages.
+ /// </remarks>
+ public void AddOptions(List<Sprite> options)
+ {
+ for (int i = 0; i < options.Count; i++)
+ this.options.Add(new OptionData(options[i]));
+
+ RefreshShownValue();
+ }
+
+ /// <summary>
+ /// Clear the list of options in the Dropdown.
+ /// </summary>
+ public void ClearOptions()
+ {
+ options.Clear();
+ m_Value = 0;
+ RefreshShownValue();
+ }
+
+ private void SetupTemplate()
+ {
+ validTemplate = false;
+
+ if (!m_Template)
+ {
+ Debug.LogError("The dropdown template is not assigned. The template needs to be assigned and must have a child GameObject with a Toggle component serving as the item.", this);
+ return;
+ }
+
+ GameObject templateGo = m_Template.gameObject;
+ templateGo.SetActive(true);
+ Toggle itemToggle = m_Template.GetComponentInChildren<Toggle>();
+
+ validTemplate = true;
+ if (!itemToggle || itemToggle.transform == template)
+ {
+ validTemplate = false;
+ Debug.LogError("The dropdown template is not valid. The template must have a child GameObject with a Toggle component serving as the item.", template);
+ }
+ else if (!(itemToggle.transform.parent is RectTransform))
+ {
+ validTemplate = false;
+ Debug.LogError("The dropdown template is not valid. The child GameObject with a Toggle component (the item) must have a RectTransform on its parent.", template);
+ }
+ else if (itemText != null && !itemText.transform.IsChildOf(itemToggle.transform))
+ {
+ validTemplate = false;
+ Debug.LogError("The dropdown template is not valid. The Item Text must be on the item GameObject or children of it.", template);
+ }
+ else if (itemImage != null && !itemImage.transform.IsChildOf(itemToggle.transform))
+ {
+ validTemplate = false;
+ Debug.LogError("The dropdown template is not valid. The Item Image must be on the item GameObject or children of it.", template);
+ }
+
+ if (!validTemplate)
+ {
+ templateGo.SetActive(false);
+ return;
+ }
+
+ DropdownItem item = itemToggle.gameObject.AddComponent<DropdownItem>();
+ item.text = m_ItemText;
+ item.image = m_ItemImage;
+ item.toggle = itemToggle;
+ item.rectTransform = (RectTransform)itemToggle.transform;
+
+ Canvas popupCanvas = GetOrAddComponent<Canvas>(templateGo);
+ popupCanvas.overrideSorting = true;
+ popupCanvas.sortingOrder = 30000;
+
+ GetOrAddComponent<GraphicRaycaster>(templateGo);
+ GetOrAddComponent<CanvasGroup>(templateGo);
+ templateGo.SetActive(false);
+
+ validTemplate = true;
+ }
+
+ private static T GetOrAddComponent<T>(GameObject go) where T : Component
+ {
+ T comp = go.GetComponent<T>();
+ if (!comp)
+ comp = go.AddComponent<T>();
+ return comp;
+ }
+
+ /// <summary>
+ /// Handling for when the dropdown is initially 'clicked'. Typically shows the dropdown
+ /// </summary>
+ /// <param name="eventData">The associated event data.</param>
+ public virtual void OnPointerClick(PointerEventData eventData)
+ {
+ Show();
+ }
+
+ /// <summary>
+ /// Handling for when the dropdown is selected and a submit event is processed. Typically shows the dropdown
+ /// </summary>
+ /// <param name="eventData">The associated event data.</param>
+ public virtual void OnSubmit(BaseEventData eventData)
+ {
+ Show();
+ }
+
+ /// <summary>
+ /// This will hide the dropdown list.
+ /// </summary>
+ /// <remarks>
+ /// Called by a BaseInputModule when a Cancel event occurs.
+ /// </remarks>
+ /// <param name="eventData">The associated event data.</param>
+ public virtual void OnCancel(BaseEventData eventData)
+ {
+ Hide();
+ }
+
+ /// <summary>
+ /// Show the dropdown.
+ ///
+ /// Plan for dropdown scrolling to ensure dropdown is contained within screen.
+ ///
+ /// We assume the Canvas is the screen that the dropdown must be kept inside.
+ /// This is always valid for screen space canvas modes.
+ /// For world space canvases we don't know how it's used, but it could be e.g. for an in-game monitor.
+ /// We consider it a fair constraint that the canvas must be big enough to contain dropdowns.
+ /// </summary>
+ public void Show()
+ {
+ if (!IsActive() || !IsInteractable() || m_Dropdown != null)
+ return;
+
+ // Get root Canvas.
+ var list = TMP_ListPool<Canvas>.Get();
+ gameObject.GetComponentsInParent(false, list);
+ if (list.Count == 0)
+ return;
+
+ Canvas rootCanvas = list[list.Count - 1];
+ for (int i = 0; i < list.Count; i++)
+ {
+ if (list[i].isRootCanvas)
+ {
+ rootCanvas = list[i];
+ break;
+ }
+ }
+
+ TMP_ListPool<Canvas>.Release(list);
+
+ if (!validTemplate)
+ {
+ SetupTemplate();
+ if (!validTemplate)
+ return;
+ }
+
+ m_Template.gameObject.SetActive(true);
+
+ // popupCanvas used to assume the root canvas had the default sorting Layer, next line fixes (case 958281 - [UI] Dropdown list does not copy the parent canvas layer when the panel is opened)
+ m_Template.GetComponent<Canvas>().sortingLayerID = rootCanvas.sortingLayerID;
+
+ // Instantiate the drop-down template
+ m_Dropdown = CreateDropdownList(m_Template.gameObject);
+ m_Dropdown.name = "Dropdown List";
+ m_Dropdown.SetActive(true);
+
+ // Make drop-down RectTransform have same values as original.
+ RectTransform dropdownRectTransform = m_Dropdown.transform as RectTransform;
+ dropdownRectTransform.SetParent(m_Template.transform.parent, false);
+
+ // Instantiate the drop-down list items
+
+ // Find the dropdown item and disable it.
+ DropdownItem itemTemplate = m_Dropdown.GetComponentInChildren<DropdownItem>();
+
+ GameObject content = itemTemplate.rectTransform.parent.gameObject;
+ RectTransform contentRectTransform = content.transform as RectTransform;
+ itemTemplate.rectTransform.gameObject.SetActive(true);
+
+ // Get the rects of the dropdown and item
+ Rect dropdownContentRect = contentRectTransform.rect;
+ Rect itemTemplateRect = itemTemplate.rectTransform.rect;
+
+ // Calculate the visual offset between the item's edges and the background's edges
+ Vector2 offsetMin = itemTemplateRect.min - dropdownContentRect.min + (Vector2)itemTemplate.rectTransform.localPosition;
+ Vector2 offsetMax = itemTemplateRect.max - dropdownContentRect.max + (Vector2)itemTemplate.rectTransform.localPosition;
+ Vector2 itemSize = itemTemplateRect.size;
+
+ m_Items.Clear();
+
+ Toggle prev = null;
+ for (int i = 0; i < options.Count; ++i)
+ {
+ OptionData data = options[i];
+ DropdownItem item = AddItem(data, value == i, itemTemplate, m_Items);
+ if (item == null)
+ continue;
+
+ // Automatically set up a toggle state change listener
+ item.toggle.isOn = value == i;
+ item.toggle.onValueChanged.AddListener(x => OnSelectItem(item.toggle));
+
+ // Select current option
+ if (item.toggle.isOn)
+ item.toggle.Select();
+
+ // Automatically set up explicit navigation
+ if (prev != null)
+ {
+ Navigation prevNav = prev.navigation;
+ Navigation toggleNav = item.toggle.navigation;
+ prevNav.mode = Navigation.Mode.Explicit;
+ toggleNav.mode = Navigation.Mode.Explicit;
+
+ prevNav.selectOnDown = item.toggle;
+ prevNav.selectOnRight = item.toggle;
+ toggleNav.selectOnLeft = prev;
+ toggleNav.selectOnUp = prev;
+
+ prev.navigation = prevNav;
+ item.toggle.navigation = toggleNav;
+ }
+ prev = item.toggle;
+ }
+
+ // Reposition all items now that all of them have been added
+ Vector2 sizeDelta = contentRectTransform.sizeDelta;
+ sizeDelta.y = itemSize.y * m_Items.Count + offsetMin.y - offsetMax.y;
+ contentRectTransform.sizeDelta = sizeDelta;
+
+ float extraSpace = dropdownRectTransform.rect.height - contentRectTransform.rect.height;
+ if (extraSpace > 0)
+ dropdownRectTransform.sizeDelta = new Vector2(dropdownRectTransform.sizeDelta.x, dropdownRectTransform.sizeDelta.y - extraSpace);
+
+ // Invert anchoring and position if dropdown is partially or fully outside of canvas rect.
+ // Typically this will have the effect of placing the dropdown above the button instead of below,
+ // but it works as inversion regardless of initial setup.
+ Vector3[] corners = new Vector3[4];
+ dropdownRectTransform.GetWorldCorners(corners);
+
+ RectTransform rootCanvasRectTransform = rootCanvas.transform as RectTransform;
+ Rect rootCanvasRect = rootCanvasRectTransform.rect;
+ for (int axis = 0; axis < 2; axis++)
+ {
+ bool outside = false;
+ for (int i = 0; i < 4; i++)
+ {
+ Vector3 corner = rootCanvasRectTransform.InverseTransformPoint(corners[i]);
+ if ((corner[axis] < rootCanvasRect.min[axis] && !Mathf.Approximately(corner[axis], rootCanvasRect.min[axis])) ||
+ (corner[axis] > rootCanvasRect.max[axis] && !Mathf.Approximately(corner[axis], rootCanvasRect.max[axis])))
+ {
+ outside = true;
+ break;
+ }
+ }
+ if (outside)
+ RectTransformUtility.FlipLayoutOnAxis(dropdownRectTransform, axis, false, false);
+ }
+
+ for (int i = 0; i < m_Items.Count; i++)
+ {
+ RectTransform itemRect = m_Items[i].rectTransform;
+ itemRect.anchorMin = new Vector2(itemRect.anchorMin.x, 0);
+ itemRect.anchorMax = new Vector2(itemRect.anchorMax.x, 0);
+ itemRect.anchoredPosition = new Vector2(itemRect.anchoredPosition.x, offsetMin.y + itemSize.y * (m_Items.Count - 1 - i) + itemSize.y * itemRect.pivot.y);
+ itemRect.sizeDelta = new Vector2(itemRect.sizeDelta.x, itemSize.y);
+ }
+
+ // Fade in the popup
+ AlphaFadeList(0.15f, 0f, 1f);
+
+ // Make drop-down template and item template inactive
+ m_Template.gameObject.SetActive(false);
+ itemTemplate.gameObject.SetActive(false);
+
+ m_Blocker = CreateBlocker(rootCanvas);
+ }
+
+ /// <summary>
+ /// Create a blocker that blocks clicks to other controls while the dropdown list is open.
+ /// </summary>
+ /// <remarks>
+ /// Override this method to implement a different way to obtain a blocker GameObject.
+ /// </remarks>
+ /// <param name="rootCanvas">The root canvas the dropdown is under.</param>
+ /// <returns>The created blocker object</returns>
+ protected virtual GameObject CreateBlocker(Canvas rootCanvas)
+ {
+ // Create blocker GameObject.
+ GameObject blocker = new GameObject("Blocker");
+
+ // Setup blocker RectTransform to cover entire root canvas area.
+ RectTransform blockerRect = blocker.AddComponent<RectTransform>();
+ blockerRect.SetParent(rootCanvas.transform, false);
+ blockerRect.anchorMin = Vector3.zero;
+ blockerRect.anchorMax = Vector3.one;
+ blockerRect.sizeDelta = Vector2.zero;
+
+ // Make blocker be in separate canvas in same layer as dropdown and in layer just below it.
+ Canvas blockerCanvas = blocker.AddComponent<Canvas>();
+ blockerCanvas.overrideSorting = true;
+ Canvas dropdownCanvas = m_Dropdown.GetComponent<Canvas>();
+ blockerCanvas.sortingLayerID = dropdownCanvas.sortingLayerID;
+ blockerCanvas.sortingOrder = dropdownCanvas.sortingOrder - 1;
+
+ // Add raycaster since it's needed to block.
+ blocker.AddComponent<GraphicRaycaster>();
+
+ // Add image since it's needed to block, but make it clear.
+ Image blockerImage = blocker.AddComponent<Image>();
+ blockerImage.color = Color.clear;
+
+ // Add button since it's needed to block, and to close the dropdown when blocking area is clicked.
+ Button blockerButton = blocker.AddComponent<Button>();
+ blockerButton.onClick.AddListener(Hide);
+
+ return blocker;
+ }
+
+ /// <summary>
+ /// Convenience method to explicitly destroy the previously generated blocker object
+ /// </summary>
+ /// <remarks>
+ /// Override this method to implement a different way to dispose of a blocker GameObject that blocks clicks to other controls while the dropdown list is open.
+ /// </remarks>
+ /// <param name="blocker">The blocker object to destroy.</param>
+ protected virtual void DestroyBlocker(GameObject blocker)
+ {
+ Destroy(blocker);
+ }
+
+ /// <summary>
+ /// Create the dropdown list to be shown when the dropdown is clicked. The dropdown list should correspond to the provided template GameObject, equivalent to instantiating a copy of it.
+ /// </summary>
+ /// <remarks>
+ /// Override this method to implement a different way to obtain a dropdown list GameObject.
+ /// </remarks>
+ /// <param name="template">The template to create the dropdown list from.</param>
+ /// <returns>The created drop down list gameobject.</returns>
+ protected virtual GameObject CreateDropdownList(GameObject template)
+ {
+ return (GameObject)Instantiate(template);
+ }
+
+ /// <summary>
+ /// Convenience method to explicitly destroy the previously generated dropdown list
+ /// </summary>
+ /// <remarks>
+ /// Override this method to implement a different way to dispose of a dropdown list GameObject.
+ /// </remarks>
+ /// <param name="dropdownList">The dropdown list GameObject to destroy</param>
+ protected virtual void DestroyDropdownList(GameObject dropdownList)
+ {
+ Destroy(dropdownList);
+ }
+
+ /// <summary>
+ /// Create a dropdown item based upon the item template.
+ /// </summary>
+ /// <remarks>
+ /// Override this method to implement a different way to obtain an option item.
+ /// The option item should correspond to the provided template DropdownItem and its GameObject, equivalent to instantiating a copy of it.
+ /// </remarks>
+ /// <param name="itemTemplate">e template to create the option item from.</param>
+ /// <returns>The created dropdown item component</returns>
+ protected virtual DropdownItem CreateItem(DropdownItem itemTemplate)
+ {
+ return (DropdownItem)Instantiate(itemTemplate);
+ }
+
+ /// <summary>
+ /// Convenience method to explicitly destroy the previously generated Items.
+ /// </summary>
+ /// <remarks>
+ /// Override this method to implement a different way to dispose of an option item.
+ /// Likely no action needed since destroying the dropdown list destroys all contained items as well.
+ /// </remarks>
+ /// <param name="item">The Item to destroy.</param>
+ protected virtual void DestroyItem(DropdownItem item) { }
+
+ // Add a new drop-down list item with the specified values.
+ private DropdownItem AddItem(OptionData data, bool selected, DropdownItem itemTemplate, List<DropdownItem> items)
+ {
+ // Add a new item to the dropdown.
+ DropdownItem item = CreateItem(itemTemplate);
+ item.rectTransform.SetParent(itemTemplate.rectTransform.parent, false);
+
+ item.gameObject.SetActive(true);
+ item.gameObject.name = "Item " + items.Count + (data.text != null ? ": " + data.text : "");
+
+ if (item.toggle != null)
+ {
+ item.toggle.isOn = false;
+ }
+
+ // Set the item's data
+ if (item.text)
+ item.text.text = data.text;
+ if (item.image)
+ {
+ item.image.sprite = data.image;
+ item.image.enabled = (item.image.sprite != null);
+ }
+
+ items.Add(item);
+ return item;
+ }
+
+ private void AlphaFadeList(float duration, float alpha)
+ {
+ CanvasGroup group = m_Dropdown.GetComponent<CanvasGroup>();
+ AlphaFadeList(duration, group.alpha, alpha);
+ }
+
+ private void AlphaFadeList(float duration, float start, float end)
+ {
+ if (end.Equals(start))
+ return;
+
+ FloatTween tween = new FloatTween { duration = duration, startValue = start, targetValue = end };
+ tween.AddOnChangedCallback(SetAlpha);
+ tween.ignoreTimeScale = true;
+ m_AlphaTweenRunner.StartTween(tween);
+ }
+
+ private void SetAlpha(float alpha)
+ {
+ if (!m_Dropdown)
+ return;
+ CanvasGroup group = m_Dropdown.GetComponent<CanvasGroup>();
+ group.alpha = alpha;
+ }
+
+ /// <summary>
+ /// Hide the dropdown list. I.e. close it.
+ /// </summary>
+ public void Hide()
+ {
+ if (m_Dropdown != null)
+ {
+ AlphaFadeList(0.15f, 0f);
+
+ // User could have disabled the dropdown during the OnValueChanged call.
+ if (IsActive())
+ StartCoroutine(DelayedDestroyDropdownList(0.15f));
+ }
+ if (m_Blocker != null)
+ DestroyBlocker(m_Blocker);
+ m_Blocker = null;
+ Select();
+ }
+
+ private IEnumerator DelayedDestroyDropdownList(float delay)
+ {
+ yield return new WaitForSecondsRealtime(delay);
+ ImmediateDestroyDropdownList();
+ }
+
+ private void ImmediateDestroyDropdownList()
+ {
+ for (int i = 0; i < m_Items.Count; i++)
+ {
+ if (m_Items[i] != null)
+ DestroyItem(m_Items[i]);
+ }
+ m_Items.Clear();
+
+ if (m_Dropdown != null)
+ DestroyDropdownList(m_Dropdown);
+ m_Dropdown = null;
+ }
+
+ // Change the value and hide the dropdown.
+ private void OnSelectItem(Toggle toggle)
+ {
+ if (!toggle.isOn)
+ toggle.isOn = true;
+
+ int selectedIndex = -1;
+ Transform tr = toggle.transform;
+ Transform parent = tr.parent;
+ for (int i = 0; i < parent.childCount; i++)
+ {
+ if (parent.GetChild(i) == tr)
+ {
+ // Subtract one to account for template child.
+ selectedIndex = i - 1;
+ break;
+ }
+ }
+
+ if (selectedIndex < 0)
+ return;
+
+ value = selectedIndex;
+ Hide();
+ }
+ }
+} \ No newline at end of file
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_Dropdown.cs.meta b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_Dropdown.cs.meta
new file mode 100644
index 0000000..7e89574
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_Dropdown.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 7b743370ac3e4ec2a1668f5455a8ef8a
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {fileID: 2800000, guid: a7ec9e7ad8b847b7ae4510af83c5d868, type: 3}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_EditorResourceManager.cs b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_EditorResourceManager.cs
new file mode 100644
index 0000000..6fd4aa6
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_EditorResourceManager.cs
@@ -0,0 +1,142 @@
+#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 Dictionary<int, int> m_ObjectUpdateQueueLookup = new Dictionary<int, int>();
+
+ private readonly List<Object> m_ObjectReImportQueue = new List<Object>();
+ private Dictionary<int, int> m_ObjectReImportQueueLookup = new Dictionary<int, 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;
+ }
+
+
+ void OnCameraPostRender(Camera cam)
+ {
+ // Exclude the PreRenderCamera
+ if (cam.cameraType == CameraType.Preview)
+ return;
+
+ DoUpdates();
+ }
+
+ /// <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.ContainsKey(id))
+ return;
+
+ m_ObjectReImportQueueLookup[id] = id;
+ m_ObjectReImportQueue.Add(obj);
+
+ return;
+ }
+
+ /// <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.ContainsKey(id))
+ return;
+
+ m_ObjectUpdateQueueLookup[id] = id;
+ m_ObjectUpdateQueue.Add(obj);
+
+ return;
+ }
+
+
+ void DoUpdates()
+ {
+ // 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)
+ {
+ //Debug.Log("Re-importing [" + obj.name + "]");
+ AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(obj));
+ }
+ }
+
+ if (objReImportCount > 0)
+ {
+ m_ObjectReImportQueue.Clear();
+ m_ObjectReImportQueueLookup.Clear();
+ }
+ }
+
+ }
+}
+#endif
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_EditorResourceManager.cs.meta b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_EditorResourceManager.cs.meta
new file mode 100644
index 0000000..0e27664
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_EditorResourceManager.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 6b259c4003a802847b9ada90744e34c5
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_FontAsset.cs b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_FontAsset.cs
new file mode 100644
index 0000000..faa5796
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_FontAsset.cs
@@ -0,0 +1,1948 @@
+using System;
+using UnityEngine;
+using UnityEngine.Serialization;
+using UnityEngine.TextCore;
+using UnityEngine.TextCore.LowLevel;
+using UnityEngine.Profiling;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+
+
+namespace TMPro
+{
+ public enum AtlasPopulationMode
+ {
+ Static = 0x0,
+ Dynamic = 0x1,
+ }
+
+
+ [Serializable]
+ public class TMP_FontAsset : TMP_Asset
+ {
+ /// <summary>
+ /// The version of the font asset class.
+ /// Version 1.1.0 adds support for the new TextCore.FontEngine and Dynamic SDF system.
+ /// </summary>
+ public string version
+ {
+ get { return m_Version; }
+ internal set { m_Version = value; }
+ }
+ [SerializeField]
+ private string m_Version;
+
+ /// <summary>
+ /// This field is set when the font asset is first created.
+ /// </summary>
+ [SerializeField]
+ internal string m_SourceFontFileGUID;
+
+ #if UNITY_EDITOR
+ /// <summary>
+ /// Persistent reference to the source font file maintained in the editor.
+ /// </summary>
+ [SerializeField]
+ internal Font m_SourceFontFile_EditorRef;
+ #endif
+
+ /// <summary>
+ /// Source font file when atlas population mode is set to dynamic. Null when the atlas population mode is set to static.
+ /// </summary>
+ public Font sourceFontFile
+ {
+ get { return m_SourceFontFile; }
+ internal set { m_SourceFontFile = value; }
+ }
+ [SerializeField]
+ private Font m_SourceFontFile;
+
+ public AtlasPopulationMode atlasPopulationMode
+ {
+ get { return m_AtlasPopulationMode; }
+
+ set
+ {
+ m_AtlasPopulationMode = value;
+
+ #if UNITY_EDITOR
+ if (m_AtlasPopulationMode == AtlasPopulationMode.Static)
+ m_SourceFontFile = null;
+ else if (m_AtlasPopulationMode == AtlasPopulationMode.Dynamic)
+ m_SourceFontFile = m_SourceFontFile_EditorRef;
+ #endif
+ }
+ }
+ [SerializeField]
+ private AtlasPopulationMode m_AtlasPopulationMode;
+
+
+ /// <summary>
+ /// Information about the font face.
+ /// </summary>
+ public FaceInfo faceInfo
+ {
+ get { return m_FaceInfo; }
+ internal set { m_FaceInfo = value; }
+ }
+ [SerializeField]
+ private FaceInfo m_FaceInfo;
+
+
+ /// <summary>
+ /// List of glyphs contained in the font asset.
+ /// </summary>
+ public List<Glyph> glyphTable
+ {
+ get { return m_GlyphTable; }
+ internal set { m_GlyphTable = value; }
+ }
+ [SerializeField]
+ private List<Glyph> m_GlyphTable = new List<Glyph>();
+
+ /// <summary>
+ /// Dictionary used to lookup glyphs contained in the font asset by their index.
+ /// </summary>
+ public Dictionary<uint, Glyph> glyphLookupTable
+ {
+ get
+ {
+ if (m_GlyphLookupDictionary == null)
+ ReadFontAssetDefinition();
+
+ return m_GlyphLookupDictionary;
+ }
+ }
+ private Dictionary<uint, Glyph> m_GlyphLookupDictionary;
+
+
+ /// <summary>
+ /// List containing the characters of the given font asset.
+ /// </summary>
+ public List<TMP_Character> characterTable
+ {
+ get { return m_CharacterTable; }
+ internal set { m_CharacterTable = value; }
+ }
+ [SerializeField]
+ private List<TMP_Character> m_CharacterTable = new List<TMP_Character>();
+
+ /// <summary>
+ /// Dictionary used to lookup characters contained in the font asset by their unicode values.
+ /// </summary>
+ public Dictionary<uint, TMP_Character> characterLookupTable
+ {
+ get
+ {
+ if (m_CharacterLookupDictionary == null)
+ ReadFontAssetDefinition();
+
+
+ return m_CharacterLookupDictionary;
+ }
+ }
+ private Dictionary<uint, TMP_Character> m_CharacterLookupDictionary;
+
+
+ /// <summary>
+ /// The font atlas used by this font asset.
+ /// This is always the texture at index [0] of the fontAtlasTextures.
+ /// </summary>
+ public Texture2D atlasTexture
+ {
+ get
+ {
+ if (m_AtlasTexture == null)
+ {
+ m_AtlasTexture = atlasTextures[0];
+ }
+
+ return m_AtlasTexture;
+ }
+ }
+ private Texture2D m_AtlasTexture;
+
+ /// <summary>
+ /// Array of atlas textures that contain the glyphs used by this font asset.
+ /// </summary>
+ public Texture2D[] atlasTextures
+ {
+ get
+ {
+ if (m_AtlasTextures == null)
+ {
+ //
+ }
+
+ return m_AtlasTextures;
+ }
+
+ set
+ {
+ m_AtlasTextures = value;
+ }
+ }
+ [SerializeField]
+ private Texture2D[] m_AtlasTextures;
+
+ /// <summary>
+ /// Index of the font atlas texture that still has available space to add new glyphs.
+ /// </summary>
+ [SerializeField]
+ internal int m_AtlasTextureIndex;
+
+ /// <summary>
+ /// List of spaces occupied by glyphs in a given texture.
+ /// </summary>
+ internal List<GlyphRect> usedGlyphRects
+ {
+ get { return m_UsedGlyphRects; }
+ set { m_UsedGlyphRects = value; }
+ }
+ [SerializeField]
+ private List<GlyphRect> m_UsedGlyphRects;
+
+ /// <summary>
+ /// List of spaces available in a given texture to add new glyphs.
+ /// </summary>
+ internal List<GlyphRect> freeGlyphRects
+ {
+ get { return m_FreeGlyphRects; }
+ set { m_FreeGlyphRects = value; }
+ }
+ [SerializeField]
+ private List<GlyphRect> m_FreeGlyphRects;
+
+ /// <summary>
+ /// The general information about the font.
+ /// This property and FaceInfo_Legacy type are not longer used in version 1.1.0 of the font asset.
+ /// </summary>
+ [Obsolete("The fontInfo property and underlying type is now obsolete. Please use the faceInfo property and FaceInfo type instead.")]
+ public FaceInfo_Legacy fontInfo
+ {
+ get { return m_fontInfo; }
+ }
+
+ [SerializeField]
+ private FaceInfo_Legacy m_fontInfo = null;
+
+ /// <summary>
+ ///
+ /// </summary>
+ [SerializeField]
+ public Texture2D atlas; // Should add a property to make this read-only.
+
+ /// <summary>
+ /// The width of the atlas texture(s) used by this font asset.
+ /// </summary>
+ public int atlasWidth
+ {
+ get { return m_AtlasWidth; }
+ internal set { m_AtlasWidth = value; }
+ }
+ [SerializeField]
+ private int m_AtlasWidth;
+
+ /// <summary>
+ /// The height of the atlas texture(s) used by this font asset.
+ /// </summary>
+ public int atlasHeight
+ {
+ get { return m_AtlasHeight; }
+ internal set { m_AtlasHeight = value; }
+ }
+ [SerializeField]
+ private int m_AtlasHeight;
+
+ /// <summary>
+ /// The padding used between glyphs contained in the atlas texture(s) used by this font asset.
+ /// </summary>
+ public int atlasPadding
+ {
+ get { return m_AtlasPadding; }
+ internal set { m_AtlasPadding = value; }
+ }
+ [SerializeField]
+ private int m_AtlasPadding;
+
+ public GlyphRenderMode atlasRenderMode
+ {
+ get { return m_AtlasRenderMode; }
+ internal set { m_AtlasRenderMode = value; }
+ }
+ [SerializeField]
+ private GlyphRenderMode m_AtlasRenderMode;
+
+ // Legacy field that will eventually be removed.
+ [SerializeField]
+ internal List<TMP_Glyph> m_glyphInfoList;
+
+ [SerializeField]
+ [FormerlySerializedAs("m_kerningInfo")]
+ internal KerningTable m_KerningTable = new KerningTable();
+
+ /// <summary>
+ /// Table containing the various font features of this font asset.
+ /// </summary>
+ public TMP_FontFeatureTable fontFeatureTable
+ {
+ get { return m_FontFeatureTable; }
+ internal set { m_FontFeatureTable = value; }
+ }
+ [SerializeField]
+ private TMP_FontFeatureTable m_FontFeatureTable = new TMP_FontFeatureTable();
+
+ // Legacy field that will eventually be removed
+ [SerializeField]
+ #pragma warning disable 0649
+ private List<TMP_FontAsset> fallbackFontAssets;
+
+ /// <summary>
+ /// List containing the Fallback font assets for this font.
+ /// </summary>
+ public List<TMP_FontAsset> fallbackFontAssetTable
+ {
+ get { return m_FallbackFontAssetTable; }
+ set { m_FallbackFontAssetTable = value; }
+ }
+ [SerializeField]
+ public List<TMP_FontAsset> m_FallbackFontAssetTable;
+
+ /// <summary>
+ /// The settings used in the Font Asset Creator when this font asset was created or edited.
+ /// </summary>
+ public FontAssetCreationSettings creationSettings
+ {
+ get { return m_CreationSettings; }
+ set { m_CreationSettings = value; }
+ }
+ [SerializeField]
+ internal FontAssetCreationSettings m_CreationSettings;
+
+ /// <summary>
+ /// Array containing font assets to be used as alternative typefaces for the various potential font weights of this font asset.
+ /// </summary>
+ public TMP_FontWeightPair[] fontWeightTable
+ {
+ get { return m_FontWeightTable; }
+ internal set { m_FontWeightTable = value; }
+ }
+ [SerializeField]
+ private TMP_FontWeightPair[] m_FontWeightTable = new TMP_FontWeightPair[10];
+
+ // FONT WEIGHTS
+ /// <summary>
+ /// Font weights used by font asset prior to version 1.1.0.
+ /// This is legacy and will be removed at some point in the future.
+ /// </summary>
+ [SerializeField]
+ private TMP_FontWeightPair[] fontWeights = null;
+
+ //private int[] m_characterSet; // Array containing all the characters in this font asset.
+
+ /// <summary>
+ /// Defines the dilation of the text when using regular style.
+ /// </summary>
+ public float normalStyle = 0;
+
+ /// <summary>
+ /// The spacing between characters when using regular style.
+ /// </summary>
+ public float normalSpacingOffset = 0;
+
+ /// <summary>
+ /// Defines the dilation of the text when using bold style.
+ /// </summary>
+ public float boldStyle = 0.75f;
+
+ /// <summary>
+ /// The spacing between characters when using regular style.
+ /// </summary>
+ public float boldSpacing = 7f;
+
+ /// <summary>
+ /// Defines the slant of the text when using italic style.
+ /// </summary>
+ public byte italicStyle = 35;
+
+ public byte tabSize = 10;
+
+ private byte m_oldTabSize;
+ internal bool m_IsFontAssetLookupTablesDirty = false;
+
+ /// <summary>
+ /// Create new instance of a font asset using default settings.
+ /// </summary>
+ /// <param name="font"></param>
+ /// <returns></returns>
+ public static TMP_FontAsset CreateFontAsset(Font font)
+ {
+ return CreateFontAsset(font, 90, 9, GlyphRenderMode.SDFAA, 1024, 1024, AtlasPopulationMode.Dynamic);
+ }
+
+ /// <summary>
+ /// Create new instance of a font asset.
+ /// </summary>
+ /// <param name="font">The source font file.</param>
+ /// <param name="samplingPointSize">The sampling point size.</param>
+ /// <param name="atlasPadding">The padding / spread between individual glyphs in the font asset.</param>
+ /// <param name="renderMode"></param>
+ /// <param name="atlasWidth">The atlas texture width.</param>
+ /// <param name="atlasHeight">The atlas texture height.</param>
+ /// <param name="atlasPopulationMode"></param>
+ /// <returns></returns>
+ public static TMP_FontAsset CreateFontAsset(Font font, int samplingPointSize, int atlasPadding, GlyphRenderMode renderMode, int atlasWidth, int atlasHeight, AtlasPopulationMode atlasPopulationMode = AtlasPopulationMode.Dynamic)
+ {
+ TMP_FontAsset fontAsset = ScriptableObject.CreateInstance<TMP_FontAsset>();
+
+ fontAsset.m_Version = "1.1.0";
+
+ // Set face information
+ FontEngine.InitializeFontEngine();
+ FontEngine.LoadFontFace(font, samplingPointSize);
+
+ fontAsset.faceInfo = FontEngine.GetFaceInfo();
+
+ // Set font reference and GUID
+ if (atlasPopulationMode == AtlasPopulationMode.Dynamic)
+ fontAsset.sourceFontFile = font;
+
+ // Set persistent reference to source font file in the Editor only.
+ #if UNITY_EDITOR
+ UnityEditor.AssetDatabase.TryGetGUIDAndLocalFileIdentifier(font, out string guid, out long localID);
+ fontAsset.m_SourceFontFileGUID = guid;
+ fontAsset.m_SourceFontFile_EditorRef = font;
+ #endif
+
+ fontAsset.atlasPopulationMode = atlasPopulationMode;
+
+ fontAsset.atlasWidth = atlasWidth;
+ fontAsset.atlasHeight = atlasHeight;
+ fontAsset.atlasPadding = atlasPadding;
+ fontAsset.atlasRenderMode = renderMode;
+
+ // Initialize array for the font atlas textures.
+ fontAsset.atlasTextures = new Texture2D[1];
+
+ // Create and add font atlas texture.
+ Texture2D texture = new Texture2D(0, 0, TextureFormat.Alpha8, false);
+
+ //texture.name = assetName + " Atlas";
+ fontAsset.atlasTextures[0] = texture;
+
+ // Add free rectangle of the size of the texture.
+ int packingModifier;
+ if (((GlyphRasterModes)renderMode & GlyphRasterModes.RASTER_MODE_BITMAP) == GlyphRasterModes.RASTER_MODE_BITMAP)
+ {
+ packingModifier = 0;
+
+ // Optimize by adding static ref to shader.
+ Material tmp_material = new Material(ShaderUtilities.ShaderRef_MobileBitmap);
+
+ //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);
+
+ fontAsset.material = tmp_material;
+ }
+ else
+ {
+ packingModifier = 1;
+
+ // Optimize by adding static ref to shader.
+ Material tmp_material = new Material(ShaderUtilities.ShaderRef_MobileSDF);
+
+ //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;
+ }
+
+ fontAsset.freeGlyphRects = new List<GlyphRect>() { new GlyphRect(0, 0, atlasWidth - packingModifier, atlasHeight - packingModifier) };
+ fontAsset.usedGlyphRects = new List<GlyphRect>();
+
+ // TODO: Consider adding support for extracting glyph positioning data
+
+ fontAsset.ReadFontAssetDefinition();
+
+ return fontAsset;
+ }
+
+
+ void Awake()
+ {
+ //Debug.Log("TMP Font Asset [" + this.name + "] with Version #" + m_Version + " has been enabled!");
+
+ // Check version number of font asset to see if it needs to be upgraded.
+ if (this.material != null && string.IsNullOrEmpty(m_Version))
+ UpgradeFontAsset();
+ }
+
+
+ #if UNITY_EDITOR
+ void OnValidate()
+ {
+ //if (m_oldTabSize != tabSize)
+ //{
+ // m_oldTabSize = tabSize;
+ // ReadFontAssetDefinition();
+ //}
+
+ // Handle changes to atlas population mode
+ //if (m_AtlasPopulationMode == AtlasPopulationMode.Static)
+ // m_SourceFontFile = null;
+ //else
+ //{
+ // string path = UnityEditor.AssetDatabase.GUIDToAssetPath(m_SourceFontFileGUID);
+
+ // if (path != string.Empty)
+ // m_SourceFontFile = UnityEditor.AssetDatabase.LoadAssetAtPath<Font>(path);
+ //}
+ }
+ #endif
+
+ /// <summary>
+ /// Read the various data tables of the font asset to populate its different dictionaries to allow for faster lookup of related font asset data.
+ /// </summary>
+ internal void InitializeDictionaryLookupTables()
+ {
+ // Create new instance of the glyph lookup dictionary or clear the existing one.
+ if (m_GlyphLookupDictionary == null)
+ m_GlyphLookupDictionary = new Dictionary<uint, Glyph>();
+ else
+ m_GlyphLookupDictionary.Clear();
+
+ int glyphCount = m_GlyphTable.Count;
+
+ // Initialize glyph index array or clear the existing one.
+ if (m_GlyphIndexList == null)
+ m_GlyphIndexList = new List<uint>();
+ else
+ m_GlyphIndexList.Clear();
+
+ // Add the characters contained in the character table into the dictionary for faster lookup.
+ for (int i = 0; i < glyphCount; i++)
+ {
+ Glyph glyph = m_GlyphTable[i];
+
+ uint index = glyph.index;
+
+ // TODO: Not sure it is necessary to check here.
+ if (m_GlyphLookupDictionary.ContainsKey(index) == false)
+ {
+ m_GlyphLookupDictionary.Add(index, glyph);
+ m_GlyphIndexList.Add(index);
+ }
+ }
+
+ // Create new instance of the character lookup dictionary or clear the existing one.
+ if (m_CharacterLookupDictionary == null)
+ m_CharacterLookupDictionary = new Dictionary<uint, TMP_Character>();
+ else
+ m_CharacterLookupDictionary.Clear();
+
+ // Add the characters contained in the character table into the dictionary for faster lookup.
+ for (int i = 0; i < m_CharacterTable.Count; i++)
+ {
+ TMP_Character character = m_CharacterTable[i];
+
+ uint unicode = character.unicode;
+ uint glyphIndex = character.glyphIndex;
+
+ if (m_CharacterLookupDictionary.ContainsKey(unicode) == false)
+ m_CharacterLookupDictionary.Add(unicode, character);
+
+ if (m_GlyphLookupDictionary.ContainsKey(glyphIndex))
+ {
+ character.glyph = m_GlyphLookupDictionary[glyphIndex];
+ }
+ }
+
+ // Upgrade Glyph Adjustment Table to the new Font Feature table and Glyph Pair Adjustment Records
+ if (m_KerningTable != null && m_KerningTable.kerningPairs != null && m_KerningTable.kerningPairs.Count > 0)
+ UpgradeGlyphAdjustmentTableToFontFeatureTable();
+
+ // Read Font Features which will include kerning data.
+ if (m_FontFeatureTable.m_GlyphPairAdjustmentRecordLookupDictionary == null)
+ m_FontFeatureTable.m_GlyphPairAdjustmentRecordLookupDictionary = new Dictionary<long, TMP_GlyphPairAdjustmentRecord>();
+ else
+ m_FontFeatureTable.m_GlyphPairAdjustmentRecordLookupDictionary.Clear();
+
+ List<TMP_GlyphPairAdjustmentRecord> glyphPairAdjustmentRecords = m_FontFeatureTable.m_GlyphPairAdjustmentRecords;
+ if (glyphPairAdjustmentRecords != null)
+ {
+ for (int i = 0; i < glyphPairAdjustmentRecords.Count; i++)
+ {
+ TMP_GlyphPairAdjustmentRecord record = glyphPairAdjustmentRecords[i];
+
+ long key = new GlyphPairKey(record).key;
+
+ m_FontFeatureTable.m_GlyphPairAdjustmentRecordLookupDictionary.Add(key, record);
+ }
+ }
+ }
+
+
+ /// <summary>
+ ///
+ /// </summary>
+ public void ReadFontAssetDefinition()
+ {
+ //Debug.Log("Reading Font Definition for " + this.name + ".");
+
+ // Check version number of font asset to see if it needs to be upgraded.
+ if (this.material != null && string.IsNullOrEmpty(m_Version))
+ UpgradeFontAsset();
+
+ // Initialize lookup tables for characters and glyphs.
+ InitializeDictionaryLookupTables();
+
+ // Add Tab char(9) to Dictionary.
+ if (m_CharacterLookupDictionary.ContainsKey(9) == false)
+ {
+ Glyph glyph = new Glyph(0, new GlyphMetrics(0, 0, 0, 0, m_FaceInfo.tabWidth * tabSize), GlyphRect.zero, 1.0f, 0);
+ m_CharacterLookupDictionary.Add(9, new TMP_Character(9, glyph));
+ }
+
+ // Add Linefeed LF char(10) and Carriage Return CR char(13)
+ if (m_CharacterLookupDictionary.ContainsKey(10) == false)
+ {
+ Glyph glyph = new Glyph(0, new GlyphMetrics(10, 0, 0, 0, 0), GlyphRect.zero, 1.0f, 0);
+ m_CharacterLookupDictionary.Add(10, new TMP_Character(10, glyph));
+
+ if (!m_CharacterLookupDictionary.ContainsKey(13))
+ m_CharacterLookupDictionary.Add(13, new TMP_Character(13, glyph));
+ }
+
+ // Add Zero Width Space 8203 (0x200B)
+ if (m_CharacterLookupDictionary.ContainsKey(8203) == false)
+ {
+ Glyph glyph = new Glyph(0, new GlyphMetrics(0, 0, 0, 0, 0), GlyphRect.zero, 1.0f, 0);
+ m_CharacterLookupDictionary.Add(8203, new TMP_Character(8203, glyph));
+ }
+
+ // Add Zero Width Non-Breaking Space 8288 (0x2060)
+ if (m_CharacterLookupDictionary.ContainsKey(8288) == false)
+ {
+ Glyph glyph = new Glyph(0, new GlyphMetrics(0, 0, 0, 0, 0), GlyphRect.zero, 1.0f, 0);
+ m_CharacterLookupDictionary.Add(8288, new TMP_Character(8288, glyph));
+ }
+
+ // Add Non-Breaking Hyphen 8209 (0x2011)
+ if (m_CharacterLookupDictionary.ContainsKey(8209) == false)
+ {
+ if (m_CharacterLookupDictionary.TryGetValue(45, out TMP_Character character))
+ m_CharacterLookupDictionary.Add(8209, new TMP_Character(8209, character.glyph));
+ }
+
+
+ // Set Cap Height
+ if (m_FaceInfo.capLine == 0 && m_CharacterLookupDictionary.ContainsKey(72))
+ {
+ uint glyphIndex = m_CharacterLookupDictionary[72].glyphIndex;
+ m_FaceInfo.capLine = m_GlyphLookupDictionary[glyphIndex].metrics.horizontalBearingY;
+ }
+
+ // Adjust Font Scale for compatibility reasons
+ if (m_FaceInfo.scale == 0)
+ m_FaceInfo.scale = 1.0f;
+
+ // Set Strikethrough Offset (if needed)
+ if (m_FaceInfo.strikethroughOffset == 0)
+ m_FaceInfo.strikethroughOffset = m_FaceInfo.capLine / 2.5f;
+
+ // Set Padding value for legacy font assets.
+ if (m_AtlasPadding == 0)
+ {
+ if (material.HasProperty(ShaderUtilities.ID_GradientScale))
+ m_AtlasPadding = (int)material.GetFloat(ShaderUtilities.ID_GradientScale) - 1;
+ }
+
+ // Compute Hashcode for the font asset name
+ hashCode = TMP_TextUtilities.GetSimpleHashCode(this.name);
+
+ // Compute Hashcode for the material name
+ materialHashCode = TMP_TextUtilities.GetSimpleHashCode(material.name);
+
+ m_IsFontAssetLookupTablesDirty = false;
+ }
+
+
+ /// <summary>
+ /// Sort the Character table by Unicode values.
+ /// </summary>
+ internal void SortCharacterTable()
+ {
+ if (m_CharacterTable != null && m_CharacterTable.Count > 0)
+ m_CharacterTable = m_CharacterTable.OrderBy(c => c.unicode).ToList();
+ }
+
+ /// <summary>
+ /// Sort the Glyph table by index values.
+ /// </summary>
+ internal void SortGlyphTable()
+ {
+ if (m_GlyphTable != null && m_GlyphTable.Count > 0)
+ m_GlyphTable = m_GlyphTable.OrderBy(c => c.index).ToList();
+ }
+
+ /// <summary>
+ /// Sort both glyph and character tables.
+ /// </summary>
+ internal void SortGlyphAndCharacterTables()
+ {
+ SortGlyphTable();
+ SortCharacterTable();
+ }
+
+
+ /// <summary>
+ /// Function to check if a certain character exists in the font asset.
+ /// </summary>
+ /// <param name="character"></param>
+ /// <returns></returns>
+ public bool HasCharacter(int character)
+ {
+ if (m_CharacterLookupDictionary == null)
+ return false;
+
+ if (m_CharacterLookupDictionary.ContainsKey((uint)character))
+ return true;
+
+ return false;
+ }
+
+
+ /// <summary>
+ /// Function to check if a certain character exists in the font asset.
+ /// </summary>
+ /// <param name="character"></param>
+ /// <returns></returns>
+ public bool HasCharacter(char character)
+ {
+ if (m_CharacterLookupDictionary == null)
+ return false;
+
+ if (m_CharacterLookupDictionary.ContainsKey(character))
+ return true;
+
+ return false;
+ }
+
+
+ /// <summary>
+ /// Function to check if a character is contained in the font asset with the option to also check through fallback font assets.
+ /// </summary>
+ /// <param name="character"></param>
+ /// <param name="searchFallbacks"></param>
+ /// <returns></returns>
+ public bool HasCharacter(char character, bool searchFallbacks)
+ {
+ // Read font asset definition if it hasn't already been done.
+ if (m_CharacterLookupDictionary == null)
+ {
+ ReadFontAssetDefinition();
+
+ if (m_CharacterLookupDictionary == null)
+ return false;
+ }
+
+ // Check font asset
+ if (m_CharacterLookupDictionary.ContainsKey(character))
+ return true;
+
+ // Check if font asset is dynamic and if so try to add the requested character to it.
+ if (m_AtlasPopulationMode == AtlasPopulationMode.Dynamic)
+ {
+ if (TryAddCharacterInternal(character, out TMP_Character temp))
+ return true;
+ }
+
+ if (searchFallbacks)
+ {
+ // Check font asset fallbacks
+ if (fallbackFontAssetTable != null && fallbackFontAssetTable.Count > 0)
+ {
+ for (int i = 0; i < fallbackFontAssetTable.Count && fallbackFontAssetTable[i] != null; i++)
+ {
+ if (fallbackFontAssetTable[i].HasCharacter_Internal(character, searchFallbacks))
+ return true;
+ }
+ }
+
+ // Check general fallback font assets.
+ if (TMP_Settings.fallbackFontAssets != null && TMP_Settings.fallbackFontAssets.Count > 0)
+ {
+ for (int i = 0; i < TMP_Settings.fallbackFontAssets.Count && TMP_Settings.fallbackFontAssets[i] != null; i++)
+ {
+ if (TMP_Settings.fallbackFontAssets[i].m_CharacterLookupDictionary == null)
+ TMP_Settings.fallbackFontAssets[i].ReadFontAssetDefinition();
+
+ if (TMP_Settings.fallbackFontAssets[i].m_CharacterLookupDictionary != null && TMP_Settings.fallbackFontAssets[i].HasCharacter_Internal(character, searchFallbacks))
+ return true;
+ }
+ }
+
+ // Check TMP Settings Default Font Asset
+ if (TMP_Settings.defaultFontAsset != null)
+ {
+ if (TMP_Settings.defaultFontAsset.m_CharacterLookupDictionary == null)
+ TMP_Settings.defaultFontAsset.ReadFontAssetDefinition();
+
+ if (TMP_Settings.defaultFontAsset.m_CharacterLookupDictionary != null && TMP_Settings.defaultFontAsset.HasCharacter_Internal(character, searchFallbacks))
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+
+ /// <summary>
+ /// Function to check if a character is contained in a font asset with the option to also check through fallback font assets.
+ /// This private implementation does not search the fallback font asset in the TMP Settings file.
+ /// </summary>
+ /// <param name="character"></param>
+ /// <param name="searchFallbacks"></param>
+ /// <returns></returns>
+ bool HasCharacter_Internal(char character, bool searchFallbacks)
+ {
+ // Read font asset definition if it hasn't already been done.
+ if (m_CharacterLookupDictionary == null)
+ {
+ ReadFontAssetDefinition();
+
+ if (m_CharacterLookupDictionary == null)
+ return false;
+ }
+
+ // Check font asset
+ if (m_CharacterLookupDictionary.ContainsKey(character))
+ return true;
+
+ if (searchFallbacks)
+ {
+ // Check Font Asset Fallback fonts.
+ if (fallbackFontAssetTable != null && fallbackFontAssetTable.Count > 0)
+ {
+ for (int i = 0; i < fallbackFontAssetTable.Count && fallbackFontAssetTable[i] != null; i++)
+ {
+ if (fallbackFontAssetTable[i].HasCharacter_Internal(character, searchFallbacks))
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+
+ /// <summary>
+ /// Function to check if certain characters exists in the font asset. Function returns a list of missing characters.
+ /// </summary>
+ /// <param name="character"></param>
+ /// <returns></returns>
+ public bool HasCharacters(string text, out List<char> missingCharacters)
+ {
+ if (m_CharacterLookupDictionary == null)
+ {
+ missingCharacters = null;
+ return false;
+ }
+
+ missingCharacters = new List<char>();
+
+ for (int i = 0; i < text.Length; i++)
+ {
+ if (!m_CharacterLookupDictionary.ContainsKey(text[i]))
+ missingCharacters.Add(text[i]);
+ }
+
+ if (missingCharacters.Count == 0)
+ return true;
+
+ return false;
+ }
+
+
+ /// <summary>
+ /// Function to check if certain characters exists in the font asset. Function returns false if any characters are missing.
+ /// </summary>
+ /// <param name="text">String containing the characters to check</param>
+ /// <returns></returns>
+ public bool HasCharacters(string text)
+ {
+ if (m_CharacterLookupDictionary == null)
+ return false;
+
+ for (int i = 0; i < text.Length; i++)
+ {
+ if (!m_CharacterLookupDictionary.ContainsKey(text[i]))
+ return false;
+ }
+
+ return true;
+ }
+
+
+ /// <summary>
+ /// Function to extract all the characters from a font asset.
+ /// </summary>
+ /// <param name="fontAsset"></param>
+ /// <returns></returns>
+ public static string GetCharacters(TMP_FontAsset fontAsset)
+ {
+ string characters = string.Empty;
+
+ for (int i = 0; i < fontAsset.characterTable.Count; i++)
+ {
+ characters += (char)fontAsset.characterTable[i].unicode;
+ }
+
+ return characters;
+ }
+
+
+ /// <summary>
+ /// Function which returns an array that contains all the characters from a font asset.
+ /// </summary>
+ /// <param name="fontAsset"></param>
+ /// <returns></returns>
+ public static int[] GetCharactersArray(TMP_FontAsset fontAsset)
+ {
+ int[] characters = new int[fontAsset.characterTable.Count];
+
+ for (int i = 0; i < fontAsset.characterTable.Count; i++)
+ {
+ characters[i] = (int)fontAsset.characterTable[i].unicode;
+ }
+
+ return characters;
+ }
+
+
+ // ================================================================================
+ // Properties and functions related to character and glyph additions as well as
+ // tacking glyphs that need to be added to various font asset atlas textures.
+ // ================================================================================
+
+ /// <summary>
+ /// Determines if the font asset is already registered to be updated.
+ /// </summary>
+ //private bool m_IsAlreadyRegisteredForUpdate;
+
+ /// <summary>
+ /// List of glyphs that need to be added / packed in atlas texture.
+ /// </summary>
+ private List<Glyph> m_GlyphsToPack = new List<Glyph>();
+
+ /// <summary>
+ /// List of glyphs that have been packed in the atlas texture and ready to be rendered.
+ /// </summary>
+ private List<Glyph> m_GlyphsPacked = new List<Glyph>();
+
+ /// <summary>
+ ///
+ /// </summary>
+ private List<Glyph> m_GlyphsToRender = new List<Glyph>();
+
+ /// <summary>
+ /// List used in the process of adding new glyphs to the atlas texture.
+ /// </summary>
+ private List<uint> m_GlyphIndexList = new List<uint>();
+ private List<TMP_Character> m_CharactersToAdd = new List<TMP_Character>();
+
+ /// <summary>
+ /// Internal static array used to avoid allocations when using the GetGlyphPairAdjustmentTable().
+ /// </summary>
+ internal static uint[] s_GlyphIndexArray = new uint[16];
+
+ /// <summary>
+ /// Internal static list used to track characters that could not be added to the font asset.
+ /// </summary>
+ internal static List<uint> s_MissingCharacterList = new List<uint>(16);
+
+ /// <summary>
+ /// Try adding the characters from the provided string to the font asset.
+ /// </summary>
+ /// <param name="unicodes">Array that contains the characters to add to the font asset.</param>
+ /// <returns>Returns true if all the characters were successfully added to the font asset. Return false otherwise.</returns>
+ public bool TryAddCharacters(uint[] unicodes)
+ {
+ return TryAddCharacters(unicodes, out uint[] missingUnicodes);
+ }
+
+ /// <summary>
+ /// Try adding the characters from the provided string to the font asset.
+ /// </summary>
+ /// <param name="unicodes">Array that contains the characters to add to the font asset.</param>
+ /// <param name="missingUnicodes">Array containing the characters that could not be added to the font asset.</param>
+ /// <returns>Returns true if all the characters were successfully added to the font asset. Return false otherwise.</returns>
+ public bool TryAddCharacters(uint[] unicodes, out uint[] missingUnicodes)
+ {
+ s_MissingCharacterList.Clear();
+
+ // Make sure font asset is set to dynamic and that we have a valid list of characters.
+ if (unicodes == null || unicodes.Length == 0 || m_AtlasPopulationMode == AtlasPopulationMode.Static)
+ {
+ if (m_AtlasPopulationMode == AtlasPopulationMode.Static)
+ Debug.LogWarning("Unable to add characters to font asset [" + this.name + "] because its AtlasPopulationMode is set to Static.", this);
+ else
+ {
+ Debug.LogWarning("Unable to add characters to font asset [" + this.name + "] because the provided Unicode list is Null or Empty.", this);
+ }
+
+ missingUnicodes = unicodes.ToArray();
+ return false;
+ }
+
+ Profiler.BeginSample("TMP.TryAddCharacter");
+
+ // Load font face.
+ if (FontEngine.LoadFontFace(m_SourceFontFile, m_FaceInfo.pointSize) != FontEngineError.Success)
+ {
+ Profiler.EndSample();
+
+ missingUnicodes = unicodes.ToArray();
+ return false;
+ }
+
+ // Clear data structures used to track which glyph needs to be added to atlas texture.
+ m_GlyphIndexList.Clear();
+ m_CharactersToAdd.Clear();
+
+ bool isMissingCharacters = false;
+ int unicodeCount = unicodes.Length;
+
+ for (int i = 0; i < unicodeCount; i++)
+ {
+ uint unicode = unicodes[i];
+
+ // Check if character is already contained in the character table.
+ if (m_CharacterLookupDictionary.ContainsKey(unicode))
+ continue;
+
+ // Get the index of the glyph for this unicode value.
+ uint glyphIndex = FontEngine.GetGlyphIndex(unicode);
+
+ if (glyphIndex == 0)
+ {
+ isMissingCharacters = true;
+ continue;
+ }
+
+ TMP_Character character = new TMP_Character(unicode, glyphIndex);
+
+ // Check if glyph is already contained in the font asset as the same glyph might be referenced by multiple characters.
+ if (m_GlyphLookupDictionary.ContainsKey(glyphIndex))
+ {
+ character.glyph = m_GlyphLookupDictionary[glyphIndex];
+ m_CharacterTable.Add(character);
+ m_CharacterLookupDictionary.Add(unicode, character);
+
+ continue;
+ }
+
+ m_GlyphIndexList.Add(glyphIndex);
+ m_CharactersToAdd.Add(character);
+ }
+
+ if (m_GlyphIndexList.Count == 0)
+ {
+ //Debug.LogWarning("No characters will be added to font asset [" + this.name + "] either because they are already present in the font asset or missing from the font file.");
+ Profiler.EndSample();
+
+ missingUnicodes = unicodes.ToArray();
+ return false;
+ }
+
+ // Resize the Atlas Texture to the appropriate size
+ if (m_AtlasTextures[m_AtlasTextureIndex].width == 0 || m_AtlasTextures[m_AtlasTextureIndex].height == 0)
+ {
+ //Debug.Log("Setting initial size of atlas texture used by font asset [" + this.name + "].");
+ m_AtlasTextures[m_AtlasTextureIndex].Resize(m_AtlasWidth, m_AtlasHeight);
+ FontEngine.ResetAtlasTexture(m_AtlasTextures[m_AtlasTextureIndex]);
+ }
+
+ bool allCharactersAdded = FontEngine.TryAddGlyphsToTexture(m_GlyphIndexList, m_AtlasPadding, GlyphPackingMode.BestShortSideFit, m_FreeGlyphRects, m_UsedGlyphRects, m_AtlasRenderMode, m_AtlasTextures[m_AtlasTextureIndex], out Glyph[] glyphs);
+
+ // Add new glyphs to relevant data structures.
+ for (int i = 0; i < glyphs.Length; i++)
+ {
+ Glyph glyph = glyphs[i];
+ uint glyphIndex = glyph.index;
+
+ // Add new glyph to glyph table.
+ m_GlyphTable.Add(glyph);
+ m_GlyphLookupDictionary.Add(glyphIndex, glyph);
+ }
+
+ // Add new characters to relevant data structures.
+ for (int i = 0; i < m_CharactersToAdd.Count; i++)
+ {
+ TMP_Character character = m_CharactersToAdd[i];
+ if (m_GlyphLookupDictionary.TryGetValue(character.glyphIndex, out Glyph glyph) == false)
+ {
+ s_MissingCharacterList.Add(character.unicode);
+ continue;
+ }
+
+ character.glyph = glyph;
+ m_CharacterTable.Add(character);
+ m_CharacterLookupDictionary.Add(character.unicode, character);
+ }
+
+ #if UNITY_EDITOR
+ // Makes the changes to the font asset persistent.
+ if (UnityEditor.EditorUtility.IsPersistent(this))
+ {
+ TMP_EditorResourceManager.RegisterResourceForUpdate(this);
+ }
+ #endif
+
+ Profiler.EndSample();
+
+ missingUnicodes = null;
+
+ if (s_MissingCharacterList.Count > 0)
+ missingUnicodes = s_MissingCharacterList.ToArray();
+
+ return allCharactersAdded && !isMissingCharacters;
+ }
+
+ /// <summary>
+ /// Try adding the characters from the provided string to the font asset.
+ /// </summary>
+ /// <param name="characters">String containing the characters to add to the font asset.</param>
+ /// <returns>Returns true if all the characters were successfully added to the font asset. Return false otherwise.</returns>
+ public bool TryAddCharacters(string characters)
+ {
+ return TryAddCharacters(characters, out string missingCharacters);
+ }
+
+
+ /// <summary>
+ /// Try adding the characters from the provided string to the font asset.
+ /// </summary>
+ /// <param name="characters">String containing the characters to add to the font asset.</param>
+ /// <param name="missingCharacters">String containing the characters that could not be added to the font asset.</param>
+ /// <returns>Returns true if all the characters were successfully added to the font asset. Return false otherwise.</returns>
+ public bool TryAddCharacters(string characters, out string missingCharacters)
+ {
+ // Make sure font asset is set to dynamic and that we have a valid list of characters.
+ if (string.IsNullOrEmpty(characters) || m_AtlasPopulationMode == AtlasPopulationMode.Static)
+ {
+ if (m_AtlasPopulationMode == AtlasPopulationMode.Static)
+ Debug.LogWarning("Unable to add characters to font asset [" + this.name + "] because its AtlasPopulationMode is set to Static.", this);
+ else
+ {
+ Debug.LogWarning("Unable to add characters to font asset [" + this.name + "] because the provided character list is Null or Empty.", this);
+ }
+
+ missingCharacters = characters;
+ return false;
+ }
+
+ // Load font face.
+ if (FontEngine.LoadFontFace(m_SourceFontFile, m_FaceInfo.pointSize) != FontEngineError.Success)
+ {
+ missingCharacters = characters;
+ return false;
+ }
+
+ // Clear data structures used to track which glyph needs to be added to atlas texture.
+ m_GlyphIndexList.Clear();
+ m_CharactersToAdd.Clear();
+
+ bool isMissingCharacters = false;
+ int characterCount = characters.Length;
+
+ // Iterate over each of the requested characters.
+ for (int i = 0; i < characterCount; i++)
+ {
+ uint unicode = characters[i];
+
+ // Check if character is already contained in the character table.
+ if (m_CharacterLookupDictionary.ContainsKey(unicode))
+ continue;
+
+ // Get the index of the glyph for this unicode value.
+ uint glyphIndex = FontEngine.GetGlyphIndex(unicode);
+
+ // Skip missing glyphs
+ if (glyphIndex == 0)
+ {
+ // Might want to keep track and report the missing characters.
+ isMissingCharacters = true;
+ continue;
+ }
+
+ TMP_Character character = new TMP_Character(unicode, glyphIndex);
+
+ // Check if glyph is already contained in the font asset as the same glyph might be referenced by multiple characters.
+ if (m_GlyphLookupDictionary.ContainsKey(glyphIndex))
+ {
+ character.glyph = m_GlyphLookupDictionary[glyphIndex];
+ m_CharacterTable.Add(character);
+ m_CharacterLookupDictionary.Add(unicode, character);
+
+ continue;
+ }
+
+ // Add glyph to list of glyphs to add and glyph lookup map.
+ m_GlyphIndexList.Add(glyphIndex);
+ m_CharactersToAdd.Add(character);
+ }
+
+ if (m_GlyphIndexList.Count == 0)
+ {
+ missingCharacters = characters;
+ return false;
+ }
+
+ // Resize the Atlas Texture to the appropriate size
+ if (m_AtlasTextures[m_AtlasTextureIndex].width == 0 || m_AtlasTextures[m_AtlasTextureIndex].height == 0)
+ {
+ //Debug.Log("Setting initial size of atlas texture used by font asset [" + this.name + "].");
+ m_AtlasTextures[m_AtlasTextureIndex].Resize(m_AtlasWidth, m_AtlasHeight);
+ FontEngine.ResetAtlasTexture(m_AtlasTextures[m_AtlasTextureIndex]);
+ }
+
+ bool allCharactersAdded = FontEngine.TryAddGlyphsToTexture(m_GlyphIndexList, m_AtlasPadding, GlyphPackingMode.BestShortSideFit, m_FreeGlyphRects, m_UsedGlyphRects, m_AtlasRenderMode, m_AtlasTextures[m_AtlasTextureIndex], out Glyph[] glyphs);
+
+ for (int i = 0; i < glyphs.Length; i++)
+ {
+ Glyph glyph = glyphs[i];
+ uint glyphIndex = glyph.index;
+
+ // Add new glyph to glyph table.
+ m_GlyphTable.Add(glyph);
+ m_GlyphLookupDictionary.Add(glyphIndex, glyph);
+ }
+
+ missingCharacters = string.Empty;
+
+ // Add new characters to relevant data structures.
+ for (int i = 0; i < m_CharactersToAdd.Count; i++)
+ {
+ TMP_Character character = m_CharactersToAdd[i];
+
+ if (m_GlyphLookupDictionary.TryGetValue(character.glyphIndex, out Glyph glyph) == false)
+ {
+ // TODO: Revise to avoid string concatenation.
+ missingCharacters += (char)character.unicode;
+ continue;
+ }
+
+ character.glyph = glyph;
+ m_CharacterTable.Add(character);
+ m_CharacterLookupDictionary.Add(character.unicode, character);
+ }
+
+ #if UNITY_EDITOR
+ // Makes the changes to the font asset persistent.
+ if (UnityEditor.EditorUtility.IsPersistent(this))
+ {
+ TMP_EditorResourceManager.RegisterResourceForUpdate(this);
+ }
+ #endif
+
+ return allCharactersAdded && !isMissingCharacters;
+ }
+
+
+ /// <summary>
+ /// NOT USED CURRENTLY - Try adding character using Unicode value to font asset.
+ /// </summary>
+ /// <param name="unicode">The Unicode value of the character.</param>
+ /// <param name="character">The character data if successfully added to the font asset. Null otherwise.</param>
+ /// <returns>Returns true if the character has been added. False otherwise.</returns>
+ internal bool TryAddCharacter_Internal(uint unicode)
+ {
+ TMP_Character character = null;
+
+ // Check if character is already contained in the character table.
+ if (m_CharacterLookupDictionary.ContainsKey(unicode))
+ return true;
+
+ uint glyphIndex = FontEngine.GetGlyphIndex(unicode);
+ if (glyphIndex == 0)
+ return false;
+
+ // Check if glyph is already contained in the font asset as the same glyph might be referenced by multiple characters.
+ if (m_GlyphLookupDictionary.ContainsKey(glyphIndex))
+ {
+ character = new TMP_Character(unicode, m_GlyphLookupDictionary[glyphIndex]);
+ m_CharacterTable.Add(character);
+ m_CharacterLookupDictionary.Add(unicode, character);
+
+ //#if UNITY_EDITOR
+ // Makes the changes to the font asset persistent.
+ // OPTIMIZATION: This could be handled when exiting Play mode if we added any new characters to the asset.
+ // Could also add some update registry to handle this.
+ //SortGlyphTable();
+ //UnityEditor.EditorUtility.SetDirty(this);
+ //#endif
+
+ return true;
+ }
+
+ // Resize the Atlas Texture to the appropriate size
+ if (m_AtlasTextures[m_AtlasTextureIndex].width == 0 || m_AtlasTextures[m_AtlasTextureIndex].height == 0)
+ {
+ //Debug.Log("Setting initial size of atlas texture used by font asset [" + this.name + "].");
+ m_AtlasTextures[m_AtlasTextureIndex].Resize(m_AtlasWidth, m_AtlasHeight);
+ FontEngine.ResetAtlasTexture(m_AtlasTextures[m_AtlasTextureIndex]);
+ }
+
+ if (FontEngine.TryAddGlyphToTexture(glyphIndex, m_AtlasPadding, GlyphPackingMode.BestShortSideFit, m_FreeGlyphRects, m_UsedGlyphRects, m_AtlasRenderMode, m_AtlasTextures[m_AtlasTextureIndex], out Glyph glyph))
+ {
+ // Add new glyph to glyph table.
+ m_GlyphTable.Add(glyph);
+ m_GlyphLookupDictionary.Add(glyphIndex, glyph);
+
+ // Add new character
+ character = new TMP_Character(unicode, glyph);
+ m_CharacterTable.Add(character);
+ m_CharacterLookupDictionary.Add(unicode, character);
+
+ //#if UNITY_EDITOR
+ // Makes the changes to the font asset persistent.
+ // OPTIMIZATION: This could be handled when exiting Play mode if we added any new characters to the asset.
+ // Could also add some update registry to handle this.
+ //SortGlyphTable();
+ //UnityEditor.EditorUtility.SetDirty(this);
+ //#endif
+
+ return true;
+ }
+
+ return false;
+ }
+
+
+ /// <summary>
+ /// To be removed.
+ /// </summary>
+ /// <param name="unicode"></param>
+ /// <param name="glyph"></param>
+ internal TMP_Character AddCharacter_Internal(uint unicode, Glyph glyph)
+ {
+ // Check if character is already contained in the character table.
+ if (m_CharacterLookupDictionary.ContainsKey(unicode))
+ return m_CharacterLookupDictionary[unicode];
+
+ uint glyphIndex = glyph.index;
+
+ // Resize the Atlas Texture to the appropriate size
+ if (m_AtlasTextures[m_AtlasTextureIndex].width == 0 || m_AtlasTextures[m_AtlasTextureIndex].height == 0)
+ {
+ //Debug.Log("Setting initial size of atlas texture used by font asset [" + this.name + "].");
+ m_AtlasTextures[m_AtlasTextureIndex].Resize(m_AtlasWidth, m_AtlasHeight);
+ FontEngine.ResetAtlasTexture(m_AtlasTextures[m_AtlasTextureIndex]);
+ }
+
+ // Check if glyph is already contained in the glyph table.
+ if (m_GlyphLookupDictionary.ContainsKey(glyphIndex) == false)
+ {
+ if (glyph.glyphRect.width == 0 || glyph.glyphRect.width == 0)
+ {
+ // Glyphs with zero width and / or height can be automatically added to font asset.
+ m_GlyphTable.Add(glyph);
+ }
+ else
+ {
+ // Try packing new glyph
+ if (FontEngine.TryPackGlyphInAtlas(glyph, m_AtlasPadding, GlyphPackingMode.ContactPointRule, m_AtlasRenderMode, m_AtlasWidth, m_AtlasHeight, m_FreeGlyphRects, m_UsedGlyphRects) == false)
+ {
+ // TODO: Add handling to create new atlas texture to fit glyph.
+
+ return null;
+ }
+
+ m_GlyphsToRender.Add(glyph);
+ }
+ }
+
+ // Add character to font asset.
+ TMP_Character character = new TMP_Character(unicode, glyph);
+ m_CharacterTable.Add(character);
+ m_CharacterLookupDictionary.Add(unicode, character);
+
+ //Debug.Log("Adding character [" + (char)unicode + "] with Unicode (" + unicode + ") to [" + this.name + "] font asset.");
+
+ // Schedule glyph to be added to the font atlas texture
+ //TM_FontAssetUpdateManager.RegisterFontAssetForUpdate(this);
+ UpdateAtlasTexture(); // Temporary until callback system is revised.
+
+ //#if UNITY_EDITOR
+ // Makes the changes to the font asset persistent.
+ // OPTIMIZATION: This could be handled when exiting Play mode if we added any new characters to the asset.
+ // Could also add some update registry to handle this.
+ //SortGlyphTable();
+ //UnityEditor.EditorUtility.SetDirty(this);
+ //#endif
+
+ return character;
+ }
+
+
+ /// <summary>
+ /// Try adding character using Unicode value to font asset.
+ /// Function assumes internal user has already checked to make sure the character is not already contained in the font asset.
+ /// </summary>
+ /// <param name="unicode">The Unicode value of the character.</param>
+ /// <param name="character">The character data if successfully added to the font asset. Null otherwise.</param>
+ /// <returns>Returns true if the character has been added. False otherwise.</returns>
+ internal bool TryAddCharacterInternal(uint unicode, out TMP_Character character)
+ {
+ character = null;
+
+ // Load font face.
+ if (FontEngine.LoadFontFace(sourceFontFile, m_FaceInfo.pointSize) != FontEngineError.Success)
+ return false;
+
+ uint glyphIndex = FontEngine.GetGlyphIndex(unicode);
+ if (glyphIndex == 0)
+ return false;
+
+ Profiler.BeginSample("TMP.TryAddCharacter");
+
+ // Check if glyph is already contained in the font asset as the same glyph might be referenced by multiple characters.
+ if (m_GlyphLookupDictionary.ContainsKey(glyphIndex))
+ {
+ character = new TMP_Character(unicode, m_GlyphLookupDictionary[glyphIndex]);
+ m_CharacterTable.Add(character);
+ m_CharacterLookupDictionary.Add(unicode, character);
+
+ if (TMP_Settings.getFontFeaturesAtRuntime)
+ UpdateGlyphAdjustmentRecords(unicode, glyphIndex);
+
+ #if UNITY_EDITOR
+ // Makes the changes to the font asset persistent.
+ // OPTIMIZATION: This could be handled when exiting Play mode if we added any new characters to the asset.
+ // Could also add some update registry to handle this.
+ //SortGlyphTable();
+ if (UnityEditor.EditorUtility.IsPersistent(this))
+ {
+ TMP_EditorResourceManager.RegisterResourceForUpdate(this);
+ }
+ #endif
+
+ Profiler.EndSample();
+
+ return true;
+ }
+
+ // Resize the Atlas Texture to the appropriate size
+ if (m_AtlasTextures[m_AtlasTextureIndex].width == 0 || m_AtlasTextures[m_AtlasTextureIndex].height == 0)
+ {
+ // TODO: Need texture to be readable.
+ if (m_AtlasTextures[m_AtlasTextureIndex].isReadable == false)
+ {
+ Debug.LogWarning("Unable to add the requested character to font asset [" + this.name + "]'s atlas texture. Please make the texture [" + m_AtlasTextures[m_AtlasTextureIndex].name + "] readable.", m_AtlasTextures[m_AtlasTextureIndex]);
+
+ Profiler.EndSample();
+ return false;
+ }
+
+ m_AtlasTextures[m_AtlasTextureIndex].Resize(m_AtlasWidth, m_AtlasHeight);
+ FontEngine.ResetAtlasTexture(m_AtlasTextures[m_AtlasTextureIndex]);
+ }
+
+ if (FontEngine.TryAddGlyphToTexture(glyphIndex, m_AtlasPadding, GlyphPackingMode.BestShortSideFit, m_FreeGlyphRects, m_UsedGlyphRects, m_AtlasRenderMode, m_AtlasTextures[m_AtlasTextureIndex], out Glyph glyph))
+ {
+ // Add new glyph to glyph table.
+ m_GlyphTable.Add(glyph);
+ m_GlyphLookupDictionary.Add(glyphIndex, glyph);
+
+ // Add new character
+ character = new TMP_Character(unicode, glyph);
+ m_CharacterTable.Add(character);
+ m_CharacterLookupDictionary.Add(unicode, character);
+
+ m_GlyphIndexList.Add(glyphIndex);
+
+ if (TMP_Settings.getFontFeaturesAtRuntime)
+ UpdateGlyphAdjustmentRecords(unicode, glyphIndex);
+
+ #if UNITY_EDITOR
+ // Makes the changes to the font asset persistent.
+ // OPTIMIZATION: This could be handled when exiting Play mode if we added any new characters to the asset.
+ // Could also add some update registry to handle this.
+ //SortGlyphTable();
+ if (UnityEditor.EditorUtility.IsPersistent(this))
+ {
+ TMP_EditorResourceManager.RegisterResourceForUpdate(this);
+ }
+ #endif
+
+ Profiler.EndSample();
+
+ return true;
+ }
+
+ Profiler.EndSample();
+
+ return false;
+ }
+
+
+ /// <summary>
+ /// Internal function used to get the glyph index for the given unicode.
+ /// </summary>
+ /// <param name="unicode"></param>
+ /// <returns></returns>
+ internal uint GetGlyphIndex(uint unicode)
+ {
+ // Load font face.
+ if (FontEngine.LoadFontFace(sourceFontFile, m_FaceInfo.pointSize) != FontEngineError.Success)
+ return 0;
+
+ return FontEngine.GetGlyphIndex(unicode);
+ }
+
+
+ internal void UpdateAtlasTexture()
+ {
+ // Return if we don't have any glyphs to add to atlas texture.
+ // This is possible if UpdateAtlasTexture() was called manually.
+ //if (m_GlyphsToPack.Count == 0)
+ // return;
+
+ if (m_GlyphsToRender.Count == 0)
+ return;
+
+ //Debug.Log("Updating [" + this.name + "]'s atlas texture.");
+
+ // Pack glyphs in the given atlas texture size.
+ // TODO: Packing and glyph render modes should be defined in the font asset.
+ //FontEngine.PackGlyphsInAtlas(m_GlyphsToPack, m_GlyphsPacked, m_AtlasPadding, GlyphPackingMode.ContactPointRule, GlyphRenderMode.SDFAA, m_AtlasWidth, m_AtlasHeight, m_FreeGlyphRects, m_UsedGlyphRects);
+ //FontEngine.RenderGlyphsToTexture(m_GlyphsPacked, m_AtlasPadding, GlyphRenderMode.SDFAA, m_AtlasTextures[m_AtlasTextureIndex]);
+
+ // Resize the Atlas Texture to the appropriate size
+ if (m_AtlasTextures[m_AtlasTextureIndex].width == 0 || m_AtlasTextures[m_AtlasTextureIndex].height == 0)
+ {
+ //Debug.Log("Setting initial size of atlas texture used by font asset [" + this.name + "].");
+ m_AtlasTextures[m_AtlasTextureIndex].Resize(m_AtlasWidth, m_AtlasHeight);
+ FontEngine.ResetAtlasTexture(m_AtlasTextures[m_AtlasTextureIndex]);
+ }
+
+ FontEngine.RenderGlyphsToTexture(m_GlyphsToRender, m_AtlasPadding, m_AtlasRenderMode, m_AtlasTextures[m_AtlasTextureIndex]);
+
+ // Apply changes to atlas texture
+ m_AtlasTextures[m_AtlasTextureIndex].Apply(false, false);
+
+ // Add glyphs that were successfully packed to the glyph table.
+ for (int i = 0; i < m_GlyphsToRender.Count /* m_GlyphsPacked.Count */; i++)
+ {
+ Glyph glyph = m_GlyphsToRender[i]; // m_GlyphsPacked[i];
+
+ // Update atlas texture index
+ glyph.atlasIndex = m_AtlasTextureIndex;
+
+ m_GlyphTable.Add(glyph);
+ m_GlyphLookupDictionary.Add(glyph.index, glyph);
+ }
+
+ // Clear list of glyphs
+ m_GlyphsPacked.Clear();
+ m_GlyphsToRender.Clear();
+
+ // Add any remaining glyphs into new atlas texture if multi texture support if enabled.
+ if (m_GlyphsToPack.Count > 0)
+ {
+ /*
+ // Create new atlas texture
+ Texture2D tex = new Texture2D(m_AtlasWidth, m_AtlasHeight, TextureFormat.Alpha8, false, true);
+ tex.SetPixels32(new Color32[m_AtlasWidth * m_AtlasHeight]);
+ tex.Apply();
+
+ m_AtlasTextureIndex++;
+
+ if (m_AtlasTextures.Length == m_AtlasTextureIndex)
+ Array.Resize(ref m_AtlasTextures, Mathf.NextPowerOfTwo(m_AtlasTextureIndex + 1));
+
+ m_AtlasTextures[m_AtlasTextureIndex] = tex;
+ */
+ }
+
+ #if UNITY_EDITOR
+ // Makes the changes to the font asset persistent.
+ SortGlyphAndCharacterTables();
+ TMP_EditorResourceManager.RegisterResourceForUpdate(this);
+ #endif
+ }
+
+
+ internal void UpdateGlyphAdjustmentRecords(uint unicode, uint glyphIndex)
+ {
+ Profiler.BeginSample("TMP.UpdateGlyphAdjustmentRecords");
+
+ int glyphCount = m_GlyphIndexList.Count;
+
+ if (s_GlyphIndexArray.Length <= glyphCount)
+ s_GlyphIndexArray = new uint[Mathf.NextPowerOfTwo(glyphCount + 1)];
+
+ for (int i = 0; i < glyphCount; i++)
+ s_GlyphIndexArray[i] = m_GlyphIndexList[i];
+
+ // Clear unused array elements
+ Array.Clear(s_GlyphIndexArray, glyphCount, s_GlyphIndexArray.Length - glyphCount);
+
+ // Get glyph pair adjustment records from font file.
+ // TODO: Revise FontEngine bindings to use a more efficient function where only the new glyph index is passed.
+ GlyphPairAdjustmentRecord[] pairAdjustmentRecords = FontEngine.GetGlyphPairAdjustmentTable(s_GlyphIndexArray);
+
+ if (pairAdjustmentRecords == null || pairAdjustmentRecords.Length == 0)
+ {
+ Profiler.EndSample();
+ return;
+ }
+
+ if (m_FontFeatureTable == null)
+ m_FontFeatureTable = new TMP_FontFeatureTable();
+
+ for (int i = 0; i < pairAdjustmentRecords.Length && pairAdjustmentRecords[i].firstAdjustmentRecord.glyphIndex != 0; i++)
+ {
+ long pairKey = (long)pairAdjustmentRecords[i].secondAdjustmentRecord.glyphIndex << 32 | pairAdjustmentRecords[i].firstAdjustmentRecord.glyphIndex;
+
+ // Check if table already contains a pair adjustment record for this key.
+ if (m_FontFeatureTable.m_GlyphPairAdjustmentRecordLookupDictionary.ContainsKey(pairKey))
+ continue;
+
+ TMP_GlyphPairAdjustmentRecord record = new TMP_GlyphPairAdjustmentRecord(pairAdjustmentRecords[i]);
+
+ m_FontFeatureTable.m_GlyphPairAdjustmentRecords.Add(record);
+ m_FontFeatureTable.m_GlyphPairAdjustmentRecordLookupDictionary.Add(pairKey, record);
+ }
+
+ #if UNITY_EDITOR
+ m_FontFeatureTable.SortGlyphPairAdjustmentRecords();
+ #endif
+
+ Profiler.EndSample();
+ }
+
+
+ /// <summary>
+ /// Clears font asset data including the glyph and character tables and textures.
+ /// Function might be changed to Internal and only used in tests.
+ /// </summary>
+ /// <param name="setAtlasSizeToZero">Will set the atlas texture size to zero width and height if true.</param>
+ public void ClearFontAssetData(bool setAtlasSizeToZero = false)
+ {
+ #if UNITY_EDITOR
+ // Record full object undo in the Editor.
+ //UnityEditor.Undo.RecordObjects(new UnityEngine.Object[] { this, this.atlasTexture }, "Resetting Font Asset");
+ #endif
+
+ // Clear glyph and character tables
+ if (m_GlyphTable != null)
+ m_GlyphTable.Clear();
+
+ if (m_CharacterTable != null)
+ m_CharacterTable.Clear();
+
+ // Clear glyph rectangles
+ if (m_UsedGlyphRects != null)
+ m_UsedGlyphRects.Clear();
+
+ if (m_FreeGlyphRects != null)
+ {
+ int packingModifier = ((GlyphRasterModes)m_AtlasRenderMode & GlyphRasterModes.RASTER_MODE_BITMAP) == GlyphRasterModes.RASTER_MODE_BITMAP ? 0 : 1;
+ m_FreeGlyphRects = new List<GlyphRect>() { new GlyphRect(0, 0, m_AtlasWidth - packingModifier, m_AtlasHeight - packingModifier) };
+ }
+
+ if (m_GlyphsToPack != null)
+ m_GlyphsToPack.Clear();
+
+ if (m_GlyphsPacked != null)
+ m_GlyphsPacked.Clear();
+
+ // Clear Glyph Adjustment Table
+ if (m_FontFeatureTable != null && m_FontFeatureTable.m_GlyphPairAdjustmentRecords != null)
+ m_FontFeatureTable.glyphPairAdjustmentRecords.Clear();
+
+ m_AtlasTextureIndex = 0;
+
+ // Clear atlas textures
+ if (m_AtlasTextures != null)
+ {
+ for (int i = 0; i < m_AtlasTextures.Length; i++)
+ {
+ Texture2D texture = m_AtlasTextures[i];
+
+ if (i > 0)
+ DestroyImmediate(texture, true);
+
+ if (texture == null)
+ continue;
+
+ // TODO: Need texture to be readable.
+ if (m_AtlasTextures[i].isReadable == false)
+ {
+ Debug.LogWarning("Unable to reset font asset [" + this.name + "]'s atlas texture. Please make the texture [" + m_AtlasTextures[i].name + "] readable.", m_AtlasTextures[i]);
+ continue;
+ }
+
+ if (setAtlasSizeToZero)
+ {
+ texture.Resize(0, 0, TextureFormat.Alpha8, false);
+ }
+ else if (texture.width != m_AtlasWidth || texture.height != m_AtlasHeight)
+ {
+ texture.Resize(m_AtlasWidth, m_AtlasHeight, TextureFormat.Alpha8, false);
+ }
+
+ // Clear texture atlas
+ FontEngine.ResetAtlasTexture(texture);
+ texture.Apply();
+
+ if (i == 0)
+ m_AtlasTexture = texture;
+
+ m_AtlasTextures[i] = texture;
+ }
+ }
+
+ #if UNITY_EDITOR
+ if (UnityEditor.EditorUtility.IsPersistent(this))
+ {
+ TMP_EditorResourceManager.RegisterResourceForReimport(this);
+ }
+ #endif
+
+ ReadFontAssetDefinition();
+ }
+
+
+
+ /// <summary>
+ /// Internal method used to upgrade font asset to support Dynamic SDF.
+ /// </summary>
+ private void UpgradeFontAsset()
+ {
+ m_Version = "1.1.0";
+
+ Debug.Log("Upgrading font asset [" + this.name + "] to version " + m_Version + ".", this);
+
+ m_FaceInfo.familyName = m_fontInfo.Name;
+ m_FaceInfo.styleName = string.Empty;
+
+ m_FaceInfo.pointSize = (int)m_fontInfo.PointSize;
+ m_FaceInfo.scale = m_fontInfo.Scale;
+
+ m_FaceInfo.lineHeight = m_fontInfo.LineHeight;
+ m_FaceInfo.ascentLine = m_fontInfo.Ascender;
+ m_FaceInfo.capLine = m_fontInfo.CapHeight;
+ m_FaceInfo.meanLine = m_fontInfo.CenterLine;
+ m_FaceInfo.baseline = m_fontInfo.Baseline;
+ m_FaceInfo.descentLine = m_fontInfo.Descender;
+
+ m_FaceInfo.superscriptOffset = m_fontInfo.SuperscriptOffset;
+ m_FaceInfo.superscriptSize = m_fontInfo.SubSize;
+ m_FaceInfo.subscriptOffset = m_fontInfo.SubscriptOffset;
+ m_FaceInfo.subscriptSize = m_fontInfo.SubSize;
+
+ m_FaceInfo.underlineOffset = m_fontInfo.Underline;
+ m_FaceInfo.underlineThickness = m_fontInfo.UnderlineThickness;
+ m_FaceInfo.strikethroughOffset = m_fontInfo.strikethrough;
+ m_FaceInfo.strikethroughThickness = m_fontInfo.strikethroughThickness;
+
+ m_FaceInfo.tabWidth = m_fontInfo.TabWidth;
+
+ if (m_AtlasTextures == null || m_AtlasTextures.Length == 0)
+ m_AtlasTextures = new Texture2D[1];
+
+ m_AtlasTextures[0] = atlas;
+
+ //atlas = null;
+
+ m_AtlasWidth = (int)m_fontInfo.AtlasWidth;
+ m_AtlasHeight = (int)m_fontInfo.AtlasHeight;
+ m_AtlasPadding = (int)m_fontInfo.Padding;
+
+ switch(m_CreationSettings.renderMode)
+ {
+ case 0:
+ m_AtlasRenderMode = GlyphRenderMode.SMOOTH_HINTED;
+ break;
+ case 1:
+ m_AtlasRenderMode = GlyphRenderMode.SMOOTH;
+ break;
+ case 2:
+ m_AtlasRenderMode = GlyphRenderMode.RASTER_HINTED;
+ break;
+ case 3:
+ m_AtlasRenderMode = GlyphRenderMode.RASTER;
+ break;
+ case 6:
+ m_AtlasRenderMode = GlyphRenderMode.SDF16;
+ break;
+ case 7:
+ m_AtlasRenderMode = GlyphRenderMode.SDF32;
+ break;
+ }
+
+ //m_fontInfo = null;
+
+ // Convert font weight table
+ if (fontWeights != null)
+ {
+ m_FontWeightTable[4] = fontWeights[4];
+ m_FontWeightTable[7] = fontWeights[7];
+
+ // Clear old fontWeight
+ //fontWeights = null;
+ }
+
+ // Convert font fallbacks
+ if (fallbackFontAssets != null && fallbackFontAssets.Count > 0)
+ {
+ if (m_FallbackFontAssetTable == null)
+ m_FallbackFontAssetTable = new List<TMP_FontAsset>(fallbackFontAssets.Count);
+
+ for (int i = 0; i < fallbackFontAssets.Count; i++)
+ m_FallbackFontAssetTable.Add(fallbackFontAssets[i]);
+
+ // Clear old fallbackFontAssets list
+ //fallbackFontAssets = null;
+ }
+
+ // Check if font asset creation settings contains a reference to the source font file GUID
+ if (m_CreationSettings.sourceFontFileGUID != null || m_CreationSettings.sourceFontFileGUID != string.Empty)
+ {
+ m_SourceFontFileGUID = m_CreationSettings.sourceFontFileGUID;
+ }
+ else
+ {
+ Debug.LogWarning("Font asset [" + this.name + "] doesn't have a reference to its source font file. Please assign the appropriate source font file for this asset in the Font Atlas & Material section of font asset inspector.", this);
+ }
+
+ // Convert legacy glyph and character tables to new format
+ m_GlyphTable.Clear();
+ m_CharacterTable.Clear();
+
+ //#if UNITY_EDITOR
+ // TODO: This is causing a crash in Unity and related to AssetDatabase.LoadAssetAtPath and Resources.Load()
+ // Load font to allow us to get the glyph index.
+ //string path = UnityEditor.AssetDatabase.GUIDToAssetPath(m_SourceFontFileGUID);
+
+ //if (path != string.Empty)
+ //{
+ //m_SourceFontFile_EditorRef = UnityEditor.AssetDatabase.LoadAssetAtPath<Font>(path);
+ //FontEngine.LoadFontFace(m_SourceFontFile_EditorRef);
+ //}
+ //#endif
+
+ bool isSpaceCharacterPresent = false;
+ for (int i = 0; i < m_glyphInfoList.Count; i++)
+ {
+ TMP_Glyph oldGlyph = m_glyphInfoList[i];
+
+ Glyph glyph = new Glyph();
+
+ uint glyphIndex = (uint)i + 1;
+
+ //#if UNITY_EDITOR
+ //if (m_SourceFontFile_EditorRef != null)
+ // glyphIndex = FontEngine.GetGlyphIndex((uint)oldGlyph.id);
+ //#endif
+
+ glyph.index = glyphIndex;
+ glyph.glyphRect = new GlyphRect((int)oldGlyph.x, m_AtlasHeight - (int)(oldGlyph.y + oldGlyph.height + 0.5f), (int)(oldGlyph.width + 0.5f), (int)(oldGlyph.height + 0.5f));
+ glyph.metrics = new GlyphMetrics(oldGlyph.width, oldGlyph.height, oldGlyph.xOffset, oldGlyph.yOffset, oldGlyph.xAdvance);
+ glyph.scale = oldGlyph.scale;
+ glyph.atlasIndex = 0;
+
+ m_GlyphTable.Add(glyph);
+
+ TMP_Character character = new TMP_Character((uint)oldGlyph.id, glyph);
+
+ if (oldGlyph.id == 32)
+ isSpaceCharacterPresent = true;
+
+ m_CharacterTable.Add(character);
+ }
+
+ // Special handling for the synthesized space character
+ if (!isSpaceCharacterPresent)
+ {
+ Debug.Log("Synthesizing Space for [" + this.name + "]");
+ Glyph glyph = new Glyph(0, new GlyphMetrics(0, 0, 0, 0, m_FaceInfo.ascentLine / 5), GlyphRect.zero, 1.0f, 0);
+ m_GlyphTable.Add(glyph);
+ m_CharacterTable.Add(new TMP_Character(32, glyph));
+ }
+
+ // Clear legacy glyph info list.
+ //m_glyphInfoList.Clear();
+
+ ReadFontAssetDefinition();
+
+ // Convert atlas textures data to new format
+ // TODO
+ #if UNITY_EDITOR
+ if (UnityEditor.EditorUtility.IsPersistent(this))
+ {
+ TMP_EditorResourceManager.RegisterResourceForUpdate(this);
+ }
+ #endif
+ }
+
+ /// <summary>
+ ///
+ /// </summary>
+ void UpgradeGlyphAdjustmentTableToFontFeatureTable()
+ {
+ Debug.Log("Upgrading font asset [" + this.name + "] Glyph Adjustment Table.", this);
+
+ if (m_FontFeatureTable == null)
+ m_FontFeatureTable = new TMP_FontFeatureTable();
+
+ int pairCount = m_KerningTable.kerningPairs.Count;
+
+ m_FontFeatureTable.m_GlyphPairAdjustmentRecords = new List<TMP_GlyphPairAdjustmentRecord>(pairCount);
+
+ for (int i = 0; i < pairCount; i++)
+ {
+ KerningPair pair = m_KerningTable.kerningPairs[i];
+
+ uint firstGlyphIndex = 0;
+ if (m_CharacterLookupDictionary.TryGetValue(pair.firstGlyph, out TMP_Character firstCharacter))
+ firstGlyphIndex = firstCharacter.glyphIndex;
+
+ uint secondGlyphIndex = 0;
+ if (m_CharacterLookupDictionary.TryGetValue(pair.secondGlyph, out TMP_Character secondCharacter))
+ secondGlyphIndex = secondCharacter.glyphIndex;
+
+ TMP_GlyphAdjustmentRecord firstAdjustmentRecord = new TMP_GlyphAdjustmentRecord(firstGlyphIndex, new TMP_GlyphValueRecord(pair.firstGlyphAdjustments.xPlacement, pair.firstGlyphAdjustments.yPlacement, pair.firstGlyphAdjustments.xAdvance, pair.firstGlyphAdjustments.yAdvance));
+ TMP_GlyphAdjustmentRecord secondAdjustmentRecord = new TMP_GlyphAdjustmentRecord(secondGlyphIndex, new TMP_GlyphValueRecord(pair.secondGlyphAdjustments.xPlacement, pair.secondGlyphAdjustments.yPlacement, pair.secondGlyphAdjustments.xAdvance, pair.secondGlyphAdjustments.yAdvance));
+ TMP_GlyphPairAdjustmentRecord record = new TMP_GlyphPairAdjustmentRecord(firstAdjustmentRecord, secondAdjustmentRecord);
+
+ m_FontFeatureTable.m_GlyphPairAdjustmentRecords.Add(record);
+ }
+
+ // TODO: Should clear legacy kerning table.
+ m_KerningTable.kerningPairs = null;
+ m_KerningTable = null;
+
+ #if UNITY_EDITOR
+ if (UnityEditor.EditorUtility.IsPersistent(this))
+ {
+ TMP_EditorResourceManager.RegisterResourceForUpdate(this);
+ }
+ #endif
+ }
+
+ }
+} \ No newline at end of file
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_FontAsset.cs.meta b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_FontAsset.cs.meta
new file mode 100644
index 0000000..b3ad117
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_FontAsset.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 71c1514a6bd24e1e882cebbe1904ce04
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {fileID: 2800000, guid: ee148e281f3c41c5b4ff5f8a5afe5a6c, type: 3}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_FontAssetCommon.cs b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_FontAssetCommon.cs
new file mode 100644
index 0000000..266ff63
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_FontAssetCommon.cs
@@ -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;
+ }
+ }
+} \ No newline at end of file
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_FontAssetCommon.cs.meta b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_FontAssetCommon.cs.meta
new file mode 100644
index 0000000..12f35ea
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_FontAssetCommon.cs.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: f695b5f9415c40b39ae877eaff41c96e
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_FontAssetUtilities.cs b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_FontAssetUtilities.cs
new file mode 100644
index 0000000..4fb9200
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_FontAssetUtilities.cs
@@ -0,0 +1,360 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+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>
+ /// List containing instance ID of font assets already searched.
+ /// </summary>
+ private static List<int> k_SearchedFontAssets;
+
+
+ /// <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="type">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, out TMP_FontAsset fontAsset)
+ {
+ if (includeFallbacks)
+ {
+ if (k_SearchedFontAssets == null)
+ k_SearchedFontAssets = new List<int>();
+ else
+ k_SearchedFontAssets.Clear();
+ }
+
+ return GetCharacterFromFontAsset_Internal(unicode, sourceFontAsset, includeFallbacks, fontStyle, fontWeight, out isAlternativeTypeface, out fontAsset);
+ }
+
+
+ /// <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, out TMP_FontAsset fontAsset)
+ {
+ fontAsset = null;
+ isAlternativeTypeface = false;
+ TMP_Character characterData = 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;
+ }
+
+ fontAsset = isItalic ? fontWeights[fontWeightIndex].italicTypeface : fontWeights[fontWeightIndex].regularTypeface;
+
+ if (fontAsset != null)
+ {
+ if (fontAsset.characterLookupTable.TryGetValue(unicode, out characterData))
+ {
+ isAlternativeTypeface = true;
+
+ return characterData;
+ }
+ else if (fontAsset.atlasPopulationMode == AtlasPopulationMode.Dynamic)
+ {
+ if (fontAsset.TryAddCharacterInternal(unicode, out characterData))
+ {
+ isAlternativeTypeface = true;
+
+ return characterData;
+ }
+
+ // 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 characterData))
+ {
+ // We were able to locate the requested character in the given font asset.
+ fontAsset = sourceFontAsset;
+
+ return characterData;
+ }
+ else if (sourceFontAsset.atlasPopulationMode == AtlasPopulationMode.Dynamic)
+ {
+ if (sourceFontAsset.TryAddCharacterInternal(unicode, out characterData))
+ {
+ fontAsset = sourceFontAsset;
+
+ return characterData;
+ }
+
+ //// Check if the source font file contains the requested character.
+ //if (TryGetCharacterFromFontFile(unicode, sourceFontAsset, out characterData))
+ //{
+ // fontAsset = sourceFontAsset;
+
+ // //fontAsset.AddCharacterToRasterList(unicode);
+
+ // 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)
+ }
+
+ // Search fallback font assets if we still don't have a valid character and include fallback is set to true.
+ if (characterData == 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 (fallbackFontAssets != null && fallbackCount > 0)
+ {
+ for (int i = 0; i < fallbackCount && characterData == null; i++)
+ {
+ TMP_FontAsset temp = fallbackFontAssets[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);
+
+ characterData = GetCharacterFromFontAsset_Internal(unicode, temp, includeFallbacks, fontStyle, fontWeight, out isAlternativeTypeface, out fontAsset);
+
+ if (characterData != null)
+ {
+ return characterData;
+ }
+ }
+ }
+
+ }
+
+ 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="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="type">Determines 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 GetCharacterFromFontAssets(uint unicode, List<TMP_FontAsset> fontAssets, bool includeFallbacks, FontStyles fontStyle, FontWeight fontWeight, out bool isAlternativeTypeface, out TMP_FontAsset fontAsset)
+ {
+ isAlternativeTypeface = false;
+
+ // Make sure font asset list is valid
+ if (fontAssets == null || fontAssets.Count == 0)
+ {
+ fontAsset = null;
+ return null;
+ }
+
+ if (includeFallbacks)
+ {
+ if (k_SearchedFontAssets == null)
+ k_SearchedFontAssets = new List<int>();
+ else
+ k_SearchedFontAssets.Clear();
+ }
+
+ int fontAssetCount = fontAssets.Count;
+
+ for (int i = 0; i < fontAssetCount; i++)
+ {
+ if (fontAssets[i] == null) continue;
+
+ TMP_Character characterData = GetCharacterFromFontAsset_Internal(unicode, fontAssets[i], includeFallbacks, fontStyle, fontWeight, out isAlternativeTypeface, out fontAsset);
+
+ if (characterData != null)
+ return characterData;
+ }
+
+ fontAsset = null;
+
+ 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;
+ }
+
+ }
+}
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_FontAssetUtilities.cs.meta b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_FontAssetUtilities.cs.meta
new file mode 100644
index 0000000..cae825d
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_FontAssetUtilities.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 0a017569bfe174e4890797b4d64cbabc
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_FontFeatureTable.cs b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_FontFeatureTable.cs
new file mode 100644
index 0000000..e234704
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_FontFeatureTable.cs
@@ -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>
+ internal List<TMP_GlyphPairAdjustmentRecord> glyphPairAdjustmentRecords
+ {
+ get { return m_GlyphPairAdjustmentRecords; }
+ set { m_GlyphPairAdjustmentRecords = value; }
+ }
+ [SerializeField]
+ internal List<TMP_GlyphPairAdjustmentRecord> m_GlyphPairAdjustmentRecords;
+
+ /// <summary>
+ ///
+ /// </summary>
+ internal Dictionary<long, TMP_GlyphPairAdjustmentRecord> m_GlyphPairAdjustmentRecordLookupDictionary;
+
+ // =============================================
+ // Constructor(s)
+ // =============================================
+
+ public TMP_FontFeatureTable()
+ {
+ m_GlyphPairAdjustmentRecords = new List<TMP_GlyphPairAdjustmentRecord>();
+ m_GlyphPairAdjustmentRecordLookupDictionary = new Dictionary<long, 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();
+ }
+ }
+} \ No newline at end of file
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_FontFeatureTable.cs.meta b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_FontFeatureTable.cs.meta
new file mode 100644
index 0000000..d08e812
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_FontFeatureTable.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 5ea9f573d4b800a49b9d83a1f61c0a88
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_FontFeaturesCommon.cs b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_FontFeaturesCommon.cs
new file mode 100644
index 0000000..8fd5e35
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_FontFeaturesCommon.cs
@@ -0,0 +1,223 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using UnityEngine.TextCore.LowLevel;
+
+
+namespace TMPro
+{
+ public enum FontFeatureLookupFlags
+ {
+ 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]
+ private float m_XPlacement;
+
+ [SerializeField]
+ private float m_YPlacement;
+
+ [SerializeField]
+ private float m_XAdvance;
+
+ [SerializeField]
+ private 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]
+ private uint m_GlyphIndex;
+
+ [SerializeField]
+ private 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]
+ private TMP_GlyphAdjustmentRecord m_FirstAdjustmentRecord;
+
+ [SerializeField]
+ private TMP_GlyphAdjustmentRecord m_SecondAdjustmentRecord;
+
+ [SerializeField]
+ private 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;
+ }
+
+ /// <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);
+ }
+ }
+
+ public struct GlyphPairKey
+ {
+ public uint firstGlyphIndex;
+ public uint secondGlyphIndex;
+ public long key;
+
+ public GlyphPairKey(uint firstGlyphIndex, uint secondGlyphIndex)
+ {
+ this.firstGlyphIndex = firstGlyphIndex;
+ this.secondGlyphIndex = secondGlyphIndex;
+ key = (long)secondGlyphIndex << 32 | firstGlyphIndex;
+ }
+
+ internal GlyphPairKey(TMP_GlyphPairAdjustmentRecord record)
+ {
+ firstGlyphIndex = record.firstAdjustmentRecord.glyphIndex;
+ secondGlyphIndex = record.secondAdjustmentRecord.glyphIndex;
+ key = (long)secondGlyphIndex << 32 | firstGlyphIndex;
+ }
+ }
+}
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_FontFeaturesCommon.cs.meta b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_FontFeaturesCommon.cs.meta
new file mode 100644
index 0000000..f0c7004
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_FontFeaturesCommon.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 27df3b12f30d0b74a9b10a3968c402ff
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_InputField.cs b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_InputField.cs
new file mode 100644
index 0000000..33ec8be
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_InputField.cs
@@ -0,0 +1,4149 @@
+//#define TMP_DEBUG_MODE
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Text;
+using System.Text.RegularExpressions;
+using UnityEngine;
+using UnityEngine.UI;
+using UnityEngine.Events;
+using UnityEngine.EventSystems;
+using UnityEngine.Serialization;
+
+
+namespace TMPro
+{
+ /// <summary>
+ /// Editable text input field.
+ /// </summary>
+ [AddComponentMenu("UI/TextMeshPro - Input Field", 11)]
+ public class TMP_InputField : Selectable,
+ IUpdateSelectedHandler,
+ IBeginDragHandler,
+ IDragHandler,
+ IEndDragHandler,
+ IPointerClickHandler,
+ ISubmitHandler,
+ ICanvasElement,
+ ILayoutElement,
+ IScrollHandler
+ {
+
+ // Setting the content type acts as a shortcut for setting a combination of InputType, CharacterValidation, LineType, and TouchScreenKeyboardType
+ public enum ContentType
+ {
+ Standard,
+ Autocorrected,
+ IntegerNumber,
+ DecimalNumber,
+ Alphanumeric,
+ Name,
+ EmailAddress,
+ Password,
+ Pin,
+ Custom
+ }
+
+ public enum InputType
+ {
+ Standard,
+ AutoCorrect,
+ Password,
+ }
+
+ public enum CharacterValidation
+ {
+ None,
+ Digit,
+ Integer,
+ Decimal,
+ Alphanumeric,
+ Name,
+ Regex,
+ EmailAddress,
+ CustomValidator
+ }
+
+ public enum LineType
+ {
+ SingleLine,
+ MultiLineSubmit,
+ MultiLineNewline
+ }
+
+ public delegate char OnValidateInput(string text, int charIndex, char addedChar);
+
+ [Serializable]
+ public class SubmitEvent : UnityEvent<string> { }
+
+ [Serializable]
+ public class OnChangeEvent : UnityEvent<string> { }
+
+ [Serializable]
+ public class SelectionEvent : UnityEvent<string> { }
+
+ [Serializable]
+ public class TextSelectionEvent : UnityEvent<string, int, int> { }
+
+ [Serializable]
+ public class TouchScreenKeyboardEvent : UnityEvent<TouchScreenKeyboard.Status> { }
+
+ protected TouchScreenKeyboard m_SoftKeyboard;
+ static private readonly char[] kSeparators = { ' ', '.', ',', '\t', '\r', '\n' };
+
+ #region Exposed properties
+ /// <summary>
+ /// Text Text used to display the input's value.
+ /// </summary>
+
+ [SerializeField]
+ protected RectTransform m_TextViewport;
+
+ //Vector3[] m_ViewportCorners = new Vector3[4];
+
+ [SerializeField]
+ protected TMP_Text m_TextComponent;
+
+ protected RectTransform m_TextComponentRectTransform;
+
+ [SerializeField]
+ protected Graphic m_Placeholder;
+
+ [SerializeField]
+ protected Scrollbar m_VerticalScrollbar;
+
+ [SerializeField]
+ protected TMP_ScrollbarEventHandler m_VerticalScrollbarEventHandler;
+ //private bool m_ForceDeactivation;
+
+ private bool m_IsDrivenByLayoutComponents = false;
+
+ /// <summary>
+ /// Used to keep track of scroll position
+ /// </summary>
+ private float m_ScrollPosition;
+
+ /// <summary>
+ ///
+ /// </summary>
+ [SerializeField]
+ protected float m_ScrollSensitivity = 1.0f;
+
+ //[SerializeField]
+ //protected TMP_Text m_PlaceholderTextComponent;
+
+ [SerializeField]
+ private ContentType m_ContentType = ContentType.Standard;
+
+ /// <summary>
+ /// Type of data expected by the input field.
+ /// </summary>
+ [SerializeField]
+ private InputType m_InputType = InputType.Standard;
+
+ /// <summary>
+ /// The character used to hide text in password field.
+ /// </summary>
+ [SerializeField]
+ private char m_AsteriskChar = '*';
+
+ /// <summary>
+ /// Keyboard type applies to mobile keyboards that get shown.
+ /// </summary>
+ [SerializeField]
+ private TouchScreenKeyboardType m_KeyboardType = TouchScreenKeyboardType.Default;
+
+ [SerializeField]
+ private LineType m_LineType = LineType.SingleLine;
+
+ /// <summary>
+ /// Should hide mobile input field part of the virtual keyboard.
+ /// </summary>
+ [SerializeField]
+ private bool m_HideMobileInput = false;
+
+ /// <summary>
+ /// Should hide soft / virtual keyboard.
+ /// </summary>
+ [SerializeField]
+ private bool m_HideSoftKeyboard = false;
+
+ /// <summary>
+ /// What kind of validation to use with the input field's data.
+ /// </summary>
+ [SerializeField]
+ private CharacterValidation m_CharacterValidation = CharacterValidation.None;
+
+ /// <summary>
+ /// The Regex expression used for validating the text input.
+ /// </summary>
+ [SerializeField]
+ private string m_RegexValue = string.Empty;
+
+ /// <summary>
+ /// The point sized used by the placeholder and input text object.
+ /// </summary>
+ [SerializeField]
+ private float m_GlobalPointSize = 14;
+
+ /// <summary>
+ /// Maximum number of characters allowed before input no longer works.
+ /// </summary>
+ [SerializeField]
+ private int m_CharacterLimit = 0;
+
+ /// <summary>
+ /// Event delegates triggered when the input field submits its data.
+ /// </summary>
+ [SerializeField]
+ private SubmitEvent m_OnEndEdit = new SubmitEvent();
+
+ /// <summary>
+ /// Event delegates triggered when the input field submits its data.
+ /// </summary>
+ [SerializeField]
+ private SubmitEvent m_OnSubmit = new SubmitEvent();
+
+ /// <summary>
+ /// Event delegates triggered when the input field is focused.
+ /// </summary>
+ [SerializeField]
+ private SelectionEvent m_OnSelect = new SelectionEvent();
+
+ /// <summary>
+ /// Event delegates triggered when the input field focus is lost.
+ /// </summary>
+ [SerializeField]
+ private SelectionEvent m_OnDeselect = new SelectionEvent();
+
+ /// <summary>
+ /// Event delegates triggered when the text is selected / highlighted.
+ /// </summary>
+ [SerializeField]
+ private TextSelectionEvent m_OnTextSelection = new TextSelectionEvent();
+
+ /// <summary>
+ /// Event delegates triggered when text is no longer select / highlighted.
+ /// </summary>
+ [SerializeField]
+ private TextSelectionEvent m_OnEndTextSelection = new TextSelectionEvent();
+
+ /// <summary>
+ /// Event delegates triggered when the input field changes its data.
+ /// </summary>
+ [SerializeField]
+ private OnChangeEvent m_OnValueChanged = new OnChangeEvent();
+
+ /// <summary>
+ /// Event delegates triggered when the status of the TouchScreenKeyboard changes.
+ /// </summary>
+ [SerializeField]
+ private TouchScreenKeyboardEvent m_OnTouchScreenKeyboardStatusChanged = new TouchScreenKeyboardEvent();
+
+ /// <summary>
+ /// Custom validation callback.
+ /// </summary>
+ [SerializeField]
+ private OnValidateInput m_OnValidateInput;
+
+ [SerializeField]
+ private Color m_CaretColor = new Color(50f / 255f, 50f / 255f, 50f / 255f, 1f);
+
+ [SerializeField]
+ private bool m_CustomCaretColor = false;
+
+ [SerializeField]
+ private Color m_SelectionColor = new Color(168f / 255f, 206f / 255f, 255f / 255f, 192f / 255f);
+
+ /// <summary>
+ /// Input field's value.
+ /// </summary>
+
+ [SerializeField]
+ [TextArea(5, 10)]
+ protected string m_Text = string.Empty;
+
+ [SerializeField]
+ [Range(0f, 4f)]
+ private float m_CaretBlinkRate = 0.85f;
+
+ [SerializeField]
+ [Range(1, 5)]
+ private int m_CaretWidth = 1;
+
+ [SerializeField]
+ private bool m_ReadOnly = false;
+
+ [SerializeField]
+ private bool m_RichText = true;
+
+ #endregion
+
+ protected int m_StringPosition = 0;
+ protected int m_StringSelectPosition = 0;
+ protected int m_CaretPosition = 0;
+ protected int m_CaretSelectPosition = 0;
+
+ private RectTransform caretRectTrans = null;
+ protected UIVertex[] m_CursorVerts = null;
+ private CanvasRenderer m_CachedInputRenderer;
+ private Vector2 m_LastPosition;
+
+ [NonSerialized]
+ protected Mesh m_Mesh;
+ private bool m_AllowInput = false;
+ //bool m_HasLostFocus = false;
+ private bool m_ShouldActivateNextUpdate = false;
+ private bool m_UpdateDrag = false;
+ private bool m_DragPositionOutOfBounds = false;
+ private const float kHScrollSpeed = 0.05f;
+ private const float kVScrollSpeed = 0.10f;
+ protected bool m_CaretVisible;
+ private Coroutine m_BlinkCoroutine = null;
+ private float m_BlinkStartTime = 0.0f;
+ private Coroutine m_DragCoroutine = null;
+ private string m_OriginalText = "";
+ private bool m_WasCanceled = false;
+ private bool m_HasDoneFocusTransition = false;
+ private WaitForSecondsRealtime m_WaitForSecondsRealtime;
+ private bool m_PreventCallback = false;
+
+ private bool m_TouchKeyboardAllowsInPlaceEditing = false;
+
+ private bool m_IsTextComponentUpdateRequired = false;
+ private bool m_IsScrollbarUpdateRequired = false;
+ private bool m_IsUpdatingScrollbarValues = false;
+
+ private bool m_isLastKeyBackspace = false;
+ private float m_PointerDownClickStartTime;
+ private float m_KeyDownStartTime;
+ private float m_DoubleClickDelay = 0.5f;
+
+ // Doesn't include dot and @ on purpose! See usage for details.
+ const string kEmailSpecialCharacters = "!#$%&'*+-/=?^_`{|}~";
+
+ private BaseInput inputSystem
+ {
+ get
+ {
+ if (EventSystem.current && EventSystem.current.currentInputModule)
+ return EventSystem.current.currentInputModule.input;
+ return null;
+ }
+ }
+
+ private string compositionString
+ {
+ get { return inputSystem != null ? inputSystem.compositionString : Input.compositionString; }
+ }
+
+
+
+ protected TMP_InputField()
+ {
+ SetTextComponentWrapMode();
+ }
+
+ protected Mesh mesh
+ {
+ get
+ {
+ if (m_Mesh == null)
+ m_Mesh = new Mesh();
+ return m_Mesh;
+ }
+ }
+
+ /// <summary>
+ /// Should the mobile keyboard input be hidden.
+ /// </summary>
+ public bool shouldHideMobileInput
+ {
+ get
+ {
+ switch (Application.platform)
+ {
+ case RuntimePlatform.Android:
+ case RuntimePlatform.IPhonePlayer:
+ case RuntimePlatform.tvOS:
+ return m_HideMobileInput;
+ default:
+ return true;
+ }
+ }
+
+ set
+ {
+ switch(Application.platform)
+ {
+ case RuntimePlatform.Android:
+ case RuntimePlatform.IPhonePlayer:
+ case RuntimePlatform.tvOS:
+ SetPropertyUtility.SetStruct(ref m_HideMobileInput, value);
+ break;
+ default:
+ m_HideMobileInput = true;
+ break;
+ }
+ }
+ }
+
+ public bool shouldHideSoftKeyboard
+ {
+ get
+ {
+ switch (Application.platform)
+ {
+ case RuntimePlatform.Android:
+ case RuntimePlatform.IPhonePlayer:
+ case RuntimePlatform.tvOS:
+ case RuntimePlatform.WSAPlayerX86:
+ case RuntimePlatform.WSAPlayerX64:
+ case RuntimePlatform.WSAPlayerARM:
+ return m_HideSoftKeyboard;
+ default:
+ return true;
+ }
+ }
+
+ set
+ {
+ switch (Application.platform)
+ {
+ case RuntimePlatform.Android:
+ case RuntimePlatform.IPhonePlayer:
+ case RuntimePlatform.tvOS:
+ case RuntimePlatform.WSAPlayerX86:
+ case RuntimePlatform.WSAPlayerX64:
+ case RuntimePlatform.WSAPlayerARM:
+ SetPropertyUtility.SetStruct(ref m_HideSoftKeyboard, value);
+ break;
+ default:
+ m_HideSoftKeyboard = true;
+ break;
+ }
+
+ if (m_HideSoftKeyboard == true && m_SoftKeyboard != null && TouchScreenKeyboard.isSupported && m_SoftKeyboard.active)
+ {
+ m_SoftKeyboard.active = false;
+ m_SoftKeyboard = null;
+ }
+ }
+ }
+
+ private bool isKeyboardUsingEvents()
+ {
+ switch (Application.platform)
+ {
+ case RuntimePlatform.Android:
+ case RuntimePlatform.IPhonePlayer:
+ case RuntimePlatform.tvOS:
+ return false;
+ default:
+ return true;
+ }
+ }
+
+ /// <summary>
+ /// Input field's current text value. This is not necessarily the same as what is visible on screen.
+ /// </summary>
+ /// <remarks>
+ /// Note that null is invalid value for InputField.text.
+ /// </remarks>
+ /// <example>
+ /// <code>
+ /// using UnityEngine;
+ /// using System.Collections;
+ /// using UnityEngine.UI; // Required when Using UI elements.
+ ///
+ /// public class Example : MonoBehaviour
+ /// {
+ /// public InputField mainInputField;
+ ///
+ /// public void Start()
+ /// {
+ /// mainInputField.text = "Enter Text Here...";
+ /// }
+ /// }
+ /// </code>
+ /// </example>
+ public string text
+ {
+ get
+ {
+ return m_Text;
+ }
+ set
+ {
+ SetText(value);
+ }
+ }
+
+ /// <summary>
+ /// Set Input field's current text value without invoke onValueChanged. This is not necessarily the same as what is visible on screen.
+ /// </summary>
+ public void SetTextWithoutNotify(string input)
+ {
+ SetText(input, false);
+ }
+
+ void SetText(string value, bool sendCallback = true)
+ {
+ if (this.text == value)
+ return;
+
+ if (value == null)
+ value = "";
+
+ value = value.Replace("\0", string.Empty); // remove embedded nulls
+
+ m_Text = value;
+
+ /*
+ if (m_LineType == LineType.SingleLine)
+ value = value.Replace("\n", "").Replace("\t", "");
+
+ // If we have an input validator, validate the input and apply the character limit at the same time.
+ if (onValidateInput != null || characterValidation != CharacterValidation.None)
+ {
+ m_Text = "";
+ OnValidateInput validatorMethod = onValidateInput ?? Validate;
+ m_CaretPosition = m_CaretSelectPosition = value.Length;
+ int charactersToCheck = characterLimit > 0 ? Math.Min(characterLimit, value.Length) : value.Length;
+ for (int i = 0; i < charactersToCheck; ++i)
+ {
+ char c = validatorMethod(m_Text, m_Text.Length, value[i]);
+ if (c != 0)
+ m_Text += c;
+ }
+ }
+ else
+ {
+ m_Text = characterLimit > 0 && value.Length > characterLimit ? value.Substring(0, characterLimit) : value;
+ }
+ */
+
+ #if UNITY_EDITOR
+ if (!Application.isPlaying)
+ {
+ SendOnValueChangedAndUpdateLabel();
+ return;
+ }
+ #endif
+
+ if (m_SoftKeyboard != null)
+ m_SoftKeyboard.text = m_Text;
+
+ if (m_StringPosition > m_Text.Length)
+ m_StringPosition = m_StringSelectPosition = m_Text.Length;
+ else if (m_StringSelectPosition > m_Text.Length)
+ m_StringSelectPosition = m_Text.Length;
+
+ // Set RectTransform relative position to top of viewport.
+ AdjustTextPositionRelativeToViewport(0);
+
+ m_forceRectTransformAdjustment = true;
+
+ m_IsTextComponentUpdateRequired = true;
+ UpdateLabel();
+
+ if (sendCallback)
+ SendOnValueChanged();
+ }
+
+
+ public bool isFocused
+ {
+ get { return m_AllowInput; }
+ }
+
+ public float caretBlinkRate
+ {
+ get { return m_CaretBlinkRate; }
+ set
+ {
+ if (SetPropertyUtility.SetStruct(ref m_CaretBlinkRate, value))
+ {
+ if (m_AllowInput)
+ SetCaretActive();
+ }
+ }
+ }
+
+ public int caretWidth { get { return m_CaretWidth; } set { if (SetPropertyUtility.SetStruct(ref m_CaretWidth, value)) MarkGeometryAsDirty(); } }
+
+ public RectTransform textViewport { get { return m_TextViewport; } set { SetPropertyUtility.SetClass(ref m_TextViewport, value); } }
+
+ public TMP_Text textComponent
+ {
+ get { return m_TextComponent; }
+ set
+ {
+ if (SetPropertyUtility.SetClass(ref m_TextComponent, value))
+ {
+ SetTextComponentWrapMode();
+ }
+ }
+ }
+
+ //public TMP_Text placeholderTextComponent { get { return m_PlaceholderTextComponent; } set { SetPropertyUtility.SetClass(ref m_PlaceholderTextComponent, value); } }
+
+ public Graphic placeholder { get { return m_Placeholder; } set { SetPropertyUtility.SetClass(ref m_Placeholder, value); } }
+
+ public Scrollbar verticalScrollbar
+ {
+ get { return m_VerticalScrollbar; }
+ set
+ {
+ if (m_VerticalScrollbar != null)
+ m_VerticalScrollbar.onValueChanged.RemoveListener(OnScrollbarValueChange);
+
+ SetPropertyUtility.SetClass(ref m_VerticalScrollbar, value);
+
+ if (m_VerticalScrollbar)
+ {
+ m_VerticalScrollbar.onValueChanged.AddListener(OnScrollbarValueChange);
+
+ }
+ }
+ }
+
+ public float scrollSensitivity { get { return m_ScrollSensitivity; } set { if (SetPropertyUtility.SetStruct(ref m_ScrollSensitivity, value)) MarkGeometryAsDirty(); } }
+
+ public Color caretColor { get { return customCaretColor ? m_CaretColor : textComponent.color; } set { if (SetPropertyUtility.SetColor(ref m_CaretColor, value)) MarkGeometryAsDirty(); } }
+
+ public bool customCaretColor { get { return m_CustomCaretColor; } set { if (m_CustomCaretColor != value) { m_CustomCaretColor = value; MarkGeometryAsDirty(); } } }
+
+ public Color selectionColor { get { return m_SelectionColor; } set { if (SetPropertyUtility.SetColor(ref m_SelectionColor, value)) MarkGeometryAsDirty(); } }
+
+ public SubmitEvent onEndEdit { get { return m_OnEndEdit; } set { SetPropertyUtility.SetClass(ref m_OnEndEdit, value); } }
+
+ public SubmitEvent onSubmit { get { return m_OnSubmit; } set { SetPropertyUtility.SetClass(ref m_OnSubmit, value); } }
+
+ public SelectionEvent onSelect { get { return m_OnSelect; } set { SetPropertyUtility.SetClass(ref m_OnSelect, value); } }
+
+ public SelectionEvent onDeselect { get { return m_OnDeselect; } set { SetPropertyUtility.SetClass(ref m_OnDeselect, value); } }
+
+ public TextSelectionEvent onTextSelection { get { return m_OnTextSelection; } set { SetPropertyUtility.SetClass(ref m_OnTextSelection, value); } }
+
+ public TextSelectionEvent onEndTextSelection { get { return m_OnEndTextSelection; } set { SetPropertyUtility.SetClass(ref m_OnEndTextSelection, value); } }
+
+ public OnChangeEvent onValueChanged { get { return m_OnValueChanged; } set { SetPropertyUtility.SetClass(ref m_OnValueChanged, value); } }
+
+ public TouchScreenKeyboardEvent onTouchScreenKeyboardStatusChanged { get { return m_OnTouchScreenKeyboardStatusChanged; } set { SetPropertyUtility.SetClass(ref m_OnTouchScreenKeyboardStatusChanged, value); } }
+
+ public OnValidateInput onValidateInput { get { return m_OnValidateInput; } set { SetPropertyUtility.SetClass(ref m_OnValidateInput, value); } }
+
+ public int characterLimit
+ {
+ get { return m_CharacterLimit; }
+ set
+ {
+ if (SetPropertyUtility.SetStruct(ref m_CharacterLimit, Math.Max(0, value)))
+ {
+ UpdateLabel();
+ if (m_SoftKeyboard != null)
+ m_SoftKeyboard.characterLimit = value;
+ }
+ }
+ }
+
+ //public bool isInteractableControl { set { if ( } }
+
+ /// <summary>
+ /// Set the point size on both Placeholder and Input text object.
+ /// </summary>
+ public float pointSize
+ {
+ get { return m_GlobalPointSize; }
+ set {
+ if (SetPropertyUtility.SetStruct(ref m_GlobalPointSize, Math.Max(0, value)))
+ {
+ SetGlobalPointSize(m_GlobalPointSize);
+ UpdateLabel();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Sets the Font Asset on both Placeholder and Input child objects.
+ /// </summary>
+ public TMP_FontAsset fontAsset
+ {
+ get { return m_GlobalFontAsset; }
+ set
+ {
+ if (SetPropertyUtility.SetClass(ref m_GlobalFontAsset, value))
+ {
+ SetGlobalFontAsset(m_GlobalFontAsset);
+ UpdateLabel();
+ }
+ }
+ }
+ [SerializeField]
+ protected TMP_FontAsset m_GlobalFontAsset;
+
+ /// <summary>
+ /// Determines if the whole text will be selected when focused.
+ /// </summary>
+ public bool onFocusSelectAll
+ {
+ get { return m_OnFocusSelectAll; }
+ set { m_OnFocusSelectAll = value; }
+ }
+ [SerializeField]
+ protected bool m_OnFocusSelectAll = true;
+ protected bool m_isSelectAll;
+
+ /// <summary>
+ /// Determines if the text and caret position as well as selection will be reset when the input field is deactivated.
+ /// </summary>
+ public bool resetOnDeActivation
+ {
+ get { return m_ResetOnDeActivation; }
+ set { m_ResetOnDeActivation = value; }
+ }
+ [SerializeField]
+ protected bool m_ResetOnDeActivation = true;
+ private bool m_SelectionStillActive = false;
+ private bool m_ReleaseSelection = false;
+
+ private GameObject m_SelectedObject;
+
+ /// <summary>
+ /// Controls whether the original text is restored when pressing "ESC".
+ /// </summary>
+ public bool restoreOriginalTextOnEscape
+ {
+ get { return m_RestoreOriginalTextOnEscape; }
+ set { m_RestoreOriginalTextOnEscape = value; }
+ }
+ [SerializeField]
+ private bool m_RestoreOriginalTextOnEscape = true;
+
+ /// <summary>
+ /// Is Rich Text editing allowed?
+ /// </summary>
+ public bool isRichTextEditingAllowed
+ {
+ get { return m_isRichTextEditingAllowed; }
+ set { m_isRichTextEditingAllowed = value; }
+ }
+ [SerializeField]
+ protected bool m_isRichTextEditingAllowed = false;
+
+
+ // Content Type related
+ public ContentType contentType { get { return m_ContentType; } set { if (SetPropertyUtility.SetStruct(ref m_ContentType, value)) EnforceContentType(); } }
+
+ public LineType lineType
+ {
+ get { return m_LineType; }
+ set
+ {
+ if (SetPropertyUtility.SetStruct(ref m_LineType, value))
+ {
+ SetToCustomIfContentTypeIsNot(ContentType.Standard, ContentType.Autocorrected);
+ SetTextComponentWrapMode();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Limits the number of lines of text in the Input Field.
+ /// </summary>
+ public int lineLimit
+ {
+ get { return m_LineLimit; }
+ set
+ {
+ if (m_LineType == LineType.SingleLine)
+ m_LineLimit = 1;
+ else
+ SetPropertyUtility.SetStruct(ref m_LineLimit, value);
+
+ }
+ }
+ [SerializeField]
+ protected int m_LineLimit = 0;
+
+ public InputType inputType { get { return m_InputType; } set { if (SetPropertyUtility.SetStruct(ref m_InputType, value)) SetToCustom(); } }
+
+ public TouchScreenKeyboardType keyboardType
+ {
+ get { return m_KeyboardType; }
+ set
+ {
+ if (SetPropertyUtility.SetStruct(ref m_KeyboardType, value))
+ SetToCustom();
+ }
+ }
+
+ public CharacterValidation characterValidation { get { return m_CharacterValidation; } set { if (SetPropertyUtility.SetStruct(ref m_CharacterValidation, value)) SetToCustom(); } }
+
+ /// <summary>
+ /// Sets the Input Validation to use a Custom Input Validation script.
+ /// </summary>
+ public TMP_InputValidator inputValidator
+ {
+ get { return m_InputValidator; }
+ set { if (SetPropertyUtility.SetClass(ref m_InputValidator, value)) SetToCustom(CharacterValidation.CustomValidator); }
+ }
+ [SerializeField]
+ protected TMP_InputValidator m_InputValidator = null;
+
+ public bool readOnly { get { return m_ReadOnly; } set { m_ReadOnly = value; } }
+
+ public bool richText { get { return m_RichText; } set { m_RichText = value; SetTextComponentRichTextMode(); } }
+
+ // Derived property
+ public bool multiLine { get { return m_LineType == LineType.MultiLineNewline || lineType == LineType.MultiLineSubmit; } }
+ // Not shown in Inspector.
+ public char asteriskChar { get { return m_AsteriskChar; } set { if (SetPropertyUtility.SetStruct(ref m_AsteriskChar, value)) UpdateLabel(); } }
+ public bool wasCanceled { get { return m_WasCanceled; } }
+
+
+ protected void ClampStringPos(ref int pos)
+ {
+ if (pos < 0)
+ pos = 0;
+ else if (pos > text.Length)
+ pos = text.Length;
+ }
+
+ protected void ClampCaretPos(ref int pos)
+ {
+ if (pos < 0)
+ pos = 0;
+ else if (pos > m_TextComponent.textInfo.characterCount - 1)
+ pos = m_TextComponent.textInfo.characterCount - 1;
+ }
+
+ /// <summary>
+ /// Current position of the cursor.
+ /// Getters are public Setters are protected
+ /// </summary>
+
+ protected int caretPositionInternal { get { return m_CaretPosition + compositionString.Length; } set { m_CaretPosition = value; ClampCaretPos(ref m_CaretPosition); } }
+ protected int stringPositionInternal { get { return m_StringPosition + compositionString.Length; } set { m_StringPosition = value; ClampStringPos(ref m_StringPosition); } }
+
+ protected int caretSelectPositionInternal { get { return m_CaretSelectPosition + compositionString.Length; } set { m_CaretSelectPosition = value; ClampCaretPos(ref m_CaretSelectPosition); } }
+ protected int stringSelectPositionInternal { get { return m_StringSelectPosition + compositionString.Length; } set { m_StringSelectPosition = value; ClampStringPos(ref m_StringSelectPosition); } }
+
+ private bool hasSelection { get { return stringPositionInternal != stringSelectPositionInternal; } }
+ private bool m_isSelected;
+ private bool m_IsStringPositionDirty;
+ private bool m_IsCaretPositionDirty;
+ private bool m_forceRectTransformAdjustment;
+
+ /// <summary>
+ /// Get: Returns the focus position as thats the position that moves around even during selection.
+ /// Set: Set both the anchor and focus position such that a selection doesn't happen
+ /// </summary>
+ public int caretPosition
+ {
+ get { return caretSelectPositionInternal; }
+ set { selectionAnchorPosition = value; selectionFocusPosition = value; m_IsStringPositionDirty = true; }
+ }
+
+ /// <summary>
+ /// Get: Returns the fixed position of selection
+ /// Set: If compositionString is 0 set the fixed position
+ /// </summary>
+ public int selectionAnchorPosition
+ {
+ get
+ {
+ return caretPositionInternal;
+ }
+
+ set
+ {
+ if (compositionString.Length != 0)
+ return;
+
+ caretPositionInternal = value;
+ m_IsStringPositionDirty = true;
+ }
+ }
+
+ /// <summary>
+ /// Get: Returns the variable position of selection
+ /// Set: If compositionString is 0 set the variable position
+ /// </summary>
+ public int selectionFocusPosition
+ {
+ get
+ {
+ return caretSelectPositionInternal;
+ }
+ set
+ {
+ if (compositionString.Length != 0)
+ return;
+
+ caretSelectPositionInternal = value;
+ m_IsStringPositionDirty = true;
+ }
+ }
+
+
+ /// <summary>
+ ///
+ /// </summary>
+ public int stringPosition
+ {
+ get { return stringSelectPositionInternal; }
+ set { selectionStringAnchorPosition = value; selectionStringFocusPosition = value; m_IsCaretPositionDirty = true; }
+ }
+
+
+ /// <summary>
+ /// The fixed position of the selection in the raw string which may contains rich text.
+ /// </summary>
+ public int selectionStringAnchorPosition
+ {
+ get
+ {
+ return stringPositionInternal;
+ }
+
+ set
+ {
+ if (compositionString.Length != 0)
+ return;
+
+ stringPositionInternal = value;
+ m_IsCaretPositionDirty = true;
+ }
+ }
+
+
+ /// <summary>
+ /// The variable position of the selection in the raw string which may contains rich text.
+ /// </summary>
+ public int selectionStringFocusPosition
+ {
+ get
+ {
+ return stringSelectPositionInternal;
+ }
+ set
+ {
+ if (compositionString.Length != 0)
+ return;
+
+ stringSelectPositionInternal = value;
+ m_IsCaretPositionDirty = true;
+ }
+ }
+
+
+ #if UNITY_EDITOR
+ // Remember: This is NOT related to text validation!
+ // This is Unity's own OnValidate method which is invoked when changing values in the Inspector.
+ protected override void OnValidate()
+ {
+ base.OnValidate();
+ EnforceContentType();
+
+ m_CharacterLimit = Math.Max(0, m_CharacterLimit);
+
+ //This can be invoked before OnEnabled is called. So we shouldn't be accessing other objects, before OnEnable is called.
+ if (!IsActive())
+ return;
+
+ SetTextComponentRichTextMode();
+
+ UpdateLabel();
+ if (m_AllowInput)
+ SetCaretActive();
+ }
+ #endif
+
+ protected override void OnEnable()
+ {
+ //Debug.Log("*** OnEnable() *** - " + this.name);
+
+ base.OnEnable();
+
+ if (m_Text == null)
+ m_Text = string.Empty;
+
+ if (Application.isPlaying)
+ {
+ if (m_CachedInputRenderer == null && m_TextComponent != null)
+ {
+ // Check if Input Field is driven by any layout components
+ m_IsDrivenByLayoutComponents = GetComponent<ILayoutController>() != null ? true : false;
+
+ GameObject go = new GameObject(transform.name + " Input Caret", typeof(RectTransform));
+
+ // Add MaskableGraphic Component
+ TMP_SelectionCaret caret = go.AddComponent<TMP_SelectionCaret>();
+ caret.raycastTarget = false;
+ caret.color = Color.clear;
+
+ go.hideFlags = HideFlags.DontSave;
+ go.transform.SetParent(m_TextComponent.transform.parent);
+ go.transform.SetAsFirstSibling();
+ go.layer = gameObject.layer;
+
+ caretRectTrans = go.GetComponent<RectTransform>();
+ m_CachedInputRenderer = go.GetComponent<CanvasRenderer>();
+ m_CachedInputRenderer.SetMaterial(Graphic.defaultGraphicMaterial, Texture2D.whiteTexture);
+
+ // Needed as if any layout is present we want the caret to always be the same as the text area.
+ go.AddComponent<LayoutElement>().ignoreLayout = true;
+
+ AssignPositioningIfNeeded();
+ }
+ }
+
+ // If we have a cached renderer then we had OnDisable called so just restore the material.
+ if (m_CachedInputRenderer != null)
+ m_CachedInputRenderer.SetMaterial(Graphic.defaultGraphicMaterial, Texture2D.whiteTexture);
+
+ if (m_TextComponent != null)
+ {
+ m_TextComponent.RegisterDirtyVerticesCallback(MarkGeometryAsDirty);
+ m_TextComponent.RegisterDirtyVerticesCallback(UpdateLabel);
+ //m_TextComponent.ignoreRectMaskCulling = multiLine;
+
+ //m_DefaultTransformPosition = m_TextComponent.rectTransform.localPosition;
+
+ // Cache reference to Vertical Scrollbar RectTransform and add listener.
+ if (m_VerticalScrollbar != null)
+ {
+ m_TextComponent.ignoreRectMaskCulling = true;
+ m_VerticalScrollbar.onValueChanged.AddListener(OnScrollbarValueChange);
+ }
+
+ UpdateLabel();
+ }
+
+ // Subscribe to event fired when text object has been regenerated.
+ TMPro_EventManager.TEXT_CHANGED_EVENT.Add(ON_TEXT_CHANGED);
+ }
+
+ protected override void OnDisable()
+ {
+ // the coroutine will be terminated, so this will ensure it restarts when we are next activated
+ m_BlinkCoroutine = null;
+
+ DeactivateInputField();
+ if (m_TextComponent != null)
+ {
+ m_TextComponent.UnregisterDirtyVerticesCallback(MarkGeometryAsDirty);
+ m_TextComponent.UnregisterDirtyVerticesCallback(UpdateLabel);
+
+ if (m_VerticalScrollbar != null)
+ m_VerticalScrollbar.onValueChanged.RemoveListener(OnScrollbarValueChange);
+
+ }
+ CanvasUpdateRegistry.UnRegisterCanvasElementForRebuild(this);
+
+ // Clear needs to be called otherwise sync never happens as the object is disabled.
+ if (m_CachedInputRenderer != null)
+ m_CachedInputRenderer.Clear();
+
+ if (m_Mesh != null)
+ DestroyImmediate(m_Mesh);
+ m_Mesh = null;
+
+ // Unsubscribe to event triggered when text object has been regenerated
+ TMPro_EventManager.TEXT_CHANGED_EVENT.Remove(ON_TEXT_CHANGED);
+
+ base.OnDisable();
+ }
+
+
+ /// <summary>
+ /// Method used to update the tracking of the caret position when the text object has been regenerated.
+ /// </summary>
+ /// <param name="obj"></param>
+ private void ON_TEXT_CHANGED(UnityEngine.Object obj)
+ {
+ if (obj == m_TextComponent && Application.isPlaying && compositionString.Length == 0)
+ {
+ caretPositionInternal = GetCaretPositionFromStringIndex(stringPositionInternal);
+ caretSelectPositionInternal = GetCaretPositionFromStringIndex(stringSelectPositionInternal);
+
+ #if TMP_DEBUG_MODE
+ Debug.Log("Caret Position: " + caretPositionInternal + " Selection Position: " + caretSelectPositionInternal + " String Position: " + stringPositionInternal + " String Select Position: " + stringSelectPositionInternal);
+ #endif
+ }
+ }
+
+
+ IEnumerator CaretBlink()
+ {
+ // Always ensure caret is initially visible since it can otherwise be confusing for a moment.
+ m_CaretVisible = true;
+ yield return null;
+
+ while ((isFocused || m_SelectionStillActive) && m_CaretBlinkRate > 0)
+ {
+ // the blink rate is expressed as a frequency
+ float blinkPeriod = 1f / m_CaretBlinkRate;
+
+ // the caret should be ON if we are in the first half of the blink period
+ bool blinkState = (Time.unscaledTime - m_BlinkStartTime) % blinkPeriod < blinkPeriod / 2;
+ if (m_CaretVisible != blinkState)
+ {
+ m_CaretVisible = blinkState;
+ if (!hasSelection)
+ MarkGeometryAsDirty();
+ }
+
+ // Then wait again.
+ yield return null;
+ }
+ m_BlinkCoroutine = null;
+ }
+
+ void SetCaretVisible()
+ {
+ if (!m_AllowInput)
+ return;
+
+ m_CaretVisible = true;
+ m_BlinkStartTime = Time.unscaledTime;
+ SetCaretActive();
+ }
+
+ // SetCaretActive will not set the caret immediately visible - it will wait for the next time to blink.
+ // However, it will handle things correctly if the blink speed changed from zero to non-zero or non-zero to zero.
+ void SetCaretActive()
+ {
+ if (!m_AllowInput)
+ return;
+
+ if (m_CaretBlinkRate > 0.0f)
+ {
+ if (m_BlinkCoroutine == null)
+ m_BlinkCoroutine = StartCoroutine(CaretBlink());
+ }
+ else
+ {
+ m_CaretVisible = true;
+ }
+ }
+
+ protected void OnFocus()
+ {
+ if (m_OnFocusSelectAll)
+ SelectAll();
+ }
+
+ protected void SelectAll()
+ {
+ m_isSelectAll = true;
+ stringPositionInternal = text.Length;
+ stringSelectPositionInternal = 0;
+ }
+
+ /// <summary>
+ /// Move to the end of the text.
+ /// </summary>
+ /// <param name="shift"></param>
+ public void MoveTextEnd(bool shift)
+ {
+ if (m_isRichTextEditingAllowed)
+ {
+ int position = text.Length;
+
+ if (shift)
+ {
+ stringSelectPositionInternal = position;
+ }
+ else
+ {
+ stringPositionInternal = position;
+ stringSelectPositionInternal = stringPositionInternal;
+ }
+ }
+ else
+ {
+ int position = m_TextComponent.textInfo.characterCount - 1;
+
+ if (shift)
+ {
+ caretSelectPositionInternal = position;
+ stringSelectPositionInternal = GetStringIndexFromCaretPosition(position);
+ }
+ else
+ {
+ caretPositionInternal = caretSelectPositionInternal = position;
+ stringSelectPositionInternal = stringPositionInternal = GetStringIndexFromCaretPosition(position);
+ }
+ }
+
+ UpdateLabel();
+ }
+
+ /// <summary>
+ /// Move to the start of the text.
+ /// </summary>
+ /// <param name="shift"></param>
+ public void MoveTextStart(bool shift)
+ {
+ if (m_isRichTextEditingAllowed)
+ {
+ int position = 0;
+
+ if (shift)
+ {
+ stringSelectPositionInternal = position;
+ }
+ else
+ {
+ stringPositionInternal = position;
+ stringSelectPositionInternal = stringPositionInternal;
+ }
+ }
+ else
+ {
+ int position = 0;
+
+ if (shift)
+ {
+ caretSelectPositionInternal = position;
+ stringSelectPositionInternal = GetStringIndexFromCaretPosition(position);
+ }
+ else
+ {
+ caretPositionInternal = caretSelectPositionInternal = position;
+ stringSelectPositionInternal = stringPositionInternal = GetStringIndexFromCaretPosition(position);
+ }
+ }
+
+ UpdateLabel();
+ }
+
+
+ /// <summary>
+ /// Move to the end of the current line of text.
+ /// </summary>
+ /// <param name="shift"></param>
+ public void MoveToEndOfLine(bool shift, bool ctrl)
+ {
+ // Get the line the caret is currently located on.
+ int currentLine = m_TextComponent.textInfo.characterInfo[caretPositionInternal].lineNumber;
+
+ // Get the last character of the given line.
+ int characterIndex = ctrl == true ? m_TextComponent.textInfo.characterCount - 1 : m_TextComponent.textInfo.lineInfo[currentLine].lastCharacterIndex;
+
+ int position = m_TextComponent.textInfo.characterInfo[characterIndex].index;
+
+ if (shift)
+ {
+ stringSelectPositionInternal = position;
+
+ caretSelectPositionInternal = characterIndex;
+ }
+ else
+ {
+ stringPositionInternal = position;
+ stringSelectPositionInternal = stringPositionInternal;
+
+ caretSelectPositionInternal = caretPositionInternal = characterIndex;
+ }
+
+ UpdateLabel();
+ }
+
+ /// <summary>
+ /// Move to the start of the current line of text.
+ /// </summary>
+ /// <param name="shift"></param>
+ public void MoveToStartOfLine(bool shift, bool ctrl)
+ {
+ // Get the line the caret is currently located on.
+ int currentLine = m_TextComponent.textInfo.characterInfo[caretPositionInternal].lineNumber;
+
+ // Get the first character of the given line.
+ int characterIndex = ctrl == true ? 0 : m_TextComponent.textInfo.lineInfo[currentLine].firstCharacterIndex;
+
+ int position = 0;
+ if (characterIndex > 0)
+ position = m_TextComponent.textInfo.characterInfo[characterIndex - 1].index + m_TextComponent.textInfo.characterInfo[characterIndex - 1].stringLength;
+
+ if (shift)
+ {
+ stringSelectPositionInternal = position;
+
+ caretSelectPositionInternal = characterIndex;
+ }
+ else
+ {
+ stringPositionInternal = position;
+ stringSelectPositionInternal = stringPositionInternal;
+
+ caretSelectPositionInternal = caretPositionInternal = characterIndex;
+ }
+
+ UpdateLabel();
+ }
+
+
+ static string clipboard
+ {
+ get
+ {
+ return GUIUtility.systemCopyBuffer;
+ }
+ set
+ {
+ GUIUtility.systemCopyBuffer = value;
+ }
+ }
+
+ private bool InPlaceEditing()
+ {
+ if (m_TouchKeyboardAllowsInPlaceEditing || (TouchScreenKeyboard.isSupported && (Application.platform == RuntimePlatform.WSAPlayerX86 || Application.platform == RuntimePlatform.WSAPlayerX64 || Application.platform == RuntimePlatform.WSAPlayerARM)))
+ return true;
+
+ if (TouchScreenKeyboard.isSupported && shouldHideSoftKeyboard)
+ return true;
+
+ if (TouchScreenKeyboard.isSupported && shouldHideSoftKeyboard == false && shouldHideMobileInput == false)
+ return false;
+
+ return true;
+ }
+
+ void UpdateStringPositionFromKeyboard()
+ {
+ // TODO: Might want to add null check here.
+ var selectionRange = m_SoftKeyboard.selection;
+
+ if (selectionRange.start == 0 && selectionRange.length == 0)
+ return;
+
+ var selectionStart = selectionRange.start;
+ var selectionEnd = selectionRange.end;
+
+ var stringPositionChanged = false;
+
+ if (stringPositionInternal != selectionStart)
+ {
+ stringPositionChanged = true;
+ stringPositionInternal = selectionStart;
+
+ caretPositionInternal = GetCaretPositionFromStringIndex(stringPositionInternal);
+ }
+
+ if (stringSelectPositionInternal != selectionEnd)
+ {
+ stringSelectPositionInternal = selectionEnd;
+ stringPositionChanged = true;
+
+ caretSelectPositionInternal = GetCaretPositionFromStringIndex(stringSelectPositionInternal);
+ }
+
+ if (stringPositionChanged)
+ {
+ m_BlinkStartTime = Time.unscaledTime;
+
+ UpdateLabel();
+ }
+ }
+
+ /// <summary>
+ /// Update the text based on input.
+ /// </summary>
+ // TODO: Make LateUpdate a coroutine instead. Allows us to control the update to only be when the field is active.
+ protected virtual void LateUpdate()
+ {
+ // Only activate if we are not already activated.
+ if (m_ShouldActivateNextUpdate)
+ {
+ if (!isFocused)
+ {
+ ActivateInputFieldInternal();
+ m_ShouldActivateNextUpdate = false;
+ return;
+ }
+
+ // Reset as we are already activated.
+ m_ShouldActivateNextUpdate = false;
+ }
+
+ // Update Scrollbar if needed
+ if (m_IsScrollbarUpdateRequired)
+ {
+ UpdateScrollbar();
+ m_IsScrollbarUpdateRequired = false;
+ }
+
+ // Handle double click to reset / deselect Input Field when ResetOnActivation is false.
+ if (!isFocused && m_SelectionStillActive)
+ {
+ GameObject selectedObject = EventSystem.current != null ? EventSystem.current.currentSelectedGameObject : null;
+
+ if (selectedObject != null && selectedObject != this.gameObject)
+ {
+ if (selectedObject != m_SelectedObject)
+ {
+ m_SelectedObject = selectedObject;
+
+ // Check if object has a TMP Input Field
+ if (selectedObject.GetComponent<TMP_InputField>() != null)
+ {
+ // Release selection
+ m_SelectionStillActive = false;
+ MarkGeometryAsDirty();
+ m_SelectedObject = null;
+ }
+ }
+
+ return;
+ }
+
+ if (Input.GetKeyDown(KeyCode.Mouse0))
+ {
+ // Check for Double Click
+ bool isDoubleClick = false;
+ float timeStamp = Time.unscaledTime;
+
+ if (m_KeyDownStartTime + m_DoubleClickDelay > timeStamp)
+ isDoubleClick = true;
+
+ m_KeyDownStartTime = timeStamp;
+
+ if (isDoubleClick)
+ {
+ //m_StringPosition = m_StringSelectPosition = 0;
+ //m_CaretPosition = m_CaretSelectPosition = 0;
+ //m_TextComponent.rectTransform.localPosition = m_DefaultTransformPosition;
+
+ //if (caretRectTrans != null)
+ // caretRectTrans.localPosition = Vector3.zero;
+
+ m_SelectionStillActive = false;
+
+ MarkGeometryAsDirty();
+
+ return;
+ }
+ }
+ }
+
+ if (InPlaceEditing() && isKeyboardUsingEvents() || !isFocused)
+ {
+ return;
+ }
+
+ AssignPositioningIfNeeded();
+
+ if (m_SoftKeyboard == null || m_SoftKeyboard.status != TouchScreenKeyboard.Status.Visible)
+ {
+ if (m_SoftKeyboard != null)
+ {
+ if (!m_ReadOnly)
+ text = m_SoftKeyboard.text;
+
+ if (m_SoftKeyboard.status == TouchScreenKeyboard.Status.LostFocus)
+ SendTouchScreenKeyboardStatusChanged();
+
+ if (m_SoftKeyboard.status == TouchScreenKeyboard.Status.Canceled)
+ {
+ m_ReleaseSelection = true;
+ m_WasCanceled = true;
+ SendTouchScreenKeyboardStatusChanged();
+ }
+
+ if (m_SoftKeyboard.status == TouchScreenKeyboard.Status.Done)
+ {
+ m_ReleaseSelection = true;
+ OnSubmit(null);
+ SendTouchScreenKeyboardStatusChanged();
+ }
+ }
+
+ OnDeselect(null);
+ return;
+ }
+
+ string val = m_SoftKeyboard.text;
+
+ if (m_Text != val)
+ {
+ if (m_ReadOnly)
+ {
+ m_SoftKeyboard.text = m_Text;
+ }
+ else
+ {
+ m_Text = "";
+
+ for (int i = 0; i < val.Length; ++i)
+ {
+ char c = val[i];
+
+ if (c == '\r' || (int)c == 3)
+ c = '\n';
+
+ if (onValidateInput != null)
+ c = onValidateInput(m_Text, m_Text.Length, c);
+ else if (characterValidation != CharacterValidation.None)
+ c = Validate(m_Text, m_Text.Length, c);
+
+ if (lineType == LineType.MultiLineSubmit && c == '\n')
+ {
+ m_SoftKeyboard.text = m_Text;
+
+ OnSubmit(null);
+ OnDeselect(null);
+ return;
+ }
+
+ if (c != 0)
+ m_Text += c;
+ }
+
+ if (characterLimit > 0 && m_Text.Length > characterLimit)
+ m_Text = m_Text.Substring(0, characterLimit);
+
+ UpdateStringPositionFromKeyboard();
+
+ // Set keyboard text before updating label, as we might have changed it with validation
+ // and update label will take the old value from keyboard if we don't change it here
+ if (m_Text != val)
+ m_SoftKeyboard.text = m_Text;
+
+ SendOnValueChangedAndUpdateLabel();
+ }
+ }
+ else if (m_HideMobileInput && Application.platform == RuntimePlatform.Android)
+ {
+ UpdateStringPositionFromKeyboard();
+ }
+
+ //else if (m_HideMobileInput) // m_Keyboard.canSetSelection
+ //{
+ // int length = stringPositionInternal < stringSelectPositionInternal ? stringSelectPositionInternal - stringPositionInternal : stringPositionInternal - stringSelectPositionInternal;
+ // m_SoftKeyboard.selection = new RangeInt(stringPositionInternal < stringSelectPositionInternal ? stringPositionInternal : stringSelectPositionInternal, length);
+ //}
+ //else if (!m_HideMobileInput) // m_Keyboard.canGetSelection)
+ //{
+ // UpdateStringPositionFromKeyboard();
+ //}
+
+ if (m_SoftKeyboard.status != TouchScreenKeyboard.Status.Visible)
+ {
+ if (m_SoftKeyboard.status == TouchScreenKeyboard.Status.Canceled)
+ m_WasCanceled = true;
+
+ OnDeselect(null);
+ }
+ }
+
+
+ private bool MayDrag(PointerEventData eventData)
+ {
+ return IsActive() &&
+ IsInteractable() &&
+ eventData.button == PointerEventData.InputButton.Left &&
+ m_TextComponent != null &&
+ (m_SoftKeyboard == null || shouldHideSoftKeyboard || shouldHideMobileInput);
+ }
+
+ public virtual void OnBeginDrag(PointerEventData eventData)
+ {
+ if (!MayDrag(eventData))
+ return;
+
+ m_UpdateDrag = true;
+ }
+
+ public virtual void OnDrag(PointerEventData eventData)
+ {
+ if (!MayDrag(eventData))
+ return;
+
+ int insertionIndex = TMP_TextUtilities.GetCursorIndexFromPosition(m_TextComponent, eventData.position, eventData.pressEventCamera, out CaretPosition insertionSide);
+
+ if (m_isRichTextEditingAllowed)
+ {
+ if (insertionSide == CaretPosition.Left)
+ {
+ stringSelectPositionInternal = m_TextComponent.textInfo.characterInfo[insertionIndex].index;
+ }
+ else if (insertionSide == CaretPosition.Right)
+ {
+ stringSelectPositionInternal = m_TextComponent.textInfo.characterInfo[insertionIndex].index + m_TextComponent.textInfo.characterInfo[insertionIndex].stringLength;
+ }
+ }
+ else
+ {
+ if (insertionSide == CaretPosition.Left)
+ {
+ stringSelectPositionInternal = insertionIndex == 0
+ ? m_TextComponent.textInfo.characterInfo[0].index
+ : m_TextComponent.textInfo.characterInfo[insertionIndex - 1].index + m_TextComponent.textInfo.characterInfo[insertionIndex - 1].stringLength;
+ }
+ else if (insertionSide == CaretPosition.Right)
+ {
+ stringSelectPositionInternal = m_TextComponent.textInfo.characterInfo[insertionIndex].index + m_TextComponent.textInfo.characterInfo[insertionIndex].stringLength;
+ }
+ }
+
+ caretSelectPositionInternal = GetCaretPositionFromStringIndex(stringSelectPositionInternal);
+
+ MarkGeometryAsDirty();
+
+ m_DragPositionOutOfBounds = !RectTransformUtility.RectangleContainsScreenPoint(textViewport, eventData.position, eventData.pressEventCamera);
+ if (m_DragPositionOutOfBounds && m_DragCoroutine == null)
+ m_DragCoroutine = StartCoroutine(MouseDragOutsideRect(eventData));
+
+ eventData.Use();
+
+ #if TMP_DEBUG_MODE
+ Debug.Log("Caret Position: " + caretPositionInternal + " Selection Position: " + caretSelectPositionInternal + " String Position: " + stringPositionInternal + " String Select Position: " + stringSelectPositionInternal);
+ #endif
+ }
+
+ IEnumerator MouseDragOutsideRect(PointerEventData eventData)
+ {
+ while (m_UpdateDrag && m_DragPositionOutOfBounds)
+ {
+ RectTransformUtility.ScreenPointToLocalPointInRectangle(textViewport, eventData.position, eventData.pressEventCamera, out Vector2 localMousePos);
+
+ Rect rect = textViewport.rect;
+
+ if (multiLine)
+ {
+ if (localMousePos.y > rect.yMax)
+ MoveUp(true, true);
+ else if (localMousePos.y < rect.yMin)
+ MoveDown(true, true);
+ }
+ else
+ {
+ if (localMousePos.x < rect.xMin)
+ MoveLeft(true, false);
+ else if (localMousePos.x > rect.xMax)
+ MoveRight(true, false);
+ }
+
+ UpdateLabel();
+
+ float delay = multiLine ? kVScrollSpeed : kHScrollSpeed;
+
+ if (m_WaitForSecondsRealtime == null)
+ m_WaitForSecondsRealtime = new WaitForSecondsRealtime(delay);
+ else
+ m_WaitForSecondsRealtime.waitTime = delay;
+
+ yield return m_WaitForSecondsRealtime;
+ }
+ m_DragCoroutine = null;
+ }
+
+ public virtual void OnEndDrag(PointerEventData eventData)
+ {
+ if (!MayDrag(eventData))
+ return;
+
+ m_UpdateDrag = false;
+ }
+
+ public override void OnPointerDown(PointerEventData eventData)
+ {
+ if (!MayDrag(eventData))
+ return;
+
+ EventSystem.current.SetSelectedGameObject(gameObject, eventData);
+
+ bool hadFocusBefore = m_AllowInput;
+ base.OnPointerDown(eventData);
+
+ if (InPlaceEditing() == false)
+ {
+ if (m_SoftKeyboard == null || !m_SoftKeyboard.active)
+ {
+ OnSelect(eventData);
+ return;
+ }
+ }
+
+ bool shift = Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift);
+
+ // Check for Double Click
+ bool isDoubleClick = false;
+ float timeStamp = Time.unscaledTime;
+
+ if (m_PointerDownClickStartTime + m_DoubleClickDelay > timeStamp)
+ isDoubleClick = true;
+
+ m_PointerDownClickStartTime = timeStamp;
+
+ // Only set caret position if we didn't just get focus now.
+ // Otherwise it will overwrite the select all on focus.
+ if (hadFocusBefore || !m_OnFocusSelectAll)
+ {
+ int insertionIndex = TMP_TextUtilities.GetCursorIndexFromPosition(m_TextComponent, eventData.position, eventData.pressEventCamera, out CaretPosition insertionSide);
+
+ if (shift)
+ {
+ if (m_isRichTextEditingAllowed)
+ {
+ if (insertionSide == CaretPosition.Left)
+ {
+ stringSelectPositionInternal = m_TextComponent.textInfo.characterInfo[insertionIndex].index;
+ }
+ else if (insertionSide == CaretPosition.Right)
+ {
+ stringSelectPositionInternal = m_TextComponent.textInfo.characterInfo[insertionIndex].index + m_TextComponent.textInfo.characterInfo[insertionIndex].stringLength;
+ }
+ }
+ else
+ {
+ if (insertionSide == CaretPosition.Left)
+ {
+ stringSelectPositionInternal = insertionIndex == 0
+ ? m_TextComponent.textInfo.characterInfo[0].index
+ : m_TextComponent.textInfo.characterInfo[insertionIndex - 1].index + m_TextComponent.textInfo.characterInfo[insertionIndex - 1].stringLength;
+ }
+ else if (insertionSide == CaretPosition.Right)
+ {
+ stringSelectPositionInternal = m_TextComponent.textInfo.characterInfo[insertionIndex].index + m_TextComponent.textInfo.characterInfo[insertionIndex].stringLength;
+ }
+ }
+ }
+ else
+ {
+ if (m_isRichTextEditingAllowed)
+ {
+ if (insertionSide == CaretPosition.Left)
+ {
+ stringPositionInternal = stringSelectPositionInternal = m_TextComponent.textInfo.characterInfo[insertionIndex].index;
+ }
+ else if (insertionSide == CaretPosition.Right)
+ {
+ stringPositionInternal = stringSelectPositionInternal = m_TextComponent.textInfo.characterInfo[insertionIndex].index + m_TextComponent.textInfo.characterInfo[insertionIndex].stringLength;
+ }
+ }
+ else
+ {
+ if (insertionSide == CaretPosition.Left)
+ {
+ stringPositionInternal = stringSelectPositionInternal = insertionIndex == 0
+ ? m_TextComponent.textInfo.characterInfo[0].index
+ : m_TextComponent.textInfo.characterInfo[insertionIndex - 1].index + m_TextComponent.textInfo.characterInfo[insertionIndex - 1].stringLength;
+ }
+ else if (insertionSide == CaretPosition.Right)
+ {
+ stringPositionInternal = stringSelectPositionInternal = m_TextComponent.textInfo.characterInfo[insertionIndex].index + m_TextComponent.textInfo.characterInfo[insertionIndex].stringLength;
+ }
+ }
+ }
+
+
+ if (isDoubleClick)
+ {
+ int wordIndex = TMP_TextUtilities.FindIntersectingWord(m_TextComponent, eventData.position, eventData.pressEventCamera);
+
+ if (wordIndex != -1)
+ {
+ // TODO: Should behavior be different if rich text editing is enabled or not?
+
+ // Select current word
+ caretPositionInternal = m_TextComponent.textInfo.wordInfo[wordIndex].firstCharacterIndex;
+ caretSelectPositionInternal = m_TextComponent.textInfo.wordInfo[wordIndex].lastCharacterIndex + 1;
+
+ stringPositionInternal = m_TextComponent.textInfo.characterInfo[caretPositionInternal].index;
+ stringSelectPositionInternal = m_TextComponent.textInfo.characterInfo[caretSelectPositionInternal - 1].index + m_TextComponent.textInfo.characterInfo[caretSelectPositionInternal - 1].stringLength;
+ }
+ else
+ {
+ // Select current character
+ caretPositionInternal = insertionIndex;
+ caretSelectPositionInternal = caretPositionInternal + 1;
+
+ stringPositionInternal = m_TextComponent.textInfo.characterInfo[insertionIndex].index;
+ stringSelectPositionInternal = stringPositionInternal + m_TextComponent.textInfo.characterInfo[insertionIndex].stringLength;
+ }
+ }
+ else
+ {
+ caretPositionInternal = caretSelectPositionInternal = GetCaretPositionFromStringIndex(stringPositionInternal);
+ }
+ }
+
+ UpdateLabel();
+ eventData.Use();
+
+ #if TMP_DEBUG_MODE
+ Debug.Log("Caret Position: " + caretPositionInternal + " Selection Position: " + caretSelectPositionInternal + " String Position: " + stringPositionInternal + " String Select Position: " + stringSelectPositionInternal);
+ #endif
+ }
+
+ protected enum EditState
+ {
+ Continue,
+ Finish
+ }
+
+ protected EditState KeyPressed(Event evt)
+ {
+ var currentEventModifiers = evt.modifiers;
+ bool ctrl = SystemInfo.operatingSystemFamily == OperatingSystemFamily.MacOSX ? (currentEventModifiers & EventModifiers.Command) != 0 : (currentEventModifiers & EventModifiers.Control) != 0;
+ bool shift = (currentEventModifiers & EventModifiers.Shift) != 0;
+ bool alt = (currentEventModifiers & EventModifiers.Alt) != 0;
+ bool ctrlOnly = ctrl && !alt && !shift;
+
+ switch (evt.keyCode)
+ {
+ case KeyCode.Backspace:
+ {
+ Backspace();
+ return EditState.Continue;
+ }
+
+ case KeyCode.Delete:
+ {
+ DeleteKey();
+ return EditState.Continue;
+ }
+
+ case KeyCode.Home:
+ {
+ MoveToStartOfLine(shift, ctrl);
+ return EditState.Continue;
+ }
+
+ case KeyCode.End:
+ {
+ MoveToEndOfLine(shift, ctrl);
+ return EditState.Continue;
+ }
+
+ // Select All
+ case KeyCode.A:
+ {
+ if (ctrlOnly)
+ {
+ SelectAll();
+ return EditState.Continue;
+ }
+ break;
+ }
+
+ // Copy
+ case KeyCode.C:
+ {
+ if (ctrlOnly)
+ {
+ if (inputType != InputType.Password)
+ clipboard = GetSelectedString();
+ else
+ clipboard = "";
+ return EditState.Continue;
+ }
+ break;
+ }
+
+ // Paste
+ case KeyCode.V:
+ {
+ if (ctrlOnly)
+ {
+ Append(clipboard);
+ return EditState.Continue;
+ }
+ break;
+ }
+
+ // Cut
+ case KeyCode.X:
+ {
+ if (ctrlOnly)
+ {
+ if (inputType != InputType.Password)
+ clipboard = GetSelectedString();
+ else
+ clipboard = "";
+ Delete();
+ UpdateTouchKeyboardFromEditChanges();
+ SendOnValueChangedAndUpdateLabel();
+ return EditState.Continue;
+ }
+ break;
+ }
+
+ case KeyCode.LeftArrow:
+ {
+ MoveLeft(shift, ctrl);
+ return EditState.Continue;
+ }
+
+ case KeyCode.RightArrow:
+ {
+ MoveRight(shift, ctrl);
+ return EditState.Continue;
+ }
+
+ case KeyCode.UpArrow:
+ {
+ MoveUp(shift);
+ return EditState.Continue;
+ }
+
+ case KeyCode.DownArrow:
+ {
+ MoveDown(shift);
+ return EditState.Continue;
+ }
+
+ case KeyCode.PageUp:
+ {
+ MovePageUp(shift);
+ return EditState.Continue;
+ }
+
+ case KeyCode.PageDown:
+ {
+ MovePageDown(shift);
+ return EditState.Continue;
+ }
+
+ // Submit
+ case KeyCode.Return:
+ case KeyCode.KeypadEnter:
+ {
+ if (lineType != LineType.MultiLineNewline)
+ {
+ m_ReleaseSelection = true;
+ return EditState.Finish;
+ }
+ break;
+ }
+
+ case KeyCode.Escape:
+ {
+ m_ReleaseSelection = true;
+ m_WasCanceled = true;
+ return EditState.Finish;
+ }
+ }
+
+ char c = evt.character;
+
+ // Don't allow return chars or tabulator key to be entered into single line fields.
+ if (!multiLine && (c == '\t' || c == '\r' || c == 10))
+ return EditState.Continue;
+
+ // Convert carriage return and end-of-text characters to newline.
+ if (c == '\r' || (int)c == 3)
+ c = '\n';
+
+ if (IsValidChar(c))
+ {
+ Append(c);
+ }
+
+ if (c == 0)
+ {
+ if (compositionString.Length > 0)
+ {
+ UpdateLabel();
+ }
+ }
+ return EditState.Continue;
+ }
+
+ protected virtual bool IsValidChar(char c)
+ {
+ // Delete key on mac
+ if ((int)c == 127)
+ return false;
+ // Accept newline and tab
+ if (c == '\t' || c == '\n')
+ return true;
+
+ return true;
+
+ // With the addition of Dynamic support, I think this will best be handled by the text component.
+ //return m_TextComponent.font.HasCharacter(c, true);
+ }
+
+ /// <summary>
+ /// Handle the specified event.
+ /// </summary>
+ private Event m_ProcessingEvent = new Event();
+
+ public void ProcessEvent(Event e)
+ {
+ KeyPressed(e);
+ }
+
+
+ /// <summary>
+ ///
+ /// </summary>
+ /// <param name="eventData"></param>
+ public virtual void OnUpdateSelected(BaseEventData eventData)
+ {
+ if (!isFocused)
+ return;
+
+ bool consumedEvent = false;
+ while (Event.PopEvent(m_ProcessingEvent))
+ {
+ if (m_ProcessingEvent.rawType == EventType.KeyDown)
+ {
+ //Debug.Log("Event: " + m_ProcessingEvent.ToString());
+
+ consumedEvent = true;
+ var shouldContinue = KeyPressed(m_ProcessingEvent);
+ if (shouldContinue == EditState.Finish)
+ {
+ SendOnSubmit();
+ DeactivateInputField();
+ break;
+ }
+ }
+
+ switch (m_ProcessingEvent.type)
+ {
+ case EventType.ValidateCommand:
+ case EventType.ExecuteCommand:
+ switch (m_ProcessingEvent.commandName)
+ {
+ case "SelectAll":
+ SelectAll();
+ consumedEvent = true;
+ break;
+ }
+ break;
+ }
+ }
+
+ if (consumedEvent)
+ UpdateLabel();
+
+ eventData.Use();
+ }
+
+
+ /// <summary>
+ ///
+ /// </summary>
+ /// <param name="eventData"></param>
+ public virtual void OnScroll(PointerEventData eventData)
+ {
+ if (m_TextComponent.preferredHeight < m_TextViewport.rect.height) return;
+
+ float scrollDirection = -eventData.scrollDelta.y;
+
+ m_ScrollPosition = m_ScrollPosition + (1f / m_TextComponent.textInfo.lineCount) * scrollDirection * m_ScrollSensitivity;
+
+ m_ScrollPosition = Mathf.Clamp01(m_ScrollPosition);
+
+ AdjustTextPositionRelativeToViewport(m_ScrollPosition);
+
+ // Disable focus until user re-selected the input field.
+ m_AllowInput = false;
+
+ if (m_VerticalScrollbar)
+ {
+ m_IsUpdatingScrollbarValues = true;
+ m_VerticalScrollbar.value = m_ScrollPosition;
+ }
+
+ //Debug.Log("Scroll Position:" + m_ScrollPosition);
+ }
+
+
+ private string GetSelectedString()
+ {
+ if (!hasSelection)
+ return "";
+
+ int startPos = stringPositionInternal;
+ int endPos = stringSelectPositionInternal;
+
+ // Ensure pos is always less then selPos to make the code simpler
+ if (startPos > endPos)
+ {
+ int temp = startPos;
+ startPos = endPos;
+ endPos = temp;
+ }
+
+ //for (int i = m_CaretPosition; i < m_CaretSelectPosition; i++)
+ //{
+ // Debug.Log("Character [" + m_TextComponent.textInfo.characterInfo[i].character + "] using Style [" + m_TextComponent.textInfo.characterInfo[i].style + "] has been selected.");
+ //}
+
+
+ return text.Substring(startPos, endPos - startPos);
+ }
+
+ private int FindNextWordBegin()
+ {
+ if (stringSelectPositionInternal + 1 >= text.Length)
+ return text.Length;
+
+ int spaceLoc = text.IndexOfAny(kSeparators, stringSelectPositionInternal + 1);
+
+ if (spaceLoc == -1)
+ spaceLoc = text.Length;
+ else
+ spaceLoc++;
+
+ return spaceLoc;
+ }
+
+ private void MoveRight(bool shift, bool ctrl)
+ {
+ if (hasSelection && !shift)
+ {
+ // By convention, if we have a selection and move right without holding shift,
+ // we just place the cursor at the end.
+ stringPositionInternal = stringSelectPositionInternal = Mathf.Max(stringPositionInternal, stringSelectPositionInternal);
+ caretPositionInternal = caretSelectPositionInternal = GetCaretPositionFromStringIndex(stringSelectPositionInternal);
+
+ #if TMP_DEBUG_MODE
+ Debug.Log("Caret Position: " + caretPositionInternal + " Selection Position: " + caretSelectPositionInternal + " String Position: " + stringPositionInternal + " String Select Position: " + stringSelectPositionInternal);
+ #endif
+ return;
+ }
+
+ int position;
+ if (ctrl)
+ position = FindNextWordBegin();
+ else
+ {
+ if (m_isRichTextEditingAllowed)
+ {
+ // Special handling for Surrogate pairs and Diacritical marks.
+ if (stringSelectPositionInternal < text.Length && char.IsHighSurrogate(text[stringSelectPositionInternal]))
+ position = stringSelectPositionInternal + 2;
+ else
+ position = stringSelectPositionInternal + 1;
+ }
+ else
+ {
+ position = m_TextComponent.textInfo.characterInfo[caretSelectPositionInternal].index + m_TextComponent.textInfo.characterInfo[caretSelectPositionInternal].stringLength;
+ }
+
+ }
+
+ if (shift)
+ {
+ stringSelectPositionInternal = position;
+ caretSelectPositionInternal = GetCaretPositionFromStringIndex(stringSelectPositionInternal);
+ }
+ else
+ {
+ stringSelectPositionInternal = stringPositionInternal = position;
+
+ // Only increase caret position as we cross character boundary.
+ if (stringPositionInternal >= m_TextComponent.textInfo.characterInfo[caretPositionInternal].index + m_TextComponent.textInfo.characterInfo[caretPositionInternal].stringLength)
+ caretSelectPositionInternal = caretPositionInternal = GetCaretPositionFromStringIndex(stringSelectPositionInternal);
+ }
+
+ #if TMP_DEBUG_MODE
+ Debug.Log("Caret Position: " + caretPositionInternal + " Selection Position: " + caretSelectPositionInternal + " String Position: " + stringPositionInternal + " String Select Position: " + stringSelectPositionInternal);
+ #endif
+ }
+
+ private int FindPrevWordBegin()
+ {
+ if (stringSelectPositionInternal - 2 < 0)
+ return 0;
+
+ int spaceLoc = text.LastIndexOfAny(kSeparators, stringSelectPositionInternal - 2);
+
+ if (spaceLoc == -1)
+ spaceLoc = 0;
+ else
+ spaceLoc++;
+
+ return spaceLoc;
+ }
+
+ private void MoveLeft(bool shift, bool ctrl)
+ {
+ if (hasSelection && !shift)
+ {
+ // By convention, if we have a selection and move left without holding shift,
+ // we just place the cursor at the start.
+ stringPositionInternal = stringSelectPositionInternal = Mathf.Min(stringPositionInternal, stringSelectPositionInternal);
+ caretPositionInternal = caretSelectPositionInternal = GetCaretPositionFromStringIndex(stringSelectPositionInternal);
+
+ #if TMP_DEBUG_MODE
+ Debug.Log("Caret Position: " + caretPositionInternal + " Selection Position: " + caretSelectPositionInternal + " String Position: " + stringPositionInternal + " String Select Position: " + stringSelectPositionInternal);
+ #endif
+ return;
+ }
+
+ int position;
+ if (ctrl)
+ position = FindPrevWordBegin();
+ else
+ {
+ if (m_isRichTextEditingAllowed)
+ {
+ // Special handling for Surrogate pairs and Diacritical marks.
+ if (stringSelectPositionInternal > 0 && char.IsLowSurrogate(text[stringSelectPositionInternal - 1]))
+ position = stringSelectPositionInternal - 2;
+ else
+ position = stringSelectPositionInternal - 1;
+ }
+ else
+ {
+ //position = GetStringIndexFromCaretPosition(caretSelectPositionInternal - 1);
+ position = caretSelectPositionInternal < 2
+ ? m_TextComponent.textInfo.characterInfo[0].index
+ : m_TextComponent.textInfo.characterInfo[caretSelectPositionInternal - 2].index + m_TextComponent.textInfo.characterInfo[caretSelectPositionInternal - 2].stringLength;
+ }
+ }
+
+ if (shift)
+ {
+ stringSelectPositionInternal = position;
+ caretSelectPositionInternal = GetCaretPositionFromStringIndex(stringSelectPositionInternal);
+ }
+ else
+ {
+ stringSelectPositionInternal = stringPositionInternal = position;
+
+ // Only decrease caret position as we cross character boundary.
+ if (caretPositionInternal > 0 && stringPositionInternal <= m_TextComponent.textInfo.characterInfo[caretPositionInternal - 1].index)
+ caretSelectPositionInternal = caretPositionInternal = GetCaretPositionFromStringIndex(stringSelectPositionInternal);
+ }
+
+ #if TMP_DEBUG_MODE
+ Debug.Log("Caret Position: " + caretPositionInternal + " Selection Position: " + caretSelectPositionInternal + " String Position: " + stringPositionInternal + " String Select Position: " + stringSelectPositionInternal);
+ #endif
+ }
+
+
+ private int LineUpCharacterPosition(int originalPos, bool goToFirstChar)
+ {
+ if (originalPos >= m_TextComponent.textInfo.characterCount)
+ originalPos -= 1;
+
+ TMP_CharacterInfo originChar = m_TextComponent.textInfo.characterInfo[originalPos];
+ int originLine = originChar.lineNumber;
+
+ // We are on the first line return first character
+ if (originLine - 1 < 0)
+ return goToFirstChar ? 0 : originalPos;
+
+ int endCharIdx = m_TextComponent.textInfo.lineInfo[originLine].firstCharacterIndex - 1;
+
+ int closest = -1;
+ float distance = TMP_Math.FLOAT_MAX;
+ float range = 0;
+
+ for (int i = m_TextComponent.textInfo.lineInfo[originLine - 1].firstCharacterIndex; i < endCharIdx; ++i)
+ {
+ TMP_CharacterInfo currentChar = m_TextComponent.textInfo.characterInfo[i];
+
+ float d = originChar.origin - currentChar.origin;
+ float r = d / (currentChar.xAdvance - currentChar.origin);
+
+ if (r >= 0 && r <= 1)
+ {
+ if (r < 0.5f)
+ return i;
+ else
+ return i + 1;
+ }
+
+ d = Mathf.Abs(d);
+
+ if (d < distance)
+ {
+ closest = i;
+ distance = d;
+ range = r;
+ }
+ }
+
+ if (closest == -1) return endCharIdx;
+
+ //Debug.Log("Returning nearest character with Range = " + range);
+
+ if (range < 0.5f)
+ return closest;
+ else
+ return closest + 1;
+ }
+
+
+ private int LineDownCharacterPosition(int originalPos, bool goToLastChar)
+ {
+ if (originalPos >= m_TextComponent.textInfo.characterCount)
+ return m_TextComponent.textInfo.characterCount - 1; // text.Length;
+
+ TMP_CharacterInfo originChar = m_TextComponent.textInfo.characterInfo[originalPos];
+ int originLine = originChar.lineNumber;
+
+ //// We are on the last line return last character
+ if (originLine + 1 >= m_TextComponent.textInfo.lineCount)
+ return goToLastChar ? m_TextComponent.textInfo.characterCount - 1 : originalPos;
+
+ // Need to determine end line for next line.
+ int endCharIdx = m_TextComponent.textInfo.lineInfo[originLine + 1].lastCharacterIndex;
+
+ int closest = -1;
+ float distance = TMP_Math.FLOAT_MAX;
+ float range = 0;
+
+ for (int i = m_TextComponent.textInfo.lineInfo[originLine + 1].firstCharacterIndex; i < endCharIdx; ++i)
+ {
+ TMP_CharacterInfo currentChar = m_TextComponent.textInfo.characterInfo[i];
+
+ float d = originChar.origin - currentChar.origin;
+ float r = d / (currentChar.xAdvance - currentChar.origin);
+
+ if (r >= 0 && r <= 1)
+ {
+ if (r < 0.5f)
+ return i;
+ else
+ return i + 1;
+ }
+
+ d = Mathf.Abs(d);
+
+ if (d < distance)
+ {
+ closest = i;
+ distance = d;
+ range = r;
+ }
+ }
+
+ if (closest == -1) return endCharIdx;
+
+ //Debug.Log("Returning nearest character with Range = " + range);
+
+ if (range < 0.5f)
+ return closest;
+ else
+ return closest + 1;
+ }
+
+
+ private int PageUpCharacterPosition(int originalPos, bool goToFirstChar)
+ {
+ if (originalPos >= m_TextComponent.textInfo.characterCount)
+ originalPos -= 1;
+
+ TMP_CharacterInfo originChar = m_TextComponent.textInfo.characterInfo[originalPos];
+ int originLine = originChar.lineNumber;
+
+ // We are on the first line return first character
+ if (originLine - 1 < 0)
+ return goToFirstChar ? 0 : originalPos;
+
+ float viewportHeight = m_TextViewport.rect.height;
+
+ int newLine = originLine - 1;
+ // Iterate through each subsequent line to find the first baseline that is not visible in the viewport.
+ for (; newLine > 0; newLine--)
+ {
+ if (m_TextComponent.textInfo.lineInfo[newLine].baseline > m_TextComponent.textInfo.lineInfo[originLine].baseline + viewportHeight)
+ break;
+ }
+
+ int endCharIdx = m_TextComponent.textInfo.lineInfo[newLine].lastCharacterIndex;
+
+ int closest = -1;
+ float distance = TMP_Math.FLOAT_MAX;
+ float range = 0;
+
+ for (int i = m_TextComponent.textInfo.lineInfo[newLine].firstCharacterIndex; i < endCharIdx; ++i)
+ {
+ TMP_CharacterInfo currentChar = m_TextComponent.textInfo.characterInfo[i];
+
+ float d = originChar.origin - currentChar.origin;
+ float r = d / (currentChar.xAdvance - currentChar.origin);
+
+ if (r >= 0 && r <= 1)
+ {
+ if (r < 0.5f)
+ return i;
+ else
+ return i + 1;
+ }
+
+ d = Mathf.Abs(d);
+
+ if (d < distance)
+ {
+ closest = i;
+ distance = d;
+ range = r;
+ }
+ }
+
+ if (closest == -1) return endCharIdx;
+
+ //Debug.Log("Returning nearest character with Range = " + range);
+
+ if (range < 0.5f)
+ return closest;
+ else
+ return closest + 1;
+ }
+
+
+ private int PageDownCharacterPosition(int originalPos, bool goToLastChar)
+ {
+ if (originalPos >= m_TextComponent.textInfo.characterCount)
+ return m_TextComponent.textInfo.characterCount - 1;
+
+ TMP_CharacterInfo originChar = m_TextComponent.textInfo.characterInfo[originalPos];
+ int originLine = originChar.lineNumber;
+
+ // We are on the last line return last character
+ if (originLine + 1 >= m_TextComponent.textInfo.lineCount)
+ return goToLastChar ? m_TextComponent.textInfo.characterCount - 1 : originalPos;
+
+ float viewportHeight = m_TextViewport.rect.height;
+
+ int newLine = originLine + 1;
+ // Iterate through each subsequent line to find the first baseline that is not visible in the viewport.
+ for (; newLine < m_TextComponent.textInfo.lineCount - 1; newLine++)
+ {
+ if (m_TextComponent.textInfo.lineInfo[newLine].baseline < m_TextComponent.textInfo.lineInfo[originLine].baseline - viewportHeight)
+ break;
+ }
+
+ // Need to determine end line for next line.
+ int endCharIdx = m_TextComponent.textInfo.lineInfo[newLine].lastCharacterIndex;
+
+ int closest = -1;
+ float distance = TMP_Math.FLOAT_MAX;
+ float range = 0;
+
+ for (int i = m_TextComponent.textInfo.lineInfo[newLine].firstCharacterIndex; i < endCharIdx; ++i)
+ {
+ TMP_CharacterInfo currentChar = m_TextComponent.textInfo.characterInfo[i];
+
+ float d = originChar.origin - currentChar.origin;
+ float r = d / (currentChar.xAdvance - currentChar.origin);
+
+ if (r >= 0 && r <= 1)
+ {
+ if (r < 0.5f)
+ return i;
+ else
+ return i + 1;
+ }
+
+ d = Mathf.Abs(d);
+
+ if (d < distance)
+ {
+ closest = i;
+ distance = d;
+ range = r;
+ }
+ }
+
+ if (closest == -1) return endCharIdx;
+
+ if (range < 0.5f)
+ return closest;
+ else
+ return closest + 1;
+ }
+
+
+ private void MoveDown(bool shift)
+ {
+ MoveDown(shift, true);
+ }
+
+
+ private void MoveDown(bool shift, bool goToLastChar)
+ {
+ if (hasSelection && !shift)
+ {
+ // If we have a selection and press down without shift,
+ // set caret to end of selection before we move it down.
+ caretPositionInternal = caretSelectPositionInternal = Mathf.Max(caretPositionInternal, caretSelectPositionInternal);
+ }
+
+ int position = multiLine ? LineDownCharacterPosition(caretSelectPositionInternal, goToLastChar) : m_TextComponent.textInfo.characterCount - 1; // text.Length;
+
+ if (shift)
+ {
+ caretSelectPositionInternal = position;
+ stringSelectPositionInternal = GetStringIndexFromCaretPosition(caretSelectPositionInternal);
+ }
+ else
+ {
+ caretSelectPositionInternal = caretPositionInternal = position;
+ stringSelectPositionInternal = stringPositionInternal = GetStringIndexFromCaretPosition(caretSelectPositionInternal);
+ }
+
+ #if TMP_DEBUG_MODE
+ Debug.Log("Caret Position: " + caretPositionInternal + " Selection Position: " + caretSelectPositionInternal + " String Position: " + stringPositionInternal + " String Select Position: " + stringSelectPositionInternal);
+ #endif
+ }
+
+ private void MoveUp(bool shift)
+ {
+ MoveUp(shift, true);
+ }
+
+
+ private void MoveUp(bool shift, bool goToFirstChar)
+ {
+ if (hasSelection && !shift)
+ {
+ // If we have a selection and press up without shift,
+ // set caret position to start of selection before we move it up.
+ caretPositionInternal = caretSelectPositionInternal = Mathf.Min(caretPositionInternal, caretSelectPositionInternal);
+ }
+
+ int position = multiLine ? LineUpCharacterPosition(caretSelectPositionInternal, goToFirstChar) : 0;
+
+ if (shift)
+ {
+ caretSelectPositionInternal = position;
+ stringSelectPositionInternal = GetStringIndexFromCaretPosition(caretSelectPositionInternal);
+ }
+ else
+ {
+ caretSelectPositionInternal = caretPositionInternal = position;
+ stringSelectPositionInternal = stringPositionInternal = GetStringIndexFromCaretPosition(caretSelectPositionInternal);
+ }
+
+ #if TMP_DEBUG_MODE
+ Debug.Log("Caret Position: " + caretPositionInternal + " Selection Position: " + caretSelectPositionInternal + " String Position: " + stringPositionInternal + " String Select Position: " + stringSelectPositionInternal);
+ #endif
+ }
+
+
+ private void MovePageUp(bool shift)
+ {
+ MovePageUp(shift, true);
+ }
+
+ private void MovePageUp(bool shift, bool goToFirstChar)
+ {
+ if (hasSelection && !shift)
+ {
+ // If we have a selection and press up without shift,
+ // set caret position to start of selection before we move it up.
+ caretPositionInternal = caretSelectPositionInternal = Mathf.Min(caretPositionInternal, caretSelectPositionInternal);
+ }
+
+ int position = multiLine ? PageUpCharacterPosition(caretSelectPositionInternal, goToFirstChar) : 0;
+
+ if (shift)
+ {
+ caretSelectPositionInternal = position;
+ stringSelectPositionInternal = GetStringIndexFromCaretPosition(caretSelectPositionInternal);
+ }
+ else
+ {
+ caretSelectPositionInternal = caretPositionInternal = position;
+ stringSelectPositionInternal = stringPositionInternal = GetStringIndexFromCaretPosition(caretSelectPositionInternal);
+ }
+
+
+ // Scroll to top of viewport
+ //int currentLine = m_TextComponent.textInfo.characterInfo[position].lineNumber;
+ //float lineAscender = m_TextComponent.textInfo.lineInfo[currentLine].ascender;
+
+ // Adjust text area up or down if not in single line mode.
+ if (m_LineType != LineType.SingleLine)
+ {
+ float offset = m_TextViewport.rect.height; // m_TextViewport.rect.yMax - (m_TextComponent.rectTransform.anchoredPosition.y + lineAscender);
+
+ float topTextBounds = m_TextComponent.rectTransform.position.y + m_TextComponent.textBounds.max.y;
+ float topViewportBounds = m_TextViewport.position.y + m_TextViewport.rect.yMax;
+
+ offset = topViewportBounds > topTextBounds + offset ? offset : topViewportBounds - topTextBounds;
+
+ m_TextComponent.rectTransform.anchoredPosition += new Vector2(0, offset);
+ AssignPositioningIfNeeded();
+ m_IsScrollbarUpdateRequired = true;
+ }
+
+ #if TMP_DEBUG_MODE
+ Debug.Log("Caret Position: " + caretPositionInternal + " Selection Position: " + caretSelectPositionInternal + " String Position: " + stringPositionInternal + " String Select Position: " + stringSelectPositionInternal);
+ #endif
+
+ }
+
+
+ private void MovePageDown(bool shift)
+ {
+ MovePageDown(shift, true);
+ }
+
+ private void MovePageDown(bool shift, bool goToLastChar)
+ {
+ if (hasSelection && !shift)
+ {
+ // If we have a selection and press down without shift,
+ // set caret to end of selection before we move it down.
+ caretPositionInternal = caretSelectPositionInternal = Mathf.Max(caretPositionInternal, caretSelectPositionInternal);
+ }
+
+ int position = multiLine ? PageDownCharacterPosition(caretSelectPositionInternal, goToLastChar) : m_TextComponent.textInfo.characterCount - 1;
+
+ if (shift)
+ {
+ caretSelectPositionInternal = position;
+ stringSelectPositionInternal = GetStringIndexFromCaretPosition(caretSelectPositionInternal);
+ }
+ else
+ {
+ caretSelectPositionInternal = caretPositionInternal = position;
+ stringSelectPositionInternal = stringPositionInternal = GetStringIndexFromCaretPosition(caretSelectPositionInternal);
+ }
+
+ // Scroll to top of viewport
+ //int currentLine = m_TextComponent.textInfo.characterInfo[position].lineNumber;
+ //float lineAscender = m_TextComponent.textInfo.lineInfo[currentLine].ascender;
+
+ // Adjust text area up or down if not in single line mode.
+ if (m_LineType != LineType.SingleLine)
+ {
+ float offset = m_TextViewport.rect.height; // m_TextViewport.rect.yMax - (m_TextComponent.rectTransform.anchoredPosition.y + lineAscender);
+
+ float bottomTextBounds = m_TextComponent.rectTransform.position.y + m_TextComponent.textBounds.min.y;
+ float bottomViewportBounds = m_TextViewport.position.y + m_TextViewport.rect.yMin;
+
+ offset = bottomViewportBounds > bottomTextBounds + offset ? offset : bottomViewportBounds - bottomTextBounds;
+
+ m_TextComponent.rectTransform.anchoredPosition += new Vector2(0, offset);
+ AssignPositioningIfNeeded();
+ m_IsScrollbarUpdateRequired = true;
+ }
+
+ #if TMP_DEBUG_MODE
+ Debug.Log("Caret Position: " + caretPositionInternal + " Selection Position: " + caretSelectPositionInternal + " String Position: " + stringPositionInternal + " String Select Position: " + stringSelectPositionInternal);
+ #endif
+
+ }
+
+ private void Delete()
+ {
+ if (m_ReadOnly)
+ return;
+
+ if (stringPositionInternal == stringSelectPositionInternal)
+ return;
+
+ if (m_isRichTextEditingAllowed || m_isSelectAll)
+ {
+ // Handling of Delete when Rich Text is allowed.
+ if (stringPositionInternal < stringSelectPositionInternal)
+ {
+ m_Text = text.Remove(stringPositionInternal, stringSelectPositionInternal - stringPositionInternal);
+ stringSelectPositionInternal = stringPositionInternal;
+ }
+ else
+ {
+ m_Text = text.Remove(stringSelectPositionInternal, stringPositionInternal - stringSelectPositionInternal);
+ stringPositionInternal = stringSelectPositionInternal;
+ }
+
+ m_isSelectAll = false;
+ }
+ else
+ {
+ if (caretPositionInternal < caretSelectPositionInternal)
+ {
+ stringPositionInternal = m_TextComponent.textInfo.characterInfo[caretPositionInternal].index;
+ stringSelectPositionInternal = m_TextComponent.textInfo.characterInfo[caretSelectPositionInternal - 1].index + m_TextComponent.textInfo.characterInfo[caretSelectPositionInternal - 1].stringLength;
+
+ m_Text = text.Remove(stringPositionInternal, stringSelectPositionInternal - stringPositionInternal);
+
+ stringSelectPositionInternal = stringPositionInternal;
+ caretSelectPositionInternal = caretPositionInternal;
+ }
+ else
+ {
+ stringPositionInternal = m_TextComponent.textInfo.characterInfo[caretPositionInternal - 1].index + m_TextComponent.textInfo.characterInfo[caretPositionInternal - 1].stringLength;
+ stringSelectPositionInternal = m_TextComponent.textInfo.characterInfo[caretSelectPositionInternal].index;
+
+ m_Text = text.Remove(stringSelectPositionInternal, stringPositionInternal - stringSelectPositionInternal);
+
+ stringPositionInternal = stringSelectPositionInternal;
+ caretPositionInternal = caretSelectPositionInternal;
+ }
+ }
+
+ #if TMP_DEBUG_MODE
+ Debug.Log("Caret Position: " + caretPositionInternal + " Selection Position: " + caretSelectPositionInternal + " String Position: " + stringPositionInternal + " String Select Position: " + stringSelectPositionInternal);
+ #endif
+ }
+
+ /// <summary>
+ /// Handling of DEL key
+ /// </summary>
+ private void DeleteKey()
+ {
+ if (m_ReadOnly)
+ return;
+
+ if (hasSelection)
+ {
+ Delete();
+ UpdateTouchKeyboardFromEditChanges();
+ SendOnValueChangedAndUpdateLabel();
+ }
+ else
+ {
+ if (m_isRichTextEditingAllowed)
+ {
+ if (stringPositionInternal < text.Length)
+ {
+ // Special handling for Surrogate Pairs
+ if (char.IsHighSurrogate(text[stringPositionInternal]))
+ m_Text = text.Remove(stringPositionInternal, 2);
+ else
+ m_Text = text.Remove(stringPositionInternal, 1);
+
+ UpdateTouchKeyboardFromEditChanges();
+ SendOnValueChangedAndUpdateLabel();
+ }
+ }
+ else
+ {
+ if (caretPositionInternal < m_TextComponent.textInfo.characterCount - 1)
+ {
+ int numberOfCharactersToRemove = m_TextComponent.textInfo.characterInfo[caretPositionInternal].stringLength;
+
+ // Adjust string position to skip any potential rich text tags.
+ int nextCharacterStringPosition = m_TextComponent.textInfo.characterInfo[caretPositionInternal].index;
+
+ m_Text = text.Remove(nextCharacterStringPosition, numberOfCharactersToRemove);
+
+ SendOnValueChangedAndUpdateLabel();
+ }
+ }
+ }
+
+ #if TMP_DEBUG_MODE
+ Debug.Log("Caret Position: " + caretPositionInternal + " Selection Position: " + caretSelectPositionInternal + " String Position: " + stringPositionInternal + " String Select Position: " + stringSelectPositionInternal);
+ #endif
+ }
+
+ /// <summary>
+ /// Handling of Backspace key
+ /// </summary>
+ private void Backspace()
+ {
+ if (m_ReadOnly)
+ return;
+
+ if (hasSelection)
+ {
+ Delete();
+ UpdateTouchKeyboardFromEditChanges();
+ SendOnValueChangedAndUpdateLabel();
+ }
+ else
+ {
+ if (m_isRichTextEditingAllowed)
+ {
+ if (stringPositionInternal > 0)
+ {
+ int numberOfCharactersToRemove = 1;
+
+ // Special handling for Surrogate pairs and Diacritical marks
+ if (char.IsLowSurrogate(text[stringPositionInternal - 1]))
+ numberOfCharactersToRemove = 2;
+
+ stringSelectPositionInternal = stringPositionInternal = stringPositionInternal - numberOfCharactersToRemove;
+
+ m_Text = text.Remove(stringPositionInternal, numberOfCharactersToRemove);
+
+ caretSelectPositionInternal = caretPositionInternal = caretPositionInternal - 1;
+
+ m_isLastKeyBackspace = true;
+
+ UpdateTouchKeyboardFromEditChanges();
+ SendOnValueChangedAndUpdateLabel();
+ }
+ }
+ else
+ {
+ if (caretPositionInternal > 0)
+ {
+ int numberOfCharactersToRemove = m_TextComponent.textInfo.characterInfo[caretPositionInternal - 1].stringLength;
+
+ // Delete the previous character
+ m_Text = text.Remove(m_TextComponent.textInfo.characterInfo[caretPositionInternal - 1].index, numberOfCharactersToRemove);
+
+ // Get new adjusted string position
+ stringSelectPositionInternal = stringPositionInternal = caretPositionInternal < 2
+ ? m_TextComponent.textInfo.characterInfo[0].index
+ : m_TextComponent.textInfo.characterInfo[caretPositionInternal - 2].index + m_TextComponent.textInfo.characterInfo[caretPositionInternal - 2].stringLength;
+
+ caretSelectPositionInternal = caretPositionInternal = caretPositionInternal - 1;
+ }
+
+ m_isLastKeyBackspace = true;
+
+ UpdateTouchKeyboardFromEditChanges();
+ SendOnValueChangedAndUpdateLabel();
+ }
+
+ }
+
+ #if TMP_DEBUG_MODE
+ Debug.Log("Caret Position: " + caretPositionInternal + " Selection Position: " + caretSelectPositionInternal + " String Position: " + stringPositionInternal + " String Select Position: " + stringSelectPositionInternal);
+ #endif
+ }
+
+
+ /// <summary>
+ /// Append the specified text to the end of the current.
+ /// </summary>
+ protected virtual void Append(string input)
+ {
+ if (m_ReadOnly)
+ return;
+
+ if (InPlaceEditing() == false)
+ return;
+
+ for (int i = 0, imax = input.Length; i < imax; ++i)
+ {
+ char c = input[i];
+
+ if (c >= ' ' || c == '\t' || c == '\r' || c == 10 || c == '\n')
+ {
+ Append(c);
+ }
+ }
+ }
+
+ protected virtual void Append(char input)
+ {
+ if (m_ReadOnly)
+ return;
+
+ if (InPlaceEditing() == false)
+ return;
+
+ // If we have an input validator, validate the input first
+ if (onValidateInput != null)
+ input = onValidateInput(text, stringPositionInternal, input);
+ else if (characterValidation == CharacterValidation.CustomValidator)
+ {
+ input = Validate(text, stringPositionInternal, input);
+
+ if (input == 0) return;
+
+ SendOnValueChanged();
+ UpdateLabel();
+
+ return;
+ }
+ else if (characterValidation != CharacterValidation.None)
+ input = Validate(text, stringPositionInternal, input);
+
+ // If the input is invalid, skip it
+ if (input == 0)
+ return;
+
+ // Append the character and update the label
+ Insert(input);
+ }
+
+
+ // Insert the character and update the label.
+ private void Insert(char c)
+ {
+ if (m_ReadOnly)
+ return;
+
+ string replaceString = c.ToString();
+ Delete();
+
+ // Can't go past the character limit
+ if (characterLimit > 0 && text.Length >= characterLimit)
+ return;
+
+ m_Text = text.Insert(m_StringPosition, replaceString);
+
+ if (!char.IsHighSurrogate(c))
+ caretSelectPositionInternal = caretPositionInternal += 1;
+
+ stringSelectPositionInternal = stringPositionInternal += 1;
+
+ UpdateTouchKeyboardFromEditChanges();
+ SendOnValueChanged();
+
+ #if TMP_DEBUG_MODE
+ Debug.Log("Caret Position: " + caretPositionInternal + " Selection Position: " + caretSelectPositionInternal + " String Position: " + stringPositionInternal + " String Select Position: " + stringSelectPositionInternal);
+ #endif
+ }
+
+ private void UpdateTouchKeyboardFromEditChanges()
+ {
+ // Update the TouchKeyboard's text from edit changes
+ // if in-place editing is allowed
+ if (m_SoftKeyboard != null && InPlaceEditing())
+ {
+ m_SoftKeyboard.text = m_Text;
+ }
+ }
+
+ private void SendOnValueChangedAndUpdateLabel()
+ {
+ UpdateLabel();
+ SendOnValueChanged();
+ }
+
+ private void SendOnValueChanged()
+ {
+ if (onValueChanged != null)
+ onValueChanged.Invoke(text);
+ }
+
+ /// <summary>
+ /// Submit the input field's text.
+ /// </summary>
+
+ protected void SendOnEndEdit()
+ {
+ if (onEndEdit != null)
+ onEndEdit.Invoke(m_Text);
+ }
+
+ protected void SendOnSubmit()
+ {
+ if (onSubmit != null)
+ onSubmit.Invoke(m_Text);
+ }
+
+ protected void SendOnFocus()
+ {
+ if (onSelect != null)
+ onSelect.Invoke(m_Text);
+ }
+
+ protected void SendOnFocusLost()
+ {
+ if (onDeselect != null)
+ onDeselect.Invoke(m_Text);
+ }
+
+ protected void SendOnTextSelection()
+ {
+ m_isSelected = true;
+
+ if (onTextSelection != null)
+ onTextSelection.Invoke(m_Text, stringPositionInternal, stringSelectPositionInternal);
+ }
+
+ protected void SendOnEndTextSelection()
+ {
+ if (!m_isSelected) return;
+
+ if (onEndTextSelection != null)
+ onEndTextSelection.Invoke(m_Text, stringPositionInternal, stringSelectPositionInternal);
+
+ m_isSelected = false;
+ }
+
+ protected void SendTouchScreenKeyboardStatusChanged()
+ {
+ if (onTouchScreenKeyboardStatusChanged != null)
+ onTouchScreenKeyboardStatusChanged.Invoke(m_SoftKeyboard.status);
+ }
+
+
+ /// <summary>
+ /// Update the visual text Text.
+ /// </summary>
+
+ protected void UpdateLabel()
+ {
+ if (m_TextComponent != null && m_TextComponent.font != null && m_PreventCallback == false)
+ {
+ // Prevent callback from the text component as we assign new text. This is to prevent a recursive call.
+ m_PreventCallback = true;
+
+ string fullText;
+ if (compositionString.Length > 0)
+ {
+ fullText = text.Substring(0, m_StringPosition) + compositionString + text.Substring(m_StringPosition);
+
+ // Should adjust caret position
+ //Debug.Log("Handling IME Input... [" + compositionString + "] of length [" + compositionString.Length + "] at StringPosition [" + m_StringPosition + "]");
+ //for (int i = 0; i < compositionString.Length; i++)
+ // Debug.Log((uint)compositionString[i]);
+ }
+ else
+ {
+ fullText = text;
+ //Debug.Log("Handling Input... [" + text + "]");
+ }
+
+ string processed;
+ if (inputType == InputType.Password)
+ processed = new string(asteriskChar, fullText.Length);
+ else
+ processed = fullText;
+
+ bool isEmpty = string.IsNullOrEmpty(fullText);
+
+ if (m_Placeholder != null)
+ m_Placeholder.enabled = isEmpty;
+
+ if (!isEmpty)
+ {
+ SetCaretVisible();
+ }
+
+ m_TextComponent.text = processed + "\u200B"; // Extra space is added for Caret tracking.
+
+ // Special handling to limit the number of lines of text in the Input Field.
+ if (m_LineLimit > 0)
+ {
+ m_TextComponent.ForceMeshUpdate();
+
+ // Check if text exceeds maximum number of lines.
+ if (m_TextComponent.textInfo.lineCount > m_LineLimit)
+ {
+ int lastValidCharacterIndex = m_TextComponent.textInfo.lineInfo[m_LineLimit - 1].lastCharacterIndex;
+ int characterStringIndex = m_TextComponent.textInfo.characterInfo[lastValidCharacterIndex].index + m_TextComponent.textInfo.characterInfo[lastValidCharacterIndex].stringLength;
+ text = processed.Remove(characterStringIndex, processed.Length - characterStringIndex);
+ m_TextComponent.text = text + "\u200B";
+ }
+ }
+
+ if (m_IsTextComponentUpdateRequired)
+ {
+ m_IsTextComponentUpdateRequired = false;
+ m_TextComponent.ForceMeshUpdate();
+ }
+
+ MarkGeometryAsDirty();
+
+ // Scrollbar should be updated.
+ m_IsScrollbarUpdateRequired = true;
+
+ m_PreventCallback = false;
+ }
+ }
+
+ void UpdateScrollbar()
+ {
+ // Update Scrollbar
+ if (m_VerticalScrollbar)
+ {
+ float size = m_TextViewport.rect.height / m_TextComponent.preferredHeight;
+
+ m_IsUpdatingScrollbarValues = true;
+
+ m_VerticalScrollbar.size = size;
+
+ m_ScrollPosition = m_VerticalScrollbar.value = m_TextComponent.rectTransform.anchoredPosition.y / (m_TextComponent.preferredHeight - m_TextViewport.rect.height);
+
+ //m_VerticalScrollbar.numberOfSteps = (int)(m_TextComponent.textInfo.lineCount / 0.25f); // Replace by scroll sensitivity.
+
+ //Debug.Log("Updating Scrollbar... Value: " + m_VerticalScrollbar.value);
+ }
+ }
+
+
+ /// <summary>
+ /// Function to update the vertical position of the text container when OnValueChanged event is received from the Scrollbar.
+ /// </summary>
+ /// <param name="value"></param>
+ void OnScrollbarValueChange(float value)
+ {
+ if (m_IsUpdatingScrollbarValues) { m_IsUpdatingScrollbarValues = false; return; }
+
+ if (value < 0 || value > 1) return;
+
+ AdjustTextPositionRelativeToViewport(value);
+
+ m_ScrollPosition = value;
+
+ //Debug.Log("Scrollbar value is: " + value + " Transform POS: " + m_TextComponent.rectTransform.anchoredPosition);
+ }
+
+ /// <summary>
+ /// Adjusts the relative position of the body of the text relative to the viewport.
+ /// </summary>
+ /// <param name="relativePosition"></param>
+ void AdjustTextPositionRelativeToViewport (float relativePosition)
+ {
+ //Debug.Log("- Adjusting vertical text position to " + relativePosition);
+ if (m_TextViewport == null)
+ return;
+
+ TMP_TextInfo textInfo = m_TextComponent.textInfo;
+
+ // Check to make sure we have valid data and lines to query.
+ if (textInfo == null || textInfo.lineInfo == null || textInfo.lineCount == 0 || textInfo.lineCount > textInfo.lineInfo.Length) return;
+
+ //m_TextComponent.rectTransform.anchoredPosition = new Vector2(m_TextComponent.rectTransform.anchoredPosition.x, (textHeight - viewportHeight) * relativePosition);
+ m_TextComponent.rectTransform.anchoredPosition = new Vector2(m_TextComponent.rectTransform.anchoredPosition.x, (m_TextComponent.preferredHeight - m_TextViewport.rect.height) * relativePosition);
+
+ AssignPositioningIfNeeded();
+
+ //Debug.Log("Text height: " + m_TextComponent.preferredHeight + " Viewport height: " + m_TextViewport.rect.height + " Adjusted RectTransform anchordedPosition:" + m_TextComponent.rectTransform.anchoredPosition + " Text Bounds: " + m_TextComponent.bounds.ToString("f3"));
+ }
+
+
+ private int GetCaretPositionFromStringIndex(int stringIndex)
+ {
+ int count = m_TextComponent.textInfo.characterCount;
+
+ for (int i = 0; i < count; i++)
+ {
+ if (m_TextComponent.textInfo.characterInfo[i].index >= stringIndex)
+ return i;
+ }
+
+ return count;
+ }
+
+ /// <summary>
+ /// Returns / places the caret before the given character at the string index.
+ /// </summary>
+ /// <param name="stringIndex"></param>
+ /// <returns></returns>
+ private int GetMinCaretPositionFromStringIndex(int stringIndex)
+ {
+ int count = m_TextComponent.textInfo.characterCount;
+
+ for (int i = 0; i < count; i++)
+ {
+ if (stringIndex < m_TextComponent.textInfo.characterInfo[i].index + m_TextComponent.textInfo.characterInfo[i].stringLength)
+ return i;
+ }
+
+ return count;
+ }
+
+ /// <summary>
+ /// Returns / places the caret after the given character at the string index.
+ /// </summary>
+ /// <param name="stringIndex"></param>
+ /// <returns></returns>
+ private int GetMaxCaretPositionFromStringIndex(int stringIndex)
+ {
+ int count = m_TextComponent.textInfo.characterCount;
+
+ for (int i = 0; i < count; i++)
+ {
+ if (m_TextComponent.textInfo.characterInfo[i].index >= stringIndex)
+ return i;
+ }
+
+ return count;
+ }
+
+ private int GetStringIndexFromCaretPosition(int caretPosition)
+ {
+ // Clamp values between 0 and character count.
+ ClampCaretPos(ref caretPosition);
+
+ return m_TextComponent.textInfo.characterInfo[caretPosition].index;
+ }
+
+
+ public void ForceLabelUpdate()
+ {
+ UpdateLabel();
+ }
+
+ private void MarkGeometryAsDirty()
+ {
+ #if UNITY_EDITOR
+ #if UNITY_2018_3_OR_NEWER
+ if (!Application.isPlaying || UnityEditor.PrefabUtility.IsPartOfPrefabAsset(this))
+ return;
+ #else
+ if (!Application.isPlaying || UnityEditor.PrefabUtility.GetPrefabObject(gameObject) != null)
+ return;
+ #endif
+ #endif
+
+ CanvasUpdateRegistry.RegisterCanvasElementForGraphicRebuild(this);
+ }
+
+ public virtual void Rebuild(CanvasUpdate update)
+ {
+ switch (update)
+ {
+ case CanvasUpdate.LatePreRender:
+ UpdateGeometry();
+ break;
+ }
+ }
+
+ public virtual void LayoutComplete()
+ { }
+
+ public virtual void GraphicUpdateComplete()
+ { }
+
+ private void UpdateGeometry()
+ {
+ #if UNITY_EDITOR
+ if (!Application.isPlaying)
+ return;
+ #endif
+
+ // No need to draw a cursor on mobile as its handled by the devices keyboard.
+ if (InPlaceEditing() == false)
+ return;
+
+ if (m_CachedInputRenderer == null)
+ return;
+
+ OnFillVBO(mesh);
+
+ m_CachedInputRenderer.SetMesh(mesh);
+ }
+
+
+ /// <summary>
+ /// Method to keep the Caret RectTransform properties in sync with the text object's RectTransform
+ /// </summary>
+ private void AssignPositioningIfNeeded()
+ {
+ if (m_TextComponent != null && caretRectTrans != null &&
+ (caretRectTrans.localPosition != m_TextComponent.rectTransform.localPosition ||
+ caretRectTrans.localRotation != m_TextComponent.rectTransform.localRotation ||
+ caretRectTrans.localScale != m_TextComponent.rectTransform.localScale ||
+ caretRectTrans.anchorMin != m_TextComponent.rectTransform.anchorMin ||
+ caretRectTrans.anchorMax != m_TextComponent.rectTransform.anchorMax ||
+ caretRectTrans.anchoredPosition != m_TextComponent.rectTransform.anchoredPosition ||
+ caretRectTrans.sizeDelta != m_TextComponent.rectTransform.sizeDelta ||
+ caretRectTrans.pivot != m_TextComponent.rectTransform.pivot))
+ {
+ caretRectTrans.localPosition = m_TextComponent.rectTransform.localPosition;
+ caretRectTrans.localRotation = m_TextComponent.rectTransform.localRotation;
+ caretRectTrans.localScale = m_TextComponent.rectTransform.localScale;
+ caretRectTrans.anchorMin = m_TextComponent.rectTransform.anchorMin;
+ caretRectTrans.anchorMax = m_TextComponent.rectTransform.anchorMax;
+ caretRectTrans.anchoredPosition = m_TextComponent.rectTransform.anchoredPosition;
+ caretRectTrans.sizeDelta = m_TextComponent.rectTransform.sizeDelta;
+ caretRectTrans.pivot = m_TextComponent.rectTransform.pivot;
+
+ // Get updated world corners of viewport.
+ //m_TextViewport.GetLocalCorners(m_ViewportCorners);
+ }
+ }
+
+
+ private void OnFillVBO(Mesh vbo)
+ {
+ using (var helper = new VertexHelper())
+ {
+ if (!isFocused && !m_SelectionStillActive)
+ {
+ helper.FillMesh(vbo);
+ return;
+ }
+
+ if (m_IsStringPositionDirty)
+ {
+ stringPositionInternal = GetStringIndexFromCaretPosition(m_CaretPosition);
+ stringSelectPositionInternal = GetStringIndexFromCaretPosition(m_CaretSelectPosition);
+ m_IsStringPositionDirty = false;
+ }
+
+ if (m_IsCaretPositionDirty)
+ {
+ caretPositionInternal = GetCaretPositionFromStringIndex(stringPositionInternal);
+ caretSelectPositionInternal = GetCaretPositionFromStringIndex(stringSelectPositionInternal);
+ m_IsCaretPositionDirty = false;
+ }
+
+ if (!hasSelection && !m_ReadOnly)
+ {
+ GenerateCaret(helper, Vector2.zero);
+ SendOnEndTextSelection();
+ }
+ else
+ {
+ GenerateHightlight(helper, Vector2.zero);
+ SendOnTextSelection();
+ }
+
+ helper.FillMesh(vbo);
+ }
+ }
+
+
+ private void GenerateCaret(VertexHelper vbo, Vector2 roundingOffset)
+ {
+ if (!m_CaretVisible)
+ return;
+
+ if (m_CursorVerts == null)
+ {
+ CreateCursorVerts();
+ }
+
+ float width = m_CaretWidth;
+
+ // TODO: Optimize to only update the caret position when needed.
+
+ Vector2 startPosition = Vector2.zero;
+ float height = 0;
+ TMP_CharacterInfo currentCharacter;
+
+ int currentLine = m_TextComponent.textInfo.characterInfo[caretPositionInternal].lineNumber;
+
+ // Caret is positioned at the origin for the first character of each lines and at the advance for subsequent characters.
+ if (caretPositionInternal == m_TextComponent.textInfo.lineInfo[currentLine].firstCharacterIndex)
+ {
+ currentCharacter = m_TextComponent.textInfo.characterInfo[caretPositionInternal];
+ startPosition = new Vector2(currentCharacter.origin, currentCharacter.descender);
+ height = currentCharacter.ascender - currentCharacter.descender;
+ }
+ else
+ {
+ currentCharacter = m_TextComponent.textInfo.characterInfo[caretPositionInternal - 1];
+ startPosition = new Vector2(currentCharacter.xAdvance, currentCharacter.descender);
+ height = currentCharacter.ascender - currentCharacter.descender;
+ }
+
+ if (m_SoftKeyboard != null)
+ m_SoftKeyboard.selection = new RangeInt(stringPositionInternal, 0);
+
+ // Adjust the position of the RectTransform based on the caret position in the viewport (only if we have focus).
+ if (isFocused && startPosition != m_LastPosition || m_forceRectTransformAdjustment)
+ AdjustRectTransformRelativeToViewport(startPosition, height, currentCharacter.isVisible);
+
+ m_LastPosition = startPosition;
+
+ // Clamp Caret height
+ float top = startPosition.y + height;
+ float bottom = top - height;
+
+ // Minor tweak to address caret potentially being too thin based on canvas scaler values.
+ float scale = m_TextComponent.canvas.scaleFactor;
+
+ m_CursorVerts[0].position = new Vector3(startPosition.x, bottom, 0.0f);
+ m_CursorVerts[1].position = new Vector3(startPosition.x, top, 0.0f);
+ m_CursorVerts[2].position = new Vector3(startPosition.x + (width + 1) / scale, top, 0.0f);
+ m_CursorVerts[3].position = new Vector3(startPosition.x + (width + 1) / scale, bottom, 0.0f);
+
+ // Set Vertex Color for the caret color.
+ m_CursorVerts[0].color = caretColor;
+ m_CursorVerts[1].color = caretColor;
+ m_CursorVerts[2].color = caretColor;
+ m_CursorVerts[3].color = caretColor;
+
+ vbo.AddUIVertexQuad(m_CursorVerts);
+
+ int screenHeight = Screen.height;
+ // Removed multiple display support until it supports none native resolutions(case 741751)
+ //int displayIndex = m_TextComponent.canvas.targetDisplay;
+ //if (Screen.fullScreen && displayIndex < Display.displays.Length)
+ // screenHeight = Display.displays[displayIndex].renderingHeight;
+
+ startPosition.y = screenHeight - startPosition.y;
+ inputSystem.compositionCursorPos = startPosition;
+
+ //#if TMP_DEBUG_MODE
+ //Debug.Log("Caret position updated at frame: " + Time.frameCount);
+ //#endif
+ }
+
+
+ private void CreateCursorVerts()
+ {
+ m_CursorVerts = new UIVertex[4];
+
+ for (int i = 0; i < m_CursorVerts.Length; i++)
+ {
+ m_CursorVerts[i] = UIVertex.simpleVert;
+ m_CursorVerts[i].uv0 = Vector2.zero;
+ }
+ }
+
+
+ private void GenerateHightlight(VertexHelper vbo, Vector2 roundingOffset)
+ {
+ TMP_TextInfo textInfo = m_TextComponent.textInfo;
+
+ caretPositionInternal = m_CaretPosition = GetCaretPositionFromStringIndex(stringPositionInternal);
+ caretSelectPositionInternal = m_CaretSelectPosition = GetCaretPositionFromStringIndex(stringSelectPositionInternal);
+
+ if (m_SoftKeyboard != null)
+ {
+ int stringPosition = caretPositionInternal < caretSelectPositionInternal ? textInfo.characterInfo[caretPositionInternal].index : textInfo.characterInfo[caretSelectPositionInternal].index;
+ int length = caretPositionInternal < caretSelectPositionInternal ? stringSelectPositionInternal - stringPosition : stringPositionInternal - stringPosition;
+ m_SoftKeyboard.selection = new RangeInt(stringPosition, length);
+ }
+
+ // Adjust text RectTranform position to make sure it is visible in viewport.
+ Vector2 caretPosition;
+ float height = 0;
+ if (caretSelectPositionInternal < textInfo.characterCount)
+ {
+ caretPosition = new Vector2(textInfo.characterInfo[caretSelectPositionInternal].origin, textInfo.characterInfo[caretSelectPositionInternal].descender);
+ height = textInfo.characterInfo[caretSelectPositionInternal].ascender - textInfo.characterInfo[caretSelectPositionInternal].descender;
+ }
+ else
+ {
+ caretPosition = new Vector2(textInfo.characterInfo[caretSelectPositionInternal - 1].xAdvance, textInfo.characterInfo[caretSelectPositionInternal - 1].descender);
+ height = textInfo.characterInfo[caretSelectPositionInternal - 1].ascender - textInfo.characterInfo[caretSelectPositionInternal - 1].descender;
+ }
+
+ // TODO: Don't adjust the position of the RectTransform if Reset On Deactivation is disabled
+ // and we just selected the Input Field again.
+ AdjustRectTransformRelativeToViewport(caretPosition, height, true);
+
+ int startChar = Mathf.Max(0, caretPositionInternal);
+ int endChar = Mathf.Max(0, caretSelectPositionInternal);
+
+ // Ensure pos is always less then selPos to make the code simpler
+ if (startChar > endChar)
+ {
+ int temp = startChar;
+ startChar = endChar;
+ endChar = temp;
+ }
+
+ endChar -= 1;
+
+ //Debug.Log("Updating Highlight... Caret Position: " + startChar + " Caret Select POS: " + endChar);
+
+
+ int currentLineIndex = textInfo.characterInfo[startChar].lineNumber;
+ int nextLineStartIdx = textInfo.lineInfo[currentLineIndex].lastCharacterIndex;
+
+ UIVertex vert = UIVertex.simpleVert;
+ vert.uv0 = Vector2.zero;
+ vert.color = selectionColor;
+
+ int currentChar = startChar;
+ while (currentChar <= endChar && currentChar < textInfo.characterCount)
+ {
+ if (currentChar == nextLineStartIdx || currentChar == endChar)
+ {
+ TMP_CharacterInfo startCharInfo = textInfo.characterInfo[startChar];
+ TMP_CharacterInfo endCharInfo = textInfo.characterInfo[currentChar];
+
+ // Extra check to handle Carriage Return
+ if (currentChar > 0 && endCharInfo.character == 10 && textInfo.characterInfo[currentChar - 1].character == 13)
+ endCharInfo = textInfo.characterInfo[currentChar - 1];
+
+ Vector2 startPosition = new Vector2(startCharInfo.origin, textInfo.lineInfo[currentLineIndex].ascender);
+ Vector2 endPosition = new Vector2(endCharInfo.xAdvance, textInfo.lineInfo[currentLineIndex].descender);
+
+ var startIndex = vbo.currentVertCount;
+ vert.position = new Vector3(startPosition.x, endPosition.y, 0.0f);
+ vbo.AddVert(vert);
+
+ vert.position = new Vector3(endPosition.x, endPosition.y, 0.0f);
+ vbo.AddVert(vert);
+
+ vert.position = new Vector3(endPosition.x, startPosition.y, 0.0f);
+ vbo.AddVert(vert);
+
+ vert.position = new Vector3(startPosition.x, startPosition.y, 0.0f);
+ vbo.AddVert(vert);
+
+ vbo.AddTriangle(startIndex, startIndex + 1, startIndex + 2);
+ vbo.AddTriangle(startIndex + 2, startIndex + 3, startIndex + 0);
+
+ startChar = currentChar + 1;
+ currentLineIndex++;
+
+ if (currentLineIndex < textInfo.lineCount)
+ nextLineStartIdx = textInfo.lineInfo[currentLineIndex].lastCharacterIndex;
+ }
+ currentChar++;
+ }
+
+ // Scrollbar should be updated.
+ m_IsScrollbarUpdateRequired = true;
+
+ //#if TMP_DEBUG_MODE
+ // Debug.Log("Text selection updated at frame: " + Time.frameCount);
+ //#endif
+ }
+
+
+ /// <summary>
+ ///
+ /// </summary>
+ /// <param name="startPosition"></param>
+ /// <param name="height"></param>
+ /// <param name="isCharVisible"></param>
+ private void AdjustRectTransformRelativeToViewport(Vector2 startPosition, float height, bool isCharVisible)
+ {
+ //Debug.Log("Adjusting transform position relative to viewport.");
+
+ if (m_TextViewport == null || m_IsDrivenByLayoutComponents)
+ return;
+
+ float viewportMin = m_TextViewport.rect.xMin;
+ float viewportMax = m_TextViewport.rect.xMax;
+
+ //Debug.Log("Viewport Rect: " + viewportMax + " Start Position: " + startPosition);
+ // Adjust the position of the RectTransform based on the caret position in the viewport.
+ float rightOffset = viewportMax - (m_TextComponent.rectTransform.anchoredPosition.x + startPosition.x + m_TextComponent.margin.z + m_CaretWidth);
+ if (rightOffset < 0f)
+ {
+ if (!multiLine || (multiLine && isCharVisible))
+ {
+ //Debug.Log("Shifting text to the right by " + rightOffset.ToString("f3"));
+ m_TextComponent.rectTransform.anchoredPosition += new Vector2(rightOffset, 0);
+
+ AssignPositioningIfNeeded();
+ }
+ }
+
+ float leftOffset = (m_TextComponent.rectTransform.anchoredPosition.x + startPosition.x - m_TextComponent.margin.x) - viewportMin;
+ if (leftOffset < 0f)
+ {
+ //Debug.Log("Shifting text to the left by " + leftOffset.ToString("f3"));
+ m_TextComponent.rectTransform.anchoredPosition += new Vector2(-leftOffset, 0);
+ AssignPositioningIfNeeded();
+ }
+
+
+ // Adjust text area up or down if not in single line mode.
+ if (m_LineType != LineType.SingleLine)
+ {
+ float topOffset = m_TextViewport.rect.yMax - (m_TextComponent.rectTransform.anchoredPosition.y + startPosition.y + height);
+ if (topOffset < -0.0001f)
+ {
+ m_TextComponent.rectTransform.anchoredPosition += new Vector2(0, topOffset);
+ AssignPositioningIfNeeded();
+ m_IsScrollbarUpdateRequired = true;
+ }
+
+ float bottomOffset = (m_TextComponent.rectTransform.anchoredPosition.y + startPosition.y) - m_TextViewport.rect.yMin;
+ if (bottomOffset < 0f)
+ {
+ m_TextComponent.rectTransform.anchoredPosition -= new Vector2(0, bottomOffset);
+ AssignPositioningIfNeeded();
+ m_IsScrollbarUpdateRequired = true;
+ }
+ }
+
+ // Special handling of backspace
+ if (m_isLastKeyBackspace)
+ {
+ float firstCharPosition = m_TextComponent.rectTransform.anchoredPosition.x + m_TextComponent.textInfo.characterInfo[0].origin - m_TextComponent.margin.x;
+ float lastCharPosition = m_TextComponent.rectTransform.anchoredPosition.x + m_TextComponent.textInfo.characterInfo[m_TextComponent.textInfo.characterCount - 1].origin + m_TextComponent.margin.z;
+
+ // Check if caret is at the left most position of the viewport
+ if (m_TextComponent.rectTransform.anchoredPosition.x + startPosition.x <= viewportMin + 0.0001f)
+ {
+ if (firstCharPosition < viewportMin)
+ {
+ float offset = Mathf.Min((viewportMax - viewportMin) / 2, viewportMin - firstCharPosition);
+ m_TextComponent.rectTransform.anchoredPosition += new Vector2(offset, 0);
+ AssignPositioningIfNeeded();
+ }
+ }
+ else if (lastCharPosition < viewportMax && firstCharPosition < viewportMin)
+ {
+ float offset = Mathf.Min(viewportMax - lastCharPosition, viewportMin - firstCharPosition);
+
+ m_TextComponent.rectTransform.anchoredPosition += new Vector2(offset, 0);
+ AssignPositioningIfNeeded();
+ }
+
+ m_isLastKeyBackspace = false;
+ }
+
+ m_forceRectTransformAdjustment = false;
+ }
+
+ /// <summary>
+ /// Validate the specified input.
+ /// </summary>
+ protected char Validate(string text, int pos, char ch)
+ {
+ // Validation is disabled
+ if (characterValidation == CharacterValidation.None || !enabled)
+ return ch;
+
+ if (characterValidation == CharacterValidation.Integer || characterValidation == CharacterValidation.Decimal)
+ {
+ // Integer and decimal
+ bool cursorBeforeDash = (pos == 0 && text.Length > 0 && text[0] == '-');
+ bool selectionAtStart = stringPositionInternal == 0 || stringSelectPositionInternal == 0;
+ if (!cursorBeforeDash)
+ {
+ if (ch >= '0' && ch <= '9') return ch;
+ if (ch == '-' && (pos == 0 || selectionAtStart)) return ch;
+ if (ch == '.' && characterValidation == CharacterValidation.Decimal && !text.Contains(".")) return ch;
+ }
+ }
+ else if (characterValidation == CharacterValidation.Digit)
+ {
+ if (ch >= '0' && ch <= '9') return ch;
+ }
+ else if (characterValidation == CharacterValidation.Alphanumeric)
+ {
+ // All alphanumeric characters
+ if (ch >= 'A' && ch <= 'Z') return ch;
+ if (ch >= 'a' && ch <= 'z') return ch;
+ if (ch >= '0' && ch <= '9') return ch;
+ }
+ else if (characterValidation == CharacterValidation.Name)
+ {
+ char lastChar = (text.Length > 0) ? text[Mathf.Clamp(pos, 0, text.Length - 1)] : ' ';
+ char nextChar = (text.Length > 0) ? text[Mathf.Clamp(pos + 1, 0, text.Length - 1)] : '\n';
+
+ if (char.IsLetter(ch))
+ {
+ // Space followed by a letter -- make sure it's capitalized
+ if (char.IsLower(ch) && lastChar == ' ')
+ return char.ToUpper(ch);
+
+ // Uppercase letters are only allowed after spaces (and apostrophes)
+ if (char.IsUpper(ch) && lastChar != ' ' && lastChar != '\'')
+ return char.ToLower(ch);
+
+ // If character was already in correct case, return it as-is.
+ // Also, letters that are neither upper nor lower case are always allowed.
+ return ch;
+ }
+ else if (ch == '\'')
+ {
+ // Don't allow more than one apostrophe
+ if (lastChar != ' ' && lastChar != '\'' && nextChar != '\'' && !text.Contains("'"))
+ return ch;
+ }
+ else if (ch == ' ')
+ {
+ // Don't allow more than one space in a row
+ if (lastChar != ' ' && lastChar != '\'' && nextChar != ' ' && nextChar != '\'')
+ return ch;
+ }
+ }
+ else if (characterValidation == CharacterValidation.EmailAddress)
+ {
+ // From StackOverflow about allowed characters in email addresses:
+ // Uppercase and lowercase English letters (a-z, A-Z)
+ // Digits 0 to 9
+ // Characters ! # $ % & ' * + - / = ? ^ _ ` { | } ~
+ // Character . (dot, period, full stop) provided that it is not the first or last character,
+ // and provided also that it does not appear two or more times consecutively.
+
+ if (ch >= 'A' && ch <= 'Z') return ch;
+ if (ch >= 'a' && ch <= 'z') return ch;
+ if (ch >= '0' && ch <= '9') return ch;
+ if (ch == '@' && text.IndexOf('@') == -1) return ch;
+ if (kEmailSpecialCharacters.IndexOf(ch) != -1) return ch;
+ if (ch == '.')
+ {
+ char lastChar = (text.Length > 0) ? text[Mathf.Clamp(pos, 0, text.Length - 1)] : ' ';
+ char nextChar = (text.Length > 0) ? text[Mathf.Clamp(pos + 1, 0, text.Length - 1)] : '\n';
+ if (lastChar != '.' && nextChar != '.')
+ return ch;
+ }
+ }
+ else if (characterValidation == CharacterValidation.Regex)
+ {
+ // Regex expression
+ if (Regex.IsMatch(ch.ToString(), m_RegexValue))
+ {
+ return ch;
+ }
+ }
+ else if (characterValidation == CharacterValidation.CustomValidator)
+ {
+ if (m_InputValidator != null)
+ {
+ char c = m_InputValidator.Validate(ref text, ref pos, ch);
+ m_Text = text;
+ stringSelectPositionInternal = stringPositionInternal = pos;
+ return c;
+ }
+ }
+ return (char)0;
+ }
+
+ public void ActivateInputField()
+ {
+ if (m_TextComponent == null || m_TextComponent.font == null || !IsActive() || !IsInteractable())
+ return;
+
+ if (isFocused)
+ {
+ if (m_SoftKeyboard != null && !m_SoftKeyboard.active)
+ {
+ m_SoftKeyboard.active = true;
+ m_SoftKeyboard.text = m_Text;
+ }
+ }
+
+ m_ShouldActivateNextUpdate = true;
+ }
+
+ private void ActivateInputFieldInternal()
+ {
+ if (EventSystem.current == null)
+ return;
+
+ if (EventSystem.current.currentSelectedGameObject != gameObject)
+ EventSystem.current.SetSelectedGameObject(gameObject);
+
+ if (TouchScreenKeyboard.isSupported && shouldHideSoftKeyboard == false)
+ {
+ if (inputSystem.touchSupported)
+ {
+ TouchScreenKeyboard.hideInput = shouldHideMobileInput;
+ }
+
+ if (shouldHideSoftKeyboard == false && m_ReadOnly == false && contentType != ContentType.Custom)
+ {
+ m_SoftKeyboard = (inputType == InputType.Password) ?
+ TouchScreenKeyboard.Open(m_Text, keyboardType, false, multiLine, true, false, "", characterLimit) :
+ TouchScreenKeyboard.Open(m_Text, keyboardType, inputType == InputType.AutoCorrect, multiLine, false, false, "", characterLimit);
+
+ if (shouldHideMobileInput == false)
+ {
+ MoveTextEnd(false);
+ }
+ else
+ {
+ OnFocus();
+
+ // Opening the soft keyboard sets its selection to the end of the text.
+ // As such, we set the selection to match the Input Field's internal selection.
+ if (m_SoftKeyboard != null)
+ {
+ int length = stringPositionInternal < stringSelectPositionInternal ? stringSelectPositionInternal - stringPositionInternal : stringPositionInternal - stringSelectPositionInternal;
+ m_SoftKeyboard.selection = new RangeInt(stringPositionInternal < stringSelectPositionInternal ? stringPositionInternal : stringSelectPositionInternal, length);
+ }
+ }
+ }
+
+ // Cache the value of isInPlaceEditingAllowed, because on UWP this involves calling into native code
+ // The value only needs to be updated once when the TouchKeyboard is opened.
+ #if UNITY_2019_1_OR_NEWER
+ m_TouchKeyboardAllowsInPlaceEditing = TouchScreenKeyboard.isInPlaceEditingAllowed;
+ #endif
+ }
+ else
+ {
+ if (!TouchScreenKeyboard.isSupported)
+ inputSystem.imeCompositionMode = IMECompositionMode.On;
+
+ OnFocus();
+ }
+
+ m_AllowInput = true;
+ m_OriginalText = text;
+ m_WasCanceled = false;
+ SetCaretVisible();
+ UpdateLabel();
+ }
+
+ public override void OnSelect(BaseEventData eventData)
+ {
+ //Debug.Log("OnSelect()");
+
+ base.OnSelect(eventData);
+ SendOnFocus();
+
+ ActivateInputField();
+ }
+
+ public virtual void OnPointerClick(PointerEventData eventData)
+ {
+ //Debug.Log("Pointer Click Event...");
+
+ if (eventData.button != PointerEventData.InputButton.Left)
+ return;
+
+ ActivateInputField();
+ }
+
+ public void OnControlClick()
+ {
+ //Debug.Log("Input Field control click...");
+ }
+
+ public void ReleaseSelection()
+ {
+ m_SelectionStillActive = false;
+ MarkGeometryAsDirty();
+ }
+
+ public void DeactivateInputField(bool clearSelection = false)
+ {
+ //Debug.Log("Deactivate Input Field...");
+
+ // Not activated do nothing.
+ if (!m_AllowInput)
+ return;
+
+ m_HasDoneFocusTransition = false;
+ m_AllowInput = false;
+
+ if (m_Placeholder != null)
+ m_Placeholder.enabled = string.IsNullOrEmpty(m_Text);
+
+ if (m_TextComponent != null && IsInteractable())
+ {
+ if (m_WasCanceled && m_RestoreOriginalTextOnEscape)
+ text = m_OriginalText;
+
+ if (m_SoftKeyboard != null)
+ {
+ m_SoftKeyboard.active = false;
+ m_SoftKeyboard = null;
+ }
+
+ m_SelectionStillActive = true;
+
+ if (m_ResetOnDeActivation || m_ReleaseSelection)
+ {
+ //m_StringPosition = m_StringSelectPosition = 0;
+ //m_CaretPosition = m_CaretSelectPosition = 0;
+ //m_TextComponent.rectTransform.localPosition = m_DefaultTransformPosition;
+
+ //if (caretRectTrans != null)
+ // caretRectTrans.localPosition = Vector3.zero;
+
+ m_SelectionStillActive = false;
+ m_ReleaseSelection = false;
+ m_SelectedObject = null;
+ }
+
+ SendOnEndEdit();
+ SendOnEndTextSelection();
+
+ inputSystem.imeCompositionMode = IMECompositionMode.Auto;
+ }
+
+ MarkGeometryAsDirty();
+
+ // Scrollbar should be updated.
+ m_IsScrollbarUpdateRequired = true;
+ }
+
+ public override void OnDeselect(BaseEventData eventData)
+ {
+ DeactivateInputField();
+
+ base.OnDeselect(eventData);
+ SendOnFocusLost();
+ }
+
+ public virtual void OnSubmit(BaseEventData eventData)
+ {
+ //Debug.Log("OnSubmit()");
+
+ if (!IsActive() || !IsInteractable())
+ return;
+
+ if (!isFocused)
+ m_ShouldActivateNextUpdate = true;
+
+ SendOnSubmit();
+ }
+
+ //public virtual void OnLostFocus(BaseEventData eventData)
+ //{
+ // if (!IsActive() || !IsInteractable())
+ // return;
+ //}
+
+ private void EnforceContentType()
+ {
+ switch (contentType)
+ {
+ case ContentType.Standard:
+ {
+ // Don't enforce line type for this content type.
+ m_InputType = InputType.Standard;
+ m_KeyboardType = TouchScreenKeyboardType.Default;
+ m_CharacterValidation = CharacterValidation.None;
+ break;
+ }
+ case ContentType.Autocorrected:
+ {
+ // Don't enforce line type for this content type.
+ m_InputType = InputType.AutoCorrect;
+ m_KeyboardType = TouchScreenKeyboardType.Default;
+ m_CharacterValidation = CharacterValidation.None;
+ break;
+ }
+ case ContentType.IntegerNumber:
+ {
+ m_LineType = LineType.SingleLine;
+ m_InputType = InputType.Standard;
+ m_KeyboardType = TouchScreenKeyboardType.NumberPad;
+ m_CharacterValidation = CharacterValidation.Integer;
+ break;
+ }
+ case ContentType.DecimalNumber:
+ {
+ m_LineType = LineType.SingleLine;
+ m_InputType = InputType.Standard;
+ m_KeyboardType = TouchScreenKeyboardType.NumbersAndPunctuation;
+ m_CharacterValidation = CharacterValidation.Decimal;
+ break;
+ }
+ case ContentType.Alphanumeric:
+ {
+ m_LineType = LineType.SingleLine;
+ m_InputType = InputType.Standard;
+ m_KeyboardType = TouchScreenKeyboardType.ASCIICapable;
+ m_CharacterValidation = CharacterValidation.Alphanumeric;
+ break;
+ }
+ case ContentType.Name:
+ {
+ m_LineType = LineType.SingleLine;
+ m_InputType = InputType.Standard;
+ m_KeyboardType = TouchScreenKeyboardType.Default;
+ m_CharacterValidation = CharacterValidation.Name;
+ break;
+ }
+ case ContentType.EmailAddress:
+ {
+ m_LineType = LineType.SingleLine;
+ m_InputType = InputType.Standard;
+ m_KeyboardType = TouchScreenKeyboardType.EmailAddress;
+ m_CharacterValidation = CharacterValidation.EmailAddress;
+ break;
+ }
+ case ContentType.Password:
+ {
+ m_LineType = LineType.SingleLine;
+ m_InputType = InputType.Password;
+ m_KeyboardType = TouchScreenKeyboardType.Default;
+ m_CharacterValidation = CharacterValidation.None;
+ break;
+ }
+ case ContentType.Pin:
+ {
+ m_LineType = LineType.SingleLine;
+ m_InputType = InputType.Password;
+ m_KeyboardType = TouchScreenKeyboardType.NumberPad;
+ m_CharacterValidation = CharacterValidation.Digit;
+ break;
+ }
+ default:
+ {
+ // Includes Custom type. Nothing should be enforced.
+ break;
+ }
+ }
+
+ SetTextComponentWrapMode();
+ }
+
+ void SetTextComponentWrapMode()
+ {
+ if (m_TextComponent == null)
+ return;
+
+ if (multiLine)
+ m_TextComponent.enableWordWrapping = true;
+ else
+ m_TextComponent.enableWordWrapping = false;
+ }
+
+ // Control Rich Text option on the text component.
+ void SetTextComponentRichTextMode()
+ {
+ if (m_TextComponent == null)
+ return;
+
+ m_TextComponent.richText = m_RichText;
+ }
+
+ void SetToCustomIfContentTypeIsNot(params ContentType[] allowedContentTypes)
+ {
+ if (contentType == ContentType.Custom)
+ return;
+
+ for (int i = 0; i < allowedContentTypes.Length; i++)
+ if (contentType == allowedContentTypes[i])
+ return;
+
+ contentType = ContentType.Custom;
+ }
+
+ void SetToCustom()
+ {
+ if (contentType == ContentType.Custom)
+ return;
+
+ contentType = ContentType.Custom;
+ }
+
+ void SetToCustom(CharacterValidation characterValidation)
+ {
+ if (contentType == ContentType.Custom)
+ {
+ characterValidation = CharacterValidation.CustomValidator;
+ return;
+ }
+
+ contentType = ContentType.Custom;
+ characterValidation = CharacterValidation.CustomValidator;
+ }
+
+
+ protected override void DoStateTransition(SelectionState state, bool instant)
+ {
+ if (m_HasDoneFocusTransition)
+ state = SelectionState.Highlighted;
+ else if (state == SelectionState.Pressed)
+ m_HasDoneFocusTransition = true;
+
+ base.DoStateTransition(state, instant);
+ }
+
+
+ /// <summary>
+ /// See ILayoutElement.CalculateLayoutInputHorizontal.
+ /// </summary>
+ public virtual void CalculateLayoutInputHorizontal()
+ { }
+
+ /// <summary>
+ /// See ILayoutElement.CalculateLayoutInputVertical.
+ /// </summary>
+ public virtual void CalculateLayoutInputVertical()
+ { }
+
+ /// <summary>
+ /// See ILayoutElement.minWidth.
+ /// </summary>
+ public virtual float minWidth { get { return 0; } }
+
+ /// <summary>
+ /// Get the displayed with of all input characters.
+ /// </summary>
+ public virtual float preferredWidth
+ {
+ get
+ {
+ if (textComponent == null)
+ return 0;
+
+ return m_TextComponent.preferredWidth + 16 + m_CaretWidth + 1;
+ }
+ }
+
+ /// <summary>
+ /// See ILayoutElement.flexibleWidth.
+ /// </summary>
+ public virtual float flexibleWidth { get { return -1; } }
+
+ /// <summary>
+ /// See ILayoutElement.minHeight.
+ /// </summary>
+ public virtual float minHeight { get { return 0; } }
+
+ /// <summary>
+ /// Get the height of all the text if constrained to the height of the RectTransform.
+ /// </summary>
+ public virtual float preferredHeight
+ {
+ get
+ {
+ if (textComponent == null)
+ return 0;
+
+ return m_TextComponent.preferredHeight + 16;
+ }
+ }
+
+ /// <summary>
+ /// See ILayoutElement.flexibleHeight.
+ /// </summary>
+ public virtual float flexibleHeight { get { return -1; } }
+
+ /// <summary>
+ /// See ILayoutElement.layoutPriority.
+ /// </summary>
+ public virtual int layoutPriority { get { return 1; } }
+
+
+ /// <summary>
+ /// Function to conveniently set the point size of both Placeholder and Input Field text object.
+ /// </summary>
+ /// <param name="pointSize"></param>
+ public void SetGlobalPointSize(float pointSize)
+ {
+ TMP_Text placeholderTextComponent = m_Placeholder as TMP_Text;
+
+ if (placeholderTextComponent != null) placeholderTextComponent.fontSize = pointSize;
+ textComponent.fontSize = pointSize;
+ }
+
+ /// <summary>
+ /// Function to conveniently set the Font Asset of both Placeholder and Input Field text object.
+ /// </summary>
+ /// <param name="fontAsset"></param>
+ public void SetGlobalFontAsset(TMP_FontAsset fontAsset)
+ {
+ TMP_Text placeholderTextComponent = m_Placeholder as TMP_Text;
+
+ if (placeholderTextComponent != null) placeholderTextComponent.font = fontAsset;
+ textComponent.font = fontAsset;
+
+ }
+
+ }
+
+
+ static class SetPropertyUtility
+ {
+ public static bool SetColor(ref Color currentValue, Color newValue)
+ {
+ if (currentValue.r == newValue.r && currentValue.g == newValue.g && currentValue.b == newValue.b && currentValue.a == newValue.a)
+ return false;
+
+ currentValue = newValue;
+ return true;
+ }
+
+ public static bool SetEquatableStruct<T>(ref T currentValue, T newValue) where T : IEquatable<T>
+ {
+ if (currentValue.Equals(newValue))
+ return false;
+
+ currentValue = newValue;
+ return true;
+ }
+
+ public static bool SetStruct<T>(ref T currentValue, T newValue) where T : struct
+ {
+ if (currentValue.Equals(newValue))
+ return false;
+
+ currentValue = newValue;
+ return true;
+ }
+
+ public static bool SetClass<T>(ref T currentValue, T newValue) where T : class
+ {
+ if ((currentValue == null && newValue == null) || (currentValue != null && currentValue.Equals(newValue)))
+ return false;
+
+ currentValue = newValue;
+ return true;
+ }
+ }
+} \ No newline at end of file
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_InputField.cs.meta b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_InputField.cs.meta
new file mode 100644
index 0000000..8be98c6
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_InputField.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 2da0c512f12947e489f739169773d7ca
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {fileID: 2800000, guid: 3ee40aa79cd242a5b53b0b0ca4f13f0f, type: 3}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_InputValidator.cs b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_InputValidator.cs
new file mode 100644
index 0000000..ac0d088
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_InputValidator.cs
@@ -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);
+ }
+}
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_InputValidator.cs.meta b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_InputValidator.cs.meta
new file mode 100644
index 0000000..8967aec
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_InputValidator.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 79ff392d1bde4ad78a3836a4a480392d
+timeCreated: 1473021069
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_LineInfo.cs b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_LineInfo.cs
new file mode 100644
index 0000000..6f90157
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_LineInfo.cs
@@ -0,0 +1,52 @@
+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 TextAlignmentOptions 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;
+ //}
+ }
+} \ No newline at end of file
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_LineInfo.cs.meta b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_LineInfo.cs.meta
new file mode 100644
index 0000000..0ae59a8
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_LineInfo.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: b6e75d7f429a4e7e9e1ffb4f85cff49f
+timeCreated: 1464310403
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_ListPool.cs b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_ListPool.cs
new file mode 100644
index 0000000..0220af2
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_ListPool.cs
@@ -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);
+ }
+ }
+} \ No newline at end of file
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_ListPool.cs.meta b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_ListPool.cs.meta
new file mode 100644
index 0000000..9e7d0fa
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_ListPool.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 28375447bcea455c9b51a6650b10c9d7
+timeCreated: 1458521386
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_MaterialManager.cs b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_MaterialManager.cs
new file mode 100644
index 0000000..a000975
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_MaterialManager.cs
@@ -0,0 +1,626 @@
+//#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()
+ {
+ Camera.onPreRender += new Camera.CameraCallback(OnPreRender);
+ Canvas.willRenderCanvases += new Canvas.WillRenderCanvases(OnPreRenderCanvas);
+ }
+
+
+ static void OnPreRender(Camera cam)
+ {
+ if (isFallbackListDirty)
+ {
+ //Debug.Log("1 - Cleaning up Fallback Materials.");
+ CleanupFallbackMaterials();
+ isFallbackListDirty = false;
+ }
+ }
+
+ static void OnPreRenderCanvas()
+ {
+ 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.RemoveAt(i);
+ }
+ }
+
+
+ /// <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;
+ }
+
+
+ /// <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;
+
+ if (m_fallbackMaterials.TryGetValue(key, out FallbackMaterial fallback))
+ {
+ //Debug.Log("Material [" + fallback.fallbackMaterial.name + "] already exists.");
+ return fallback.fallbackMaterial;
+ }
+
+ // Create new material from the source material and copy properties if using distance field shaders.
+ Material fallbackMaterial = null;
+ 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.baseID = sourceID;
+ fallback.baseMaterial = sourceMaterial;
+ fallback.fallbackID = key;
+ 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();
+
+ // Lookup key to retrieve
+ if (m_fallbackMaterialLookup.TryGetValue(sourceID, out long key))
+ {
+ if (m_fallbackMaterials.TryGetValue(key, out FallbackMaterial 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();
+
+ // Lookup key to retrieve
+ if (m_fallbackMaterialLookup.TryGetValue(sourceID, out long key))
+ {
+ if (m_fallbackMaterials.TryGetValue(key, out FallbackMaterial 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="fallackMaterial"></param>
+ public static void ReleaseFallbackMaterial(Material fallackMaterial)
+ {
+ if (fallackMaterial == null) return;
+
+ int materialID = fallackMaterial.GetInstanceID();
+
+ if (m_fallbackMaterialLookup.TryGetValue(materialID, out long key))
+ {
+ if (m_fallbackMaterials.TryGetValue(key, out FallbackMaterial 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 int baseID;
+ public Material baseMaterial;
+ public long fallbackID;
+ 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_fallbackMaterialList.Count() == 0)
+ {
+ Debug.Log("Material List is empty.");
+ return;
+ }
+
+ Debug.Log("List contains " + m_fallbackMaterialList.Count() + " items.");
+
+ for (int i = 0; i < m_fallbackMaterialList.Count(); i++)
+ {
+ Material baseMaterial = m_fallbackMaterialList[i].baseMaterial;
+ Material fallbackMaterial = m_fallbackMaterialList[i].fallbackMaterial;
+
+ Debug.Log("Item #" + (i + 1) + " - Base Material is [" + baseMaterial.name + "] with ID " + baseMaterial.GetInstanceID() + " is associated with [" + (fallbackMaterial != null ? fallbackMaterial.name : "Null") + "] with ID " + (fallbackMaterial != null ? fallbackMaterial.GetInstanceID() : 0) + " and is referenced " + m_fallbackMaterialList[i].count + " time(s).");
+ }
+ }
+ #endif
+ }
+
+}
+
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_MaterialManager.cs.meta b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_MaterialManager.cs.meta
new file mode 100644
index 0000000..5a011d5
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_MaterialManager.cs.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: 6d9df2bc198c417db00037803568139c
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_MeshInfo.cs b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_MeshInfo.cs
new file mode 100644
index 0000000..67f8615
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_MeshInfo.cs
@@ -0,0 +1,668 @@
+using UnityEngine;
+using System;
+using System.Linq;
+using System.Collections;
+using System.Collections.Generic;
+
+
+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;
+
+
+ /// <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;
+ }
+
+
+ /// <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;
+ }
+
+
+ /// <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)
+ {
+ // Limit the mesh to less than 65535 vertices which is the limit for Unity's Mesh.
+ 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;
+ //}
+
+ }
+}
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_MeshInfo.cs.meta b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_MeshInfo.cs.meta
new file mode 100644
index 0000000..092c4c2
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_MeshInfo.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: e21bec35f48a44298911b25ead550ce3
+timeCreated: 1462398762
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_ObjectPool.cs b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_ObjectPool.cs
new file mode 100644
index 0000000..2340c39
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_ObjectPool.cs
@@ -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);
+ }
+ }
+} \ No newline at end of file
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_ObjectPool.cs.meta b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_ObjectPool.cs.meta
new file mode 100644
index 0000000..112c3a4
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_ObjectPool.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: e69259f6ff914146ad610be5491eb44a
+timeCreated: 1458521389
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_PackageResourceImporter.cs b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_PackageResourceImporter.cs
new file mode 100644
index 0000000..88a49e1
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_PackageResourceImporter.cs
@@ -0,0 +1,212 @@
+#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 = Directory.Exists("Assets/TextMesh Pro");
+ 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;
+
+ var 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;
+ }
+
+ 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;
+ }
+ }
+
+ public class TMP_PackageResourceImporterWindow : EditorWindow
+ {
+ [SerializeField]
+ TMP_PackageResourceImporter m_ResourceImporter;
+
+ public static void ShowPackageImporterWindow()
+ {
+ var window = GetWindow<TMP_PackageResourceImporterWindow>();
+ window.titleContent = new GUIContent("TMP Importer");
+ window.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
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_PackageResourceImporter.cs.meta b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_PackageResourceImporter.cs.meta
new file mode 100644
index 0000000..c7313b9
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_PackageResourceImporter.cs.meta
@@ -0,0 +1,16 @@
+fileFormatVersion: 2
+guid: cf1fe50a641faac4691bf49eb32ce333
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences:
+ - m_PersistentViewDataDictionary: {instanceID: 0}
+ - LightSkin: {fileID: 11400000, guid: 0c156a7b2f4d450da1716b1625b5441d, type: 2}
+ - DarkSkin: {fileID: 11400000, guid: 9d345c3252c147c89e8b61a249a46a9d, type: 2}
+ - TMPEssentials: {fileID: 102900000, guid: ce4ff17ca867d2b48b5c8a4181611901, type: 3}
+ - TMPExamples: {fileID: 102900000, guid: bc00e25696e4132499f56528d3fed2e3, type: 3}
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_RichTextTagStack.cs b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_RichTextTagStack.cs
new file mode 100644
index 0000000..7272660
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_RichTextTagStack.cs
@@ -0,0 +1,278 @@
+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.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.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>
+ public struct TMP_RichTextTagStack<T>
+ {
+ public T[] m_ItemStack;
+ public int m_Index;
+ private int m_Capacity;
+
+ private T m_DefaultItem;
+
+ private const int k_DefaultCapacity = 4;
+ //static readonly T[] m_EmptyStack = new T[0];
+
+ /// <summary>
+ /// Constructor to create a new item stack.
+ /// </summary>
+ /// <param name="tagStack"></param>
+ public TMP_RichTextTagStack(T[] tagStack)
+ {
+ m_ItemStack = tagStack;
+ m_Capacity = tagStack.Length;
+ m_Index = 0;
+
+ m_DefaultItem = default(T);
+ }
+
+ /// <summary>
+ /// Constructor for a new item stack with the given capacity.
+ /// </summary>
+ /// <param name="capacity"></param>
+ public TMP_RichTextTagStack(int capacity)
+ {
+ m_ItemStack = new T[capacity];
+ m_Capacity = capacity;
+ m_Index = 0;
+
+ m_DefaultItem = default(T);
+ }
+
+
+ /// <summary>
+ /// Function to clear and reset stack to first item.
+ /// </summary>
+ public void Clear()
+ {
+ m_Index = 0;
+ }
+
+
+ /// <summary>
+ /// Function to set the first item on the stack and reset index.
+ /// </summary>
+ /// <param name="item"></param>
+ public void SetDefault(T item)
+ {
+ m_ItemStack[0] = item;
+ m_Index = 1;
+ }
+
+
+ /// <summary>
+ /// Function to add a new item to the stack.
+ /// </summary>
+ /// <param name="item"></param>
+ public void Add(T item)
+ {
+ if (m_Index < m_ItemStack.Length)
+ {
+ m_ItemStack[m_Index] = item;
+ m_Index += 1;
+ }
+ }
+
+
+ /// <summary>
+ /// Function to retrieve an item from the stack.
+ /// </summary>
+ /// <returns></returns>
+ public T Remove()
+ {
+ m_Index -= 1;
+
+ if (m_Index <= 0)
+ {
+ m_Index = 1;
+ return m_ItemStack[0];
+
+ }
+
+ return m_ItemStack[m_Index - 1];
+ }
+
+ public void Push(T item)
+ {
+ if (m_Index == m_Capacity)
+ {
+ m_Capacity *= 2;
+ if (m_Capacity == 0)
+ m_Capacity = k_DefaultCapacity;
+
+ System.Array.Resize(ref m_ItemStack, m_Capacity);
+ }
+
+ m_ItemStack[m_Index] = item;
+ m_Index += 1;
+ }
+
+ public T Pop()
+ {
+ if (m_Index == 0)
+ return default(T);
+
+ m_Index -= 1;
+ T item = m_ItemStack[m_Index];
+ m_ItemStack[m_Index] = m_DefaultItem;
+
+ return item;
+ }
+
+ /// <summary>
+ ///
+ /// </summary>
+ /// <returns></returns>
+ public T Peek()
+ {
+ if (m_Index == 0)
+ return m_DefaultItem;
+
+ return m_ItemStack[m_Index - 1];
+ }
+
+
+ /// <summary>
+ /// Function to retrieve the current item from the stack.
+ /// </summary>
+ /// <returns>itemStack <T></returns>
+ public T CurrentItem()
+ {
+ if (m_Index > 0)
+ return m_ItemStack[m_Index - 1];
+
+ return m_ItemStack[0];
+ }
+
+
+ /// <summary>
+ /// Function to retrieve the previous item without affecting the stack.
+ /// </summary>
+ /// <returns></returns>
+ public T PreviousItem()
+ {
+ if (m_Index > 1)
+ return m_ItemStack[m_Index - 2];
+
+ return m_ItemStack[0];
+ }
+ }
+}
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_RichTextTagStack.cs.meta b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_RichTextTagStack.cs.meta
new file mode 100644
index 0000000..e7cf974
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_RichTextTagStack.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 20a9b557a46149dfbfa04a3a7080f5aa
+timeCreated: 1448242247
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_RichTextTagsCommon.cs b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_RichTextTagsCommon.cs
new file mode 100644
index 0000000..f63676e
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_RichTextTagsCommon.cs
@@ -0,0 +1,113 @@
+using System;
+using UnityEngine.Bindings;
+
+
+namespace TMPro
+{
+ /// <summary>
+ /// Rich Text Tags and Attribute definitions and their respective HashCode values.
+ /// </summary>
+ enum RichTextTag : uint
+ {
+ // 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>
+ COLOR = 81999901, // <color>
+ SLASH_COLOR = 1909026194, // </color>
+ SIZE = 3061285, // <size>
+ SLASH_SIZE = 58429962, // </size>
+ SPRITE = 3303439849, // <sprite>
+ BR = 2256, // <br>
+ STYLE = 100252951, // <style>
+ SLASH_STYLE = 1927738392, // </style>
+ FONT = 2586451, // <font>
+ SLASH_FONT = 57747708, // </font>
+ LINK = 2656128, // <link>
+ SLASH_LINK = 57686191, // </link>
+ FONT_WEIGHT = 2405071134, // <font-weight=xxx>
+ SLASH_FONT_WEIGHT = 3536990865, // </font-weight>
+
+
+ // 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">
+
+ // Named Colors
+ RED = 91635,
+ GREEN = 87065851,
+ BLUE = 2457214,
+ YELLOW = 3412522628,
+ ORANGE = 3186379376,
+
+ // Prefix and Unit suffix
+ 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,
+
+ DEFAULT = 3673993291, // <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>
+ enum UnicodeCharacter : uint
+ {
+ HYPHEN_MINUS = 0x2D,
+ SOFT_HYPHEN = 0xAD,
+ HYPHEN = 0x2010,
+ NON_BREAKING_HYPHEN = 0x2011,
+ ZERO_WIDTH_SPACE = 0x200B,
+ RIGHT_SINGLE_QUOTATION = 0x2019,
+ APOSTROPHE = 0x27,
+ WORD_JOINER = 0x2060, // Prohibits line break.
+
+ }
+}
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_RichTextTagsCommon.cs.meta b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_RichTextTagsCommon.cs.meta
new file mode 100644
index 0000000..0f289ed
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_RichTextTagsCommon.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: b458b2c7f196bdc4581b2f9fd6a5d931
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_ScrollbarEventHandler.cs b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_ScrollbarEventHandler.cs
new file mode 100644
index 0000000..98a20e2
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_ScrollbarEventHandler.cs
@@ -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;
+ }
+ }
+}
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_ScrollbarEventHandler.cs.meta b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_ScrollbarEventHandler.cs.meta
new file mode 100644
index 0000000..e6e940d
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_ScrollbarEventHandler.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 34e150112c1c42ac83170b52d898e322
+timeCreated: 1484171293
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SelectionCaret.cs b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SelectionCaret.cs
new file mode 100644
index 0000000..2d3cacf
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SelectionCaret.cs
@@ -0,0 +1,23 @@
+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>
+ 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)
+ {
+ //base.Cull(clipRect, validRect);
+ }
+ }
+}
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SelectionCaret.cs.meta b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SelectionCaret.cs.meta
new file mode 100644
index 0000000..6cbb55e
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SelectionCaret.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: ca819640f53b48919bf7774744f7f15e
+timeCreated: 1477609203
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_Settings.cs b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_Settings.cs
new file mode 100644
index 0000000..8155ea3
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_Settings.cs
@@ -0,0 +1,440 @@
+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
+{
+ [System.Serializable]
+ 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>
+ /// 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>
+ /// 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>
+ /// 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 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>
+ /// 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;
+
+ /// <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 Sprite Asset 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;
+ }
+ }
+}
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_Settings.cs.meta b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_Settings.cs.meta
new file mode 100644
index 0000000..5063b32
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_Settings.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 2705215ac5b84b70bacc50632be6e391
+timeCreated: 1457654851
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_ShaderUtilities.cs b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_ShaderUtilities.cs
new file mode 100644
index 0000000..c47fe3d
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_ShaderUtilities.cs
@@ -0,0 +1,563 @@
+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;
+
+ 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_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_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_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_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_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);
+
+ // 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;
+ }
+
+ 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 = material.GetFloat(ID_UnderlayOffsetX) * scaleRatio_C;
+ float offsetY = material.GetFloat(ID_UnderlayOffsetY) * scaleRatio_C;
+ float dilate = material.GetFloat(ID_UnderlayDilate) * scaleRatio_C;
+ float 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;
+ }
+
+
+ }
+
+}
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_ShaderUtilities.cs.meta b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_ShaderUtilities.cs.meta
new file mode 100644
index 0000000..468b6af
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_ShaderUtilities.cs.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: fea49a0730244a98bf1087f7ca9410a8
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_Sprite.cs b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_Sprite.cs
new file mode 100644
index 0000000..c2ba632
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_Sprite.cs
@@ -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;
+ }
+} \ No newline at end of file
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_Sprite.cs.meta b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_Sprite.cs.meta
new file mode 100644
index 0000000..159afd0
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_Sprite.cs.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: 968a09f153574430a6e15ae975145768
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SpriteAnimator.cs b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SpriteAnimator.cs
new file mode 100644
index 0000000..b349d0a
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SpriteAnimator.cs
@@ -0,0 +1,147 @@
+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)
+ {
+ // Need to add tracking of coroutines that have been lunched for this text object.
+ if (!m_animations.TryGetValue(currentCharacter, out bool 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 geometry of the current character.
+ 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 elapsedTime = 0;
+ float targetTime = 1f / Mathf.Abs(framerate);
+
+ while (true)
+ {
+ if (elapsedTime > targetTime)
+ {
+ elapsedTime = 0;
+
+ // 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.fontAsset.faceInfo.ascentLine / spriteCharacter.glyph.metrics.height * spriteCharacter.scale * charInfo.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;
+ }
+ }
+
+ }
+}
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SpriteAnimator.cs.meta b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SpriteAnimator.cs.meta
new file mode 100644
index 0000000..2cd3c46
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SpriteAnimator.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 6366ee97f6b541449155028b9487355a
+timeCreated: 1471590333
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SpriteAsset.cs b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SpriteAsset.cs
new file mode 100644
index 0000000..985a895
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SpriteAsset.cs
@@ -0,0 +1,503 @@
+using UnityEngine;
+using UnityEngine.TextCore;
+using System.Collections.Generic;
+using System.Linq;
+
+
+namespace TMPro
+{
+
+ public class TMP_SpriteAsset : TMP_Asset
+ {
+ internal Dictionary<uint, int> m_UnicodeLookup;
+ 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;
+
+ // The texture which contains the sprites.
+ public Texture spriteSheet;
+
+ 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>();
+
+
+ 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>();
+
+ // List which contains the SpriteInfo for the sprites contained in the sprite sheet.
+ public List<TMP_Sprite> spriteInfoList;
+
+ /// <summary>
+ /// Dictionary used to lookup the index of a given sprite based on a Unicode value.
+ /// </summary>
+ //private Dictionary<int, int> m_SpriteUnicodeLookup;
+
+
+ /// <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();
+ }
+
+
+ #if UNITY_EDITOR
+ /// <summary>
+ ///
+ /// </summary>
+ void OnValidate()
+ {
+ //Debug.Log("Sprite Asset [" + name + "] has changed.");
+
+ //UpdateLookupTables();
+
+ //TMPro_EventManager.ON_SPRITE_ASSET_PROPERTY_CHANGED(true, this);
+ }
+ #endif
+
+
+ /// <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();
+
+ for (int i = 0; i < m_SpriteGlyphTable.Count; i++)
+ {
+ uint glyphIndex = m_SpriteGlyphTable[i].index;
+
+ if (m_GlyphIndexLookup.ContainsKey(glyphIndex) == false)
+ m_GlyphIndexLookup.Add(glyphIndex, i);
+ }
+
+ if (m_NameLookup == null)
+ m_NameLookup = new Dictionary<int, int>();
+ else
+ m_NameLookup.Clear();
+
+ if (m_UnicodeLookup == null)
+ m_UnicodeLookup = new Dictionary<uint, int>();
+ else
+ m_UnicodeLookup.Clear();
+
+ for (int i = 0; i < m_SpriteCharacterTable.Count; i++)
+ {
+ int nameHashCode = m_SpriteCharacterTable[i].hashCode;
+
+ if (m_NameLookup.ContainsKey(nameHashCode) == false)
+ m_NameLookup.Add(nameHashCode, i);
+
+ uint unicode = m_SpriteCharacterTable[i].unicode;
+
+ if (m_UnicodeLookup.ContainsKey(unicode) == false)
+ m_UnicodeLookup.Add(unicode, i);
+
+ // Update glyph reference which is not serialized
+ uint glyphIndex = m_SpriteCharacterTable[i].glyphIndex;
+
+ if (m_GlyphIndexLookup.TryGetValue(glyphIndex, out int index))
+ m_SpriteCharacterTable[i].glyph = m_SpriteGlyphTable[index];
+ }
+
+ 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();
+
+ if (m_NameLookup.TryGetValue(hashCode, out int 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_UnicodeLookup == null)
+ UpdateLookupTables();
+
+ if (m_UnicodeLookup.TryGetValue(unicode, out int index))
+ return index;
+
+ 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 List<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 List<int>();
+
+ 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, includeFallbacks, out spriteIndex);
+
+ // Search default sprite asset potentially assigned in the TMP Settings.
+ if (includeFallbacks && TMP_Settings.defaultSpriteAsset != null)
+ return SearchForSpriteByUnicodeInternal(TMP_Settings.defaultSpriteAsset, unicode, includeFallbacks, 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 over the fallback sprite asset if it has already been searched.
+ if (k_searchedSpriteAssets.Contains(id)) continue;
+
+ // Add to list of font assets already searched.
+ k_searchedSpriteAssets.Add(id);
+
+ 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, includeFallbacks, 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 list to track instance of Sprite Assets that have already been searched.
+ if (k_searchedSpriteAssets == null)
+ k_searchedSpriteAssets = new List<int>();
+
+ k_searchedSpriteAssets.Clear();
+
+ int id = spriteAsset.GetInstanceID();
+ // Add to list of font assets already searched.
+ k_searchedSpriteAssets.Add(id);
+
+ if (includeFallbacks && spriteAsset.fallbackSpriteAssets != null && spriteAsset.fallbackSpriteAssets.Count > 0)
+ return SearchForSpriteByHashCodeInternal(spriteAsset.fallbackSpriteAssets, hashCode, includeFallbacks, out spriteIndex);
+
+ // Search default sprite asset potentially assigned in the TMP Settings.
+ if (includeFallbacks && TMP_Settings.defaultSpriteAsset != null)
+ return SearchForSpriteByHashCodeInternal(TMP_Settings.defaultSpriteAsset, hashCode, includeFallbacks, out spriteIndex);
+
+ 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.GetInstanceID();
+
+ // Skip over the fallback sprite asset if it has already been searched.
+ if (k_searchedSpriteAssets.Contains(id)) continue;
+
+ // Add to list of font assets already searched.
+ k_searchedSpriteAssets.Add(id);
+
+ 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, searchFallbacks, 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((uint)oldSprite.unicode, spriteGlyph);
+ 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
+ }
+
+ }
+}
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SpriteAsset.cs.meta b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SpriteAsset.cs.meta
new file mode 100644
index 0000000..ac79a19
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SpriteAsset.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 84a92b25f83d49b9bc132d206b370281
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {fileID: 2800000, guid: ec7c645d93308c04d8840982af12101e, type: 3}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SpriteAssetImportFormats.cs b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SpriteAssetImportFormats.cs
new file mode 100644
index 0000000..c9dd075
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SpriteAssetImportFormats.cs
@@ -0,0 +1,61 @@
+using UnityEngine;
+using System.Collections;
+using System.Collections.Generic;
+
+
+namespace TMPro.SpriteAssetUtilities
+{
+ public enum SpriteAssetImportFormats { None = 0, TexturePacker = 0x1 };
+
+ public class TexturePacker
+ {
+ [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 SpriteData
+ {
+ public string filename;
+ public SpriteFrame frame;
+ public bool rotated;
+ public bool trimmed;
+ public SpriteFrame spriteSourceSize;
+ public SpriteSize sourceSize;
+ public Vector2 pivot;
+
+ }
+
+ [System.Serializable]
+ public class SpriteDataObject
+ {
+ public List<SpriteData> frames;
+ }
+
+
+ }
+} \ No newline at end of file
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SpriteAssetImportFormats.cs.meta b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SpriteAssetImportFormats.cs.meta
new file mode 100644
index 0000000..f200f4e
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SpriteAssetImportFormats.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: e4e0b1de1aee400d81ed4273141e7823
+timeCreated: 1480042510
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SpriteCharacter.cs b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SpriteCharacter.cs
new file mode 100644
index 0000000..48af7a5
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SpriteCharacter.cs
@@ -0,0 +1,74 @@
+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;
+ }
+
+ }
+} \ No newline at end of file
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SpriteCharacter.cs.meta b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SpriteCharacter.cs.meta
new file mode 100644
index 0000000..7627135
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SpriteCharacter.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 202d758d102b6854a9710c8b93db742c
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SpriteGlyph.cs b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SpriteGlyph.cs
new file mode 100644
index 0000000..c57b84c
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SpriteGlyph.cs
@@ -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;
+ }
+ }
+} \ No newline at end of file
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SpriteGlyph.cs.meta b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SpriteGlyph.cs.meta
new file mode 100644
index 0000000..d3024cd
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SpriteGlyph.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 5b5c6a576605b3c4aab7d27193785f27
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_Style.cs b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_Style.cs
new file mode 100644
index 0000000..180911c
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_Style.cs
@@ -0,0 +1,95 @@
+using UnityEngine;
+using System.Collections;
+
+#pragma warning disable 0649 // Disabled warnings.
+
+namespace TMPro
+{
+
+ [System.Serializable]
+ public class TMP_Style
+ {
+ // 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;
+
+
+ //public TMP_Style()
+ //{
+ //Debug.Log("New Style with Name: " + m_Name + " was created. ID: ");
+ //}
+
+
+ /// <summary>
+ /// Function to update the content of the int[] resulting from changes to OpeningDefinition & ClosingDefinition.
+ /// </summary>
+ public void RefreshStyle()
+ {
+ m_HashCode = TMP_TextUtilities.GetSimpleHashCode(m_Name);
+
+ m_OpeningTagArray = new int[m_OpeningDefinition.Length];
+ for (int i = 0; i < m_OpeningDefinition.Length; i++)
+ m_OpeningTagArray[i] = m_OpeningDefinition[i];
+
+ m_ClosingTagArray = new int[m_ClosingDefinition.Length];
+ for (int i = 0; i < m_ClosingDefinition.Length; i++)
+ m_ClosingTagArray[i] = m_ClosingDefinition[i];
+
+#if UNITY_EDITOR
+ // Event to update objects when styles are changed in the editor.
+ TMPro_EventManager.ON_TEXT_STYLE_PROPERTY_CHANGED(true);
+#endif
+ }
+
+ }
+}
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_Style.cs.meta b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_Style.cs.meta
new file mode 100644
index 0000000..1cb98cd
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_Style.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 30bed781e402439ab8ce4e3357708115
+timeCreated: 1432681409
+licenseType: Store
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_StyleSheet.cs b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_StyleSheet.cs
new file mode 100644
index 0000000..8d02a18
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_StyleSheet.cs
@@ -0,0 +1,131 @@
+using UnityEngine;
+using System;
+using System.Collections.Generic;
+
+
+namespace TMPro
+{
+
+ [Serializable]
+ public class TMP_StyleSheet : ScriptableObject
+ {
+ private static TMP_StyleSheet s_Instance;
+
+ [SerializeField]
+ private List<TMP_Style> m_StyleList = new List<TMP_Style>(1);
+ private Dictionary<int, TMP_Style> m_StyleDictionary = new Dictionary<int, TMP_Style>();
+
+
+ /// <summary>
+ /// Get a singleton instance of the TMP_StyleSheet
+ /// </summary>
+ public static TMP_StyleSheet instance
+ {
+ get
+ {
+ if (s_Instance == null)
+ {
+ s_Instance = TMP_Settings.defaultStyleSheet;
+
+ if (s_Instance == null)
+ s_Instance = Resources.Load<TMP_StyleSheet>("Style Sheets/Default Style Sheet");
+
+ if (s_Instance == null) return null;
+
+ // Load the style dictionary.
+ s_Instance.LoadStyleDictionaryInternal();
+ }
+
+ return s_Instance;
+ }
+ }
+
+
+ /// <summary>
+ /// Static Function to load the Default Style Sheet.
+ /// </summary>
+ /// <returns></returns>
+ public static TMP_StyleSheet LoadDefaultStyleSheet()
+ {
+ return instance;
+ }
+
+
+ /// <summary>
+ /// Function to retrieve the Style matching the HashCode.
+ /// </summary>
+ /// <param name="hashCode"></param>
+ /// <returns></returns>
+ public static TMP_Style GetStyle(int hashCode)
+ {
+ return instance.GetStyleInternal(hashCode);
+ }
+
+
+ /// <summary>
+ /// Internal method to retrieve the Style matching the Hashcode.
+ /// </summary>
+ /// <param name="hashCode"></param>
+ /// <returns></returns>
+ private TMP_Style GetStyleInternal(int hashCode)
+ {
+ if (m_StyleDictionary.TryGetValue(hashCode, out TMP_Style style))
+ {
+ return style;
+ }
+
+ return null;
+ }
+
+
+ public void UpdateStyleDictionaryKey(int old_key, int new_key)
+ {
+ if (m_StyleDictionary.ContainsKey(old_key))
+ {
+ TMP_Style style = m_StyleDictionary[old_key];
+ m_StyleDictionary.Add(new_key, style);
+ m_StyleDictionary.Remove(old_key);
+ }
+ }
+
+
+ /// <summary>
+ /// Function to update the internal reference to a newly assigned style sheet in the TMP Settings.
+ /// </summary>
+ public static void UpdateStyleSheet()
+ {
+ // Reset instance
+ s_Instance = null;
+
+ RefreshStyles();
+ }
+
+
+ /// <summary>
+ /// Function to refresh the Style Dictionary.
+ /// </summary>
+ public static void RefreshStyles()
+ {
+ instance.LoadStyleDictionaryInternal();
+ }
+
+
+ /// <summary>
+ ///
+ /// </summary>
+ private void LoadStyleDictionaryInternal()
+ {
+ m_StyleDictionary.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_StyleDictionary.ContainsKey(m_StyleList[i].hashCode))
+ m_StyleDictionary.Add(m_StyleList[i].hashCode, m_StyleList[i]);
+ }
+ }
+ }
+
+} \ No newline at end of file
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_StyleSheet.cs.meta b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_StyleSheet.cs.meta
new file mode 100644
index 0000000..dbbe867
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_StyleSheet.cs.meta
@@ -0,0 +1,13 @@
+fileFormatVersion: 2
+guid: ab2114bdc8544297b417dfefe9f1e410
+timeCreated: 1436650317
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences:
+ - StyleSheet: {fileID: 11400000, guid: cab1ac28c8e6be24e995befe0c36d7c1, type: 2}
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SubMesh.cs b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SubMesh.cs
new file mode 100644
index 0000000..c9c7648
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SubMesh.cs
@@ -0,0 +1,580 @@
+using UnityEngine;
+using System;
+using System.Collections;
+
+#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))]
+ [RequireComponent(typeof(MeshFilter))]
+ [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>();
+ return m_meshFilter;
+ }
+ }
+ [SerializeField]
+ 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;
+ this.meshFilter.mesh = m_mesh;
+ }
+
+ 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;
+
+ [SerializeField]
+ private TextMeshPro m_TextComponent;
+
+ [NonSerialized]
+ private bool m_isRegisteredForEvents;
+
+
+ void 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;
+ }
+
+ // 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()
+ {
+ // 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()
+ {
+ // 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;
+ }
+
+
+
+ #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 ***");
+ int targetMaterialID = mat.GetInstanceID();
+ int sharedMaterialID = m_sharedMaterial.GetInstanceID();
+ int fallbackSourceMaterialID = m_fallbackSourceMaterial == null ? 0 : m_fallbackSourceMaterial.GetInstanceID();
+
+ // 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_MaterialManager.CopyMaterialPresetProperties(mat, m_fallbackMaterial);
+ else
+ return;
+ }
+
+ if (m_TextComponent == null) m_TextComponent = GetComponentInParent<TextMeshPro>();
+
+ 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, TMP_FontAsset font)
+ {
+ if (m_fontAsset != null && font.GetInstanceID() == m_fontAsset.GetInstanceID())
+ {
+ // Copy Normal and Bold Weight
+ if (m_fallbackMaterial != null)
+ {
+ m_fallbackMaterial.SetFloat(ShaderUtilities.ID_WeightNormal, m_fontAsset.normalStyle);
+ m_fallbackMaterial.SetFloat(ShaderUtilities.ID_WeightBold, m_fontAsset.boldStyle);
+ }
+ }
+ }
+
+ /// <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 static TMP_SubMesh AddSubTextObject(TextMeshPro textComponent, MaterialReference materialReference)
+ {
+ GameObject go = new GameObject("TMP SubMesh [" + materialReference.material.name + "]", typeof(TMP_SubMesh));
+
+ 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_meshFilter = go.GetComponent<MeshFilter>();
+
+ 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;
+ }
+
+
+ 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 (!this.enabled)
+ // return;
+
+ if (m_renderer == null) m_renderer = this.renderer;
+
+ m_renderer.sharedMaterial = m_sharedMaterial;
+
+ #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;
+ //}
+ }
+}
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SubMesh.cs.meta b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SubMesh.cs.meta
new file mode 100644
index 0000000..ffe3eda
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SubMesh.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 07994bfe8b0e4adb97d706de5dea48d5
+timeCreated: 1454709708
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SubMeshUI.cs b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SubMeshUI.cs
new file mode 100644
index 0000000..e678e7d
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SubMeshUI.cs
@@ -0,0 +1,807 @@
+using UnityEngine;
+using UnityEngine.UI;
+using System.Collections;
+using System.Collections.Generic;
+
+#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]
+ public class TMP_SubMeshUI : MaskableGraphic, IClippable, IMaskable, IMaterialModifier
+ {
+ /// <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 Renderer of this text sub object.
+ /// </summary>
+ public new CanvasRenderer canvasRenderer
+ {
+ get { if (m_canvasRenderer == null) m_canvasRenderer = GetComponent<CanvasRenderer>();
+
+ return m_canvasRenderer;
+ }
+ }
+ [SerializeField]
+ private CanvasRenderer m_canvasRenderer;
+
+
+ /// <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;
+
+
+ [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.transform.SetParent(textComponent.transform, false);
+ 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;
+
+ TMP_SubMeshUI subMesh = go.AddComponent<TMP_SubMeshUI>();
+
+ subMesh.m_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;
+ }
+
+ m_ShouldRecalculateStencil = true;
+ RecalculateClipping();
+ RecalculateMasking();
+
+ //SetAllDirty();
+ }
+
+
+ protected override void OnDisable()
+ {
+ //Debug.Log("*** SubObject OnDisable() ***");
+
+ //m_canvasRenderer.Clear();
+ TMP_UpdateRegistry.UnRegisterCanvasElementForRebuild(this);
+
+ if (m_MaskMaterial != null)
+ {
+ TMP_MaterialManager.ReleaseStencilMaterial(m_MaskMaterial);
+ m_MaskMaterial = null;
+ }
+
+ if (m_fallbackMaterial != null)
+ {
+ TMP_MaterialManager.ReleaseFallbackMaterial(m_fallbackMaterial);
+ m_fallbackMaterial = null;
+ }
+
+ base.OnDisable();
+ }
+
+
+ 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();
+ }
+
+
+
+#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 ***");
+
+ 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();
+
+ // Filter events and return if the affected material is not this object's material.
+ //if (targetMaterialID != sharedMaterialID && targetMaterialID != maskingMaterialID) return;
+
+ // Filter events and return if the affected material is not this object's material.
+ if (m_fallbackMaterial != null && fallbackSourceMaterialID == targetMaterialID)
+ TMP_MaterialManager.CopyMaterialPresetProperties(mat, m_fallbackMaterial);
+
+ if (m_TextComponent == null) m_TextComponent = GetComponentInParent<TextMeshProUGUI>();
+
+ // 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);
+ }
+ }
+
+ 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(m_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, TMP_FontAsset font)
+ {
+ if (m_fontAsset != null && font.GetInstanceID() == m_fontAsset.GetInstanceID())
+ {
+ // Copy Normal and Bold Weight
+ if (m_fallbackMaterial != null)
+ {
+ m_fallbackMaterial.SetFloat(ShaderUtilities.ID_WeightNormal, m_fontAsset.normalStyle);
+ m_fallbackMaterial.SetFloat(ShaderUtilities.ID_WeightBold, m_fontAsset.boldStyle);
+ }
+ }
+ }
+
+ /// <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)
+ {
+ m_StencilValue = TMP_MaterialManager.GetStencilID(gameObject);
+ m_ShouldRecalculateStencil = false;
+ }
+
+ if (m_StencilValue > 0)
+ {
+ mat = TMP_MaterialManager.GetStencilMaterial(baseMaterial, m_StencilValue);
+ if (m_MaskMaterial != null)
+ TMP_MaterialManager.ReleaseStencilMaterial(m_MaskMaterial);
+
+ m_MaskMaterial = mat;
+ }
+
+ 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;
+ }
+
+
+ /// <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)
+ {
+ if (m_TextComponent.ignoreRectMaskCulling) return;
+
+ base.Cull(clipRect, validRect);
+ }
+
+
+ /// <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 (!this.IsActive())
+ // return;
+
+ if (m_canvasRenderer == null) m_canvasRenderer = this.canvasRenderer;
+
+ m_canvasRenderer.materialCount = 1;
+ m_canvasRenderer.SetMaterial(materialForRendering, 0);
+ m_canvasRenderer.SetTexture(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 (m_canvasRenderer == null)
+ m_canvasRenderer = GetComponent<CanvasRenderer>();
+
+ return m_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
+ }
+ }
+}
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SubMeshUI.cs.meta b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SubMeshUI.cs.meta
new file mode 100644
index 0000000..f792f92
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_SubMeshUI.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 058cba836c1846c3aa1c5fd2e28aea77
+timeCreated: 1454709708
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_Text.cs b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_Text.cs
new file mode 100644
index 0000000..ee09b89
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_Text.cs
@@ -0,0 +1,7594 @@
+#define TMP_PRESENT
+
+using UnityEngine;
+using UnityEngine.TextCore;
+using UnityEngine.UI;
+using UnityEngine.Events;
+using UnityEngine.EventSystems;
+using System;
+using System.Text;
+using System.Collections;
+using System.Collections.Generic;
+
+
+namespace TMPro
+{
+ public interface ITextElement
+ {
+ Material sharedMaterial { get; }
+
+ void Rebuild(CanvasUpdate update);
+ int GetInstanceID();
+ }
+
+ public enum TextAlignmentOptions
+ {
+ TopLeft = _HorizontalAlignmentOptions.Left | _VerticalAlignmentOptions.Top,
+ Top = _HorizontalAlignmentOptions.Center | _VerticalAlignmentOptions.Top,
+ TopRight = _HorizontalAlignmentOptions.Right | _VerticalAlignmentOptions.Top,
+ TopJustified = _HorizontalAlignmentOptions.Justified | _VerticalAlignmentOptions.Top,
+ TopFlush = _HorizontalAlignmentOptions.Flush | _VerticalAlignmentOptions.Top,
+ TopGeoAligned = _HorizontalAlignmentOptions.Geometry | _VerticalAlignmentOptions.Top,
+
+ Left = _HorizontalAlignmentOptions.Left | _VerticalAlignmentOptions.Middle,
+ Center = _HorizontalAlignmentOptions.Center | _VerticalAlignmentOptions.Middle,
+ Right = _HorizontalAlignmentOptions.Right | _VerticalAlignmentOptions.Middle,
+ Justified = _HorizontalAlignmentOptions.Justified | _VerticalAlignmentOptions.Middle,
+ Flush = _HorizontalAlignmentOptions.Flush | _VerticalAlignmentOptions.Middle,
+ CenterGeoAligned = _HorizontalAlignmentOptions.Geometry | _VerticalAlignmentOptions.Middle,
+
+ BottomLeft = _HorizontalAlignmentOptions.Left | _VerticalAlignmentOptions.Bottom,
+ Bottom = _HorizontalAlignmentOptions.Center | _VerticalAlignmentOptions.Bottom,
+ BottomRight = _HorizontalAlignmentOptions.Right | _VerticalAlignmentOptions.Bottom,
+ BottomJustified = _HorizontalAlignmentOptions.Justified | _VerticalAlignmentOptions.Bottom,
+ BottomFlush = _HorizontalAlignmentOptions.Flush | _VerticalAlignmentOptions.Bottom,
+ BottomGeoAligned = _HorizontalAlignmentOptions.Geometry | _VerticalAlignmentOptions.Bottom,
+
+ BaselineLeft = _HorizontalAlignmentOptions.Left | _VerticalAlignmentOptions.Baseline,
+ Baseline = _HorizontalAlignmentOptions.Center | _VerticalAlignmentOptions.Baseline,
+ BaselineRight = _HorizontalAlignmentOptions.Right | _VerticalAlignmentOptions.Baseline,
+ BaselineJustified = _HorizontalAlignmentOptions.Justified | _VerticalAlignmentOptions.Baseline,
+ BaselineFlush = _HorizontalAlignmentOptions.Flush | _VerticalAlignmentOptions.Baseline,
+ BaselineGeoAligned = _HorizontalAlignmentOptions.Geometry | _VerticalAlignmentOptions.Baseline,
+
+ MidlineLeft = _HorizontalAlignmentOptions.Left | _VerticalAlignmentOptions.Geometry,
+ Midline = _HorizontalAlignmentOptions.Center | _VerticalAlignmentOptions.Geometry,
+ MidlineRight = _HorizontalAlignmentOptions.Right | _VerticalAlignmentOptions.Geometry,
+ MidlineJustified = _HorizontalAlignmentOptions.Justified | _VerticalAlignmentOptions.Geometry,
+ MidlineFlush = _HorizontalAlignmentOptions.Flush | _VerticalAlignmentOptions.Geometry,
+ MidlineGeoAligned = _HorizontalAlignmentOptions.Geometry | _VerticalAlignmentOptions.Geometry,
+
+ CaplineLeft = _HorizontalAlignmentOptions.Left | _VerticalAlignmentOptions.Capline,
+ Capline = _HorizontalAlignmentOptions.Center | _VerticalAlignmentOptions.Capline,
+ CaplineRight = _HorizontalAlignmentOptions.Right | _VerticalAlignmentOptions.Capline,
+ CaplineJustified = _HorizontalAlignmentOptions.Justified | _VerticalAlignmentOptions.Capline,
+ CaplineFlush = _HorizontalAlignmentOptions.Flush | _VerticalAlignmentOptions.Capline,
+ CaplineGeoAligned = _HorizontalAlignmentOptions.Geometry | _VerticalAlignmentOptions.Capline
+ };
+
+ /// <summary>
+ /// Internal horizontal text alignment options.
+ /// </summary>
+ public enum _HorizontalAlignmentOptions
+ {
+ Left = 0x1, Center = 0x2, Right = 0x4, Justified = 0x8, Flush = 0x10, Geometry = 0x20
+ }
+
+ /// <summary>
+ /// Internal vertical text alignment options.
+ /// </summary>
+ public enum _VerticalAlignmentOptions
+ {
+ Top = 0x100, Middle = 0x200, Bottom = 0x400, Baseline = 0x800, Geometry = 0x1000, Capline = 0x2000,
+ }
+
+
+ /// <summary>
+ /// Flags controlling what vertex data gets pushed to the mesh.
+ /// </summary>
+ public enum TextRenderFlags
+ {
+ DontRender = 0x0,
+ Render = 0xFF
+ };
+
+ public enum TMP_TextElementType { Character, Sprite };
+ public enum MaskingTypes { MaskOff = 0, MaskHard = 1, MaskSoft = 2 }; //, MaskTex = 4 };
+ public enum TextOverflowModes { Overflow = 0, Ellipsis = 1, Masking = 2, Truncate = 3, ScrollRect = 4, Page = 5, Linked = 6 };
+ public enum MaskingOffsetMode { Percentage = 0, Pixel = 1 };
+ public enum TextureMappingOptions { Character = 0, Line = 1, Paragraph = 2, MatchAspect = 3 };
+
+ public enum FontStyles { Normal = 0x0, Bold = 0x1, Italic = 0x2, Underline = 0x4, LowerCase = 0x8, UpperCase = 0x10, SmallCaps = 0x20, Strikethrough = 0x40, Superscript = 0x80, Subscript = 0x100, Highlight = 0x200 };
+ public enum FontWeight { Thin = 100, ExtraLight = 200, Light = 300, Regular = 400, Medium = 500, SemiBold = 600, Bold = 700, Heavy = 800, Black = 900 };
+
+ /// <summary>
+ /// Base class which contains common properties and functions shared between the TextMeshPro and TextMeshProUGUI component.
+ /// </summary>
+ public abstract class TMP_Text : MaskableGraphic
+ {
+ /// <summary>
+ /// A string containing the text to be displayed.
+ /// </summary>
+ public string text
+ {
+ get { return m_text; }
+ set { if (m_text == value) return; m_text = old_text = value; m_inputSource = TextInputSources.String; m_havePropertiesChanged = true; m_isCalculateSizeRequired = true; m_isInputParsingRequired = true; SetVerticesDirty(); SetLayoutDirty(); }
+ }
+ [SerializeField]
+ [TextArea(5, 10)]
+ protected string m_text;
+
+
+ /// <summary>
+ ///
+ /// </summary>
+ public bool isRightToLeftText
+ {
+ get { return m_isRightToLeft; }
+ set { if (m_isRightToLeft == value) return; m_isRightToLeft = value; m_havePropertiesChanged = true; m_isCalculateSizeRequired = true; m_isInputParsingRequired = true; SetVerticesDirty(); SetLayoutDirty(); }
+ }
+ [SerializeField]
+ protected bool m_isRightToLeft = false;
+
+
+ /// <summary>
+ /// The Font Asset to be assigned to this text object.
+ /// </summary>
+ public TMP_FontAsset font
+ {
+ get { return m_fontAsset; }
+ set { if (m_fontAsset == value) return; m_fontAsset = value; LoadFontAsset(); m_havePropertiesChanged = true; m_isCalculateSizeRequired = true; m_isInputParsingRequired = true; SetVerticesDirty(); SetLayoutDirty(); }
+ }
+ [SerializeField]
+ protected TMP_FontAsset m_fontAsset;
+ protected TMP_FontAsset m_currentFontAsset;
+ protected bool m_isSDFShader;
+
+
+ /// <summary>
+ /// The material to be assigned to this text object.
+ /// </summary>
+ public virtual Material fontSharedMaterial
+ {
+ get { return m_sharedMaterial; }
+ set { if (m_sharedMaterial == value) return; SetSharedMaterial(value); m_havePropertiesChanged = true; m_isInputParsingRequired = true; SetVerticesDirty(); SetMaterialDirty(); }
+ }
+ [SerializeField]
+ protected Material m_sharedMaterial;
+ protected Material m_currentMaterial;
+ protected MaterialReference[] m_materialReferences = new MaterialReference[32];
+ protected Dictionary<int, int> m_materialReferenceIndexLookup = new Dictionary<int, int>();
+
+ protected TMP_RichTextTagStack<MaterialReference> m_materialReferenceStack = new TMP_RichTextTagStack<MaterialReference>(new MaterialReference[16]);
+ protected int m_currentMaterialIndex;
+ //protected int m_sharedMaterialHashCode;
+
+
+ /// <summary>
+ /// An array containing the materials used by the text object.
+ /// </summary>
+ public virtual Material[] fontSharedMaterials
+ {
+ get { return GetSharedMaterials(); }
+ set { SetSharedMaterials(value); m_havePropertiesChanged = true; m_isInputParsingRequired = true; SetVerticesDirty(); SetMaterialDirty(); }
+ }
+ [SerializeField]
+ protected Material[] m_fontSharedMaterials;
+
+
+ /// <summary>
+ /// The material to be assigned to this text object. An instance of the material will be assigned to the object's renderer.
+ /// </summary>
+ public Material fontMaterial
+ {
+ // Return an Instance of the current material.
+ get { return GetMaterial(m_sharedMaterial); }
+
+ // Assign new font material
+ set
+ {
+ if (m_sharedMaterial != null && m_sharedMaterial.GetInstanceID() == value.GetInstanceID()) return;
+
+ m_sharedMaterial = value;
+
+ m_padding = GetPaddingForMaterial();
+ m_havePropertiesChanged = true;
+ m_isInputParsingRequired = true;
+
+ SetVerticesDirty();
+ SetMaterialDirty();
+ }
+ }
+ [SerializeField]
+ protected Material m_fontMaterial;
+
+
+ /// <summary>
+ /// The materials to be assigned to this text object. An instance of the materials will be assigned.
+ /// </summary>
+ public virtual Material[] fontMaterials
+ {
+ get { return GetMaterials(m_fontSharedMaterials); }
+
+ set { SetSharedMaterials(value); m_havePropertiesChanged = true; m_isInputParsingRequired = true; SetVerticesDirty(); SetMaterialDirty(); }
+ }
+ [SerializeField]
+ protected Material[] m_fontMaterials;
+
+ protected bool m_isMaterialDirty;
+
+
+ /// <summary>
+ /// This is the default vertex color assigned to each vertices. Color tags will override vertex colors unless the overrideColorTags is set.
+ /// </summary>
+ public override Color color
+ {
+ get { return m_fontColor; }
+ set { if (m_fontColor == value) return; m_havePropertiesChanged = true; m_fontColor = value; SetVerticesDirty(); }
+ }
+ //[UnityEngine.Serialization.FormerlySerializedAs("m_fontColor")] // Required for backwards compatibility with pre-Unity 4.6 releases.
+ [SerializeField]
+ protected Color32 m_fontColor32 = Color.white;
+ [SerializeField]
+ protected Color m_fontColor = Color.white;
+ protected static Color32 s_colorWhite = new Color32(255, 255, 255, 255);
+ protected Color32 m_underlineColor = s_colorWhite;
+ protected Color32 m_strikethroughColor = s_colorWhite;
+ protected Color32 m_highlightColor = s_colorWhite;
+ protected Vector4 m_highlightPadding = Vector4.zero;
+
+
+ /// <summary>
+ /// Sets the vertex color alpha value.
+ /// </summary>
+ public float alpha
+ {
+ get { return m_fontColor.a; }
+ set { if (m_fontColor.a == value) return; m_fontColor.a = value; m_havePropertiesChanged = true; SetVerticesDirty(); }
+ }
+
+
+ /// <summary>
+ /// Determines if Vertex Color Gradient should be used
+ /// </summary>
+ /// <value><c>true</c> if enable vertex gradient; otherwise, <c>false</c>.</value>
+ public bool enableVertexGradient
+ {
+ get { return m_enableVertexGradient; }
+ set { if (m_enableVertexGradient == value) return; m_havePropertiesChanged = true; m_enableVertexGradient = value; SetVerticesDirty(); }
+ }
+ [SerializeField]
+ protected bool m_enableVertexGradient;
+
+ [SerializeField]
+ protected ColorMode m_colorMode = ColorMode.FourCornersGradient;
+
+ /// <summary>
+ /// Sets the vertex colors for each of the 4 vertices of the character quads.
+ /// </summary>
+ /// <value>The color gradient.</value>
+ public VertexGradient colorGradient
+ {
+ get { return m_fontColorGradient; }
+ set { m_havePropertiesChanged = true; m_fontColorGradient = value; SetVerticesDirty(); }
+ }
+ [SerializeField]
+ protected VertexGradient m_fontColorGradient = new VertexGradient(Color.white);
+
+
+ /// <summary>
+ /// Set the vertex colors of the 4 vertices of each character quads.
+ /// </summary>
+ public TMP_ColorGradient colorGradientPreset
+ {
+ get { return m_fontColorGradientPreset; }
+ set { m_havePropertiesChanged = true; m_fontColorGradientPreset = value; SetVerticesDirty(); }
+ }
+ [SerializeField]
+ protected TMP_ColorGradient m_fontColorGradientPreset;
+
+
+ /// <summary>
+ /// Default Sprite Asset used by the text object.
+ /// </summary>
+ public TMP_SpriteAsset spriteAsset
+ {
+ get { return m_spriteAsset; }
+ set { m_spriteAsset = value; m_havePropertiesChanged = true; m_isInputParsingRequired = true; m_isCalculateSizeRequired = true; SetVerticesDirty(); SetLayoutDirty(); }
+ }
+ [SerializeField]
+ protected TMP_SpriteAsset m_spriteAsset;
+
+
+ /// <summary>
+ /// Determines whether or not the sprite color is multiplies by the vertex color of the text.
+ /// </summary>
+ public bool tintAllSprites
+ {
+ get { return m_tintAllSprites; }
+ set { if (m_tintAllSprites == value) return; m_tintAllSprites = value; m_havePropertiesChanged = true; SetVerticesDirty(); }
+ }
+ [SerializeField]
+ protected bool m_tintAllSprites;
+ protected bool m_tintSprite;
+ protected Color32 m_spriteColor;
+
+
+ /// <summary>
+ /// This overrides the color tags forcing the vertex colors to be the default font color.
+ /// </summary>
+ public bool overrideColorTags
+ {
+ get { return m_overrideHtmlColors; }
+ set { if (m_overrideHtmlColors == value) return; m_havePropertiesChanged = true; m_overrideHtmlColors = value; SetVerticesDirty(); }
+ }
+ [SerializeField]
+ protected bool m_overrideHtmlColors = false;
+
+
+ /// <summary>
+ /// Sets the color of the _FaceColor property of the assigned material. Changing face color will result in an instance of the material.
+ /// </summary>
+ public Color32 faceColor
+ {
+ get
+ {
+ if (m_sharedMaterial == null) return m_faceColor;
+
+ m_faceColor = m_sharedMaterial.GetColor(ShaderUtilities.ID_FaceColor);
+ return m_faceColor;
+ }
+
+ set { if (m_faceColor.Compare(value)) return; SetFaceColor(value); m_havePropertiesChanged = true; m_faceColor = value; SetVerticesDirty(); SetMaterialDirty(); }
+ }
+ [SerializeField]
+ protected Color32 m_faceColor = Color.white;
+
+
+ /// <summary>
+ /// Sets the color of the _OutlineColor property of the assigned material. Changing outline color will result in an instance of the material.
+ /// </summary>
+ public Color32 outlineColor
+ {
+ get
+ {
+ if (m_sharedMaterial == null) return m_outlineColor;
+
+ m_outlineColor = m_sharedMaterial.GetColor(ShaderUtilities.ID_OutlineColor);
+ return m_outlineColor;
+ }
+
+ set { if (m_outlineColor.Compare(value)) return; SetOutlineColor(value); m_havePropertiesChanged = true; m_outlineColor = value; SetVerticesDirty(); }
+ }
+ [SerializeField]
+ protected Color32 m_outlineColor = Color.black;
+
+
+ /// <summary>
+ /// Sets the thickness of the outline of the font. Setting this value will result in an instance of the material.
+ /// </summary>
+ public float outlineWidth
+ {
+ get
+ {
+ if (m_sharedMaterial == null) return m_outlineWidth;
+
+ m_outlineWidth = m_sharedMaterial.GetFloat(ShaderUtilities.ID_OutlineWidth);
+ return m_outlineWidth;
+ }
+ set { if (m_outlineWidth == value) return; SetOutlineThickness(value); m_havePropertiesChanged = true; m_outlineWidth = value; SetVerticesDirty(); }
+ }
+ protected float m_outlineWidth = 0.0f;
+
+
+ /// <summary>
+ /// The point size of the font.
+ /// </summary>
+ public float fontSize
+ {
+ get { return m_fontSize; }
+ set { if (m_fontSize == value) return; m_havePropertiesChanged = true; m_isCalculateSizeRequired = true; m_fontSize = value; if (!m_enableAutoSizing) m_fontSizeBase = m_fontSize; SetVerticesDirty(); SetLayoutDirty(); }
+ }
+ [SerializeField]
+ protected float m_fontSize = 36; // Font Size
+ protected float m_currentFontSize; // Temporary Font Size affected by tags
+ [SerializeField]
+ protected float m_fontSizeBase = 36;
+ protected TMP_RichTextTagStack<float> m_sizeStack = new TMP_RichTextTagStack<float>(16);
+
+
+ /// <summary>
+ /// The scale of the current text.
+ /// </summary>
+ public float fontScale
+ {
+ get { return m_fontScale; }
+ }
+
+
+ /// <summary>
+ /// Control the weight of the font if an alternative font asset is assigned for the given weight in the font asset editor.
+ /// </summary>
+ public FontWeight fontWeight
+ {
+ get { return m_fontWeight; }
+ set { if (m_fontWeight == value) return; m_fontWeight = value; m_havePropertiesChanged = true; m_isCalculateSizeRequired = true; m_isInputParsingRequired = true; SetVerticesDirty(); SetLayoutDirty(); }
+ }
+ [SerializeField]
+ protected FontWeight m_fontWeight = FontWeight.Regular;
+ protected FontWeight m_FontWeightInternal = FontWeight.Regular;
+ protected TMP_RichTextTagStack<FontWeight> m_FontWeightStack = new TMP_RichTextTagStack<FontWeight>(8);
+
+ /// <summary>
+ ///
+ /// </summary>
+ public float pixelsPerUnit
+ {
+ get
+ {
+ var localCanvas = canvas;
+ if (!localCanvas)
+ return 1;
+ // For dynamic fonts, ensure we use one pixel per pixel on the screen.
+ if (!font)
+ return localCanvas.scaleFactor;
+ // For non-dynamic fonts, calculate pixels per unit based on specified font size relative to font object's own font size.
+ if (m_currentFontAsset == null || m_currentFontAsset.faceInfo.pointSize <= 0 || m_fontSize <= 0)
+ return 1;
+ return m_fontSize / m_currentFontAsset.faceInfo.pointSize;
+ }
+ }
+
+
+ /// <summary>
+ /// Enable text auto-sizing
+ /// </summary>
+ public bool enableAutoSizing
+ {
+ get { return m_enableAutoSizing; }
+ set { if (m_enableAutoSizing == value) return; m_enableAutoSizing = value; SetVerticesDirty(); SetLayoutDirty(); }
+ }
+ [SerializeField]
+ protected bool m_enableAutoSizing;
+ protected float m_maxFontSize; // Used in conjunction with auto-sizing
+ protected float m_minFontSize; // Used in conjunction with auto-sizing
+
+
+ /// <summary>
+ /// Minimum point size of the font when text auto-sizing is enabled.
+ /// </summary>
+ public float fontSizeMin
+ {
+ get { return m_fontSizeMin; }
+ set { if (m_fontSizeMin == value) return; m_fontSizeMin = value; SetVerticesDirty(); SetLayoutDirty(); }
+ }
+ [SerializeField]
+ protected float m_fontSizeMin = 0; // Text Auto Sizing Min Font Size.
+
+
+ /// <summary>
+ /// Maximum point size of the font when text auto-sizing is enabled.
+ /// </summary>
+ public float fontSizeMax
+ {
+ get { return m_fontSizeMax; }
+ set { if (m_fontSizeMax == value) return; m_fontSizeMax = value; SetVerticesDirty(); SetLayoutDirty(); }
+ }
+ [SerializeField]
+ protected float m_fontSizeMax = 0; // Text Auto Sizing Max Font Size.
+
+
+ /// <summary>
+ /// The style of the text
+ /// </summary>
+ public FontStyles fontStyle
+ {
+ get { return m_fontStyle; }
+ set { if (m_fontStyle == value) return; m_fontStyle = value; m_havePropertiesChanged = true; m_isCalculateSizeRequired = true; m_isInputParsingRequired = true; SetVerticesDirty(); SetLayoutDirty(); }
+ }
+ [SerializeField]
+ protected FontStyles m_fontStyle = FontStyles.Normal;
+ protected FontStyles m_FontStyleInternal = FontStyles.Normal;
+ protected TMP_FontStyleStack m_fontStyleStack;
+
+ /// <summary>
+ /// Property used in conjunction with padding calculation for the geometry.
+ /// </summary>
+ public bool isUsingBold { get { return m_isUsingBold; } }
+ protected bool m_isUsingBold = false; // Used to ensure GetPadding & Ratios take into consideration bold characters.
+
+
+ /// <summary>
+ /// Text alignment options
+ /// </summary>
+ public TextAlignmentOptions alignment
+ {
+ get { return m_textAlignment; }
+ set { if (m_textAlignment == value) return; m_havePropertiesChanged = true; m_textAlignment = value; SetVerticesDirty(); }
+ }
+ [SerializeField]
+ [UnityEngine.Serialization.FormerlySerializedAs("m_lineJustification")]
+ protected TextAlignmentOptions m_textAlignment = TextAlignmentOptions.TopLeft;
+ protected TextAlignmentOptions m_lineJustification;
+ protected TMP_RichTextTagStack<TextAlignmentOptions> m_lineJustificationStack = new TMP_RichTextTagStack<TextAlignmentOptions>(new TextAlignmentOptions[16]);
+ protected Vector3[] m_textContainerLocalCorners = new Vector3[4];
+
+ /// <summary>
+ /// Use the extents of the text geometry for alignment instead of font metrics.
+ /// </summary>
+ //public bool alignByGeometry
+ //{
+ // get { return m_alignByGeometry; }
+ // set { if (m_alignByGeometry == value) return; m_havePropertiesChanged = true; m_alignByGeometry = value; SetVerticesDirty(); }
+ //}
+ //[SerializeField]
+ //protected bool m_alignByGeometry;
+
+
+ /// <summary>
+ /// The amount of additional spacing between characters.
+ /// </summary>
+ public float characterSpacing
+ {
+ get { return m_characterSpacing; }
+ set { if (m_characterSpacing == value) return; m_havePropertiesChanged = true; m_isCalculateSizeRequired = true; m_characterSpacing = value; SetVerticesDirty(); SetLayoutDirty(); }
+ }
+ [SerializeField]
+ protected float m_characterSpacing = 0;
+ protected float m_cSpacing = 0;
+ protected float m_monoSpacing = 0;
+
+ /// <summary>
+ /// The amount of additional spacing between words.
+ /// </summary>
+ public float wordSpacing
+ {
+ get { return m_wordSpacing; }
+ set { if (m_wordSpacing == value) return; m_havePropertiesChanged = true; m_isCalculateSizeRequired = true; m_wordSpacing = value; SetVerticesDirty(); SetLayoutDirty(); }
+ }
+ [SerializeField]
+ protected float m_wordSpacing = 0;
+
+ /// <summary>
+ /// The amount of additional spacing to add between each lines of text.
+ /// </summary>
+ public float lineSpacing
+ {
+ get { return m_lineSpacing; }
+ set { if (m_lineSpacing == value) return; m_havePropertiesChanged = true; m_isCalculateSizeRequired = true; m_lineSpacing = value; SetVerticesDirty(); SetLayoutDirty(); }
+ }
+ [SerializeField]
+ protected float m_lineSpacing = 0;
+ protected float m_lineSpacingDelta = 0; // Used with Text Auto Sizing feature
+ protected float m_lineHeight = TMP_Math.FLOAT_UNSET; // Used with the <line-height=xx.x> tag.
+
+
+ /// <summary>
+ /// The amount of potential line spacing adjustment before text auto sizing kicks in.
+ /// </summary>
+ public float lineSpacingAdjustment
+ {
+ get { return m_lineSpacingMax; }
+ set { if (m_lineSpacingMax == value) return; m_havePropertiesChanged = true; m_isCalculateSizeRequired = true; m_lineSpacingMax = value; SetVerticesDirty(); SetLayoutDirty(); }
+ }
+ [SerializeField]
+ protected float m_lineSpacingMax = 0; // Text Auto Sizing Max Line spacing reduction.
+ //protected bool m_forceLineBreak;
+
+ /// <summary>
+ /// The amount of additional spacing to add between each lines of text.
+ /// </summary>
+ public float paragraphSpacing
+ {
+ get { return m_paragraphSpacing; }
+ set { if (m_paragraphSpacing == value) return; m_havePropertiesChanged = true; m_isCalculateSizeRequired = true; m_paragraphSpacing = value; SetVerticesDirty(); SetLayoutDirty(); }
+ }
+ [SerializeField]
+ protected float m_paragraphSpacing = 0;
+
+
+ /// <summary>
+ /// Percentage the width of characters can be adjusted before text auto-sizing begins to reduce the point size.
+ /// </summary>
+ public float characterWidthAdjustment
+ {
+ get { return m_charWidthMaxAdj; }
+ set { if (m_charWidthMaxAdj == value) return; m_havePropertiesChanged = true; m_isCalculateSizeRequired = true; m_charWidthMaxAdj = value; SetVerticesDirty(); SetLayoutDirty(); }
+ }
+ [SerializeField]
+ protected float m_charWidthMaxAdj = 0f; // Text Auto Sizing Max Character Width reduction.
+ protected float m_charWidthAdjDelta = 0;
+
+
+ /// <summary>
+ /// Controls whether or not word wrapping is applied. When disabled, the text will be displayed on a single line.
+ /// </summary>
+ public bool enableWordWrapping
+ {
+ get { return m_enableWordWrapping; }
+ set { if (m_enableWordWrapping == value) return; m_havePropertiesChanged = true; m_isInputParsingRequired = true; m_isCalculateSizeRequired = true; m_enableWordWrapping = value; SetVerticesDirty(); SetLayoutDirty(); }
+ }
+ [SerializeField]
+ protected bool m_enableWordWrapping = false;
+ protected bool m_isCharacterWrappingEnabled = false;
+ protected bool m_isNonBreakingSpace = false;
+ protected bool m_isIgnoringAlignment;
+
+ /// <summary>
+ /// Controls the blending between using character and word spacing to fill-in the space for justified text.
+ /// </summary>
+ public float wordWrappingRatios
+ {
+ get { return m_wordWrappingRatios; }
+ set { if (m_wordWrappingRatios == value) return; m_wordWrappingRatios = value; m_havePropertiesChanged = true; m_isCalculateSizeRequired = true; SetVerticesDirty(); SetLayoutDirty(); }
+ }
+ [SerializeField]
+ protected float m_wordWrappingRatios = 0.4f; // Controls word wrapping ratios between word or characters.
+
+
+ /// <summary>
+ ///
+ /// </summary>
+ //public bool enableAdaptiveJustification
+ //{
+ // get { return m_enableAdaptiveJustification; }
+ // set { if (m_enableAdaptiveJustification == value) return; m_enableAdaptiveJustification = value; m_havePropertiesChanged = true; m_isCalculateSizeRequired = true; SetVerticesDirty(); SetLayoutDirty(); }
+ //}
+ //[SerializeField]
+ //protected bool m_enableAdaptiveJustification;
+ //protected float m_adaptiveJustificationThreshold = 10.0f;
+
+
+ /// <summary>
+ /// Controls the Text Overflow Mode
+ /// </summary>
+ public TextOverflowModes overflowMode
+ {
+ get { return m_overflowMode; }
+ set { if (m_overflowMode == value) return; m_overflowMode = value; m_havePropertiesChanged = true; m_isCalculateSizeRequired = true; SetVerticesDirty(); SetLayoutDirty(); }
+ }
+ [SerializeField]
+ protected TextOverflowModes m_overflowMode = TextOverflowModes.Overflow;
+
+
+ /// <summary>
+ /// Indicates if the text exceeds the vertical bounds of its text container.
+ /// </summary>
+ public bool isTextOverflowing
+ {
+ get { if (m_firstOverflowCharacterIndex != -1) return true; return false; }
+ }
+
+
+ /// <summary>
+ /// The first character which exceeds the vertical bounds of its text container.
+ /// </summary>
+ public int firstOverflowCharacterIndex
+ {
+ get { return m_firstOverflowCharacterIndex; }
+ }
+ [SerializeField]
+ protected int m_firstOverflowCharacterIndex = -1;
+
+
+ /// <summary>
+ /// The linked text component used for flowing the text from one text component to another.
+ /// </summary>
+ public TMP_Text linkedTextComponent
+ {
+ get { return m_linkedTextComponent; }
+
+ set
+ {
+ if (m_linkedTextComponent != value)
+ {
+ // Release previously linked text component.
+ if (m_linkedTextComponent != null)
+ {
+ m_linkedTextComponent.overflowMode = TextOverflowModes.Overflow;
+ m_linkedTextComponent.linkedTextComponent = null;
+ m_linkedTextComponent.isLinkedTextComponent = false;
+ }
+
+ m_linkedTextComponent = value;
+
+ if (m_linkedTextComponent != null)
+ m_linkedTextComponent.isLinkedTextComponent = true;
+ }
+
+ m_havePropertiesChanged = true;
+ m_isCalculateSizeRequired = true;
+ SetVerticesDirty();
+ SetLayoutDirty();
+ }
+ }
+ [SerializeField]
+ protected TMP_Text m_linkedTextComponent;
+
+
+ /// <summary>
+ /// Indicates whether this text component is linked to another.
+ /// </summary>
+ public bool isLinkedTextComponent
+ {
+ get { return m_isLinkedTextComponent; }
+
+ set
+ {
+ m_isLinkedTextComponent = value;
+
+ if (m_isLinkedTextComponent == false)
+ m_firstVisibleCharacter = 0;
+
+ m_havePropertiesChanged = true;
+ m_isCalculateSizeRequired = true;
+ SetVerticesDirty();
+ SetLayoutDirty();
+ }
+ }
+ [SerializeField]
+ protected bool m_isLinkedTextComponent;
+
+
+ /// <summary>
+ /// Property indicating whether the text is Truncated or using Ellipsis.
+ /// </summary>
+ public bool isTextTruncated { get { return m_isTextTruncated; } }
+ [SerializeField]
+ protected bool m_isTextTruncated;
+
+
+ /// <summary>
+ /// Determines if kerning is enabled or disabled.
+ /// </summary>
+ public bool enableKerning
+ {
+ get { return m_enableKerning; }
+ set { if (m_enableKerning == value) return; m_havePropertiesChanged = true; m_isCalculateSizeRequired = true; m_enableKerning = value; SetVerticesDirty(); SetLayoutDirty(); }
+ }
+ [SerializeField]
+ protected bool m_enableKerning;
+
+
+ /// <summary>
+ /// Adds extra padding around each character. This may be necessary when the displayed text is very small to prevent clipping.
+ /// </summary>
+ public bool extraPadding
+ {
+ get { return m_enableExtraPadding; }
+ set { if (m_enableExtraPadding == value) return; m_havePropertiesChanged = true; m_enableExtraPadding = value; UpdateMeshPadding(); /* m_isCalculateSizeRequired = true;*/ SetVerticesDirty(); /* SetLayoutDirty();*/ }
+ }
+ [SerializeField]
+ protected bool m_enableExtraPadding = false;
+ [SerializeField]
+ protected bool checkPaddingRequired;
+
+
+ /// <summary>
+ /// Enables or Disables Rich Text Tags
+ /// </summary>
+ public bool richText
+ {
+ get { return m_isRichText; }
+ set { if (m_isRichText == value) return; m_isRichText = value; m_havePropertiesChanged = true; m_isCalculateSizeRequired = true; m_isInputParsingRequired = true; SetVerticesDirty(); SetLayoutDirty(); }
+ }
+ [SerializeField]
+ protected bool m_isRichText = true; // Used to enable or disable Rich Text.
+
+
+ /// <summary>
+ /// Enables or Disables parsing of CTRL characters in input text.
+ /// </summary>
+ public bool parseCtrlCharacters
+ {
+ get { return m_parseCtrlCharacters; }
+ set { if (m_parseCtrlCharacters == value) return; m_parseCtrlCharacters = value; m_havePropertiesChanged = true; m_isCalculateSizeRequired = true; m_isInputParsingRequired = true; SetVerticesDirty(); SetLayoutDirty(); }
+ }
+ [SerializeField]
+ protected bool m_parseCtrlCharacters = true;
+
+
+ /// <summary>
+ /// Sets the RenderQueue along with Ztest to force the text to be drawn last and on top of scene elements.
+ /// </summary>
+ public bool isOverlay
+ {
+ get { return m_isOverlay; }
+ set { if (m_isOverlay == value) return; m_isOverlay = value; SetShaderDepth(); m_havePropertiesChanged = true; SetVerticesDirty(); }
+ }
+ protected bool m_isOverlay = false;
+
+
+ /// <summary>
+ /// Sets Perspective Correction to Zero for Orthographic Camera mode & 0.875f for Perspective Camera mode.
+ /// </summary>
+ public bool isOrthographic
+ {
+ get { return m_isOrthographic; }
+ set { if (m_isOrthographic == value) return; m_havePropertiesChanged = true; m_isOrthographic = value; SetVerticesDirty(); }
+ }
+ [SerializeField]
+ protected bool m_isOrthographic = false;
+
+
+ /// <summary>
+ /// Sets the culling on the shaders. Note changing this value will result in an instance of the material.
+ /// </summary>
+ public bool enableCulling
+ {
+ get { return m_isCullingEnabled; }
+ set { if (m_isCullingEnabled == value) return; m_isCullingEnabled = value; SetCulling(); m_havePropertiesChanged = true; }
+ }
+ [SerializeField]
+ protected bool m_isCullingEnabled = false;
+
+ /// <summary>
+ /// Controls whether or not the text object will be culled when using a 2D Rect Mask.
+ /// </summary>
+ public bool ignoreRectMaskCulling
+ {
+ get { return m_ignoreRectMaskCulling; }
+ set { if (m_ignoreRectMaskCulling == value) return; m_ignoreRectMaskCulling = value; m_havePropertiesChanged = true; }
+ }
+ [SerializeField]
+ protected bool m_ignoreRectMaskCulling;
+
+
+ /// <summary>
+ /// Forces objects that are not visible to get refreshed.
+ /// </summary>
+ public bool ignoreVisibility
+ {
+ get { return m_ignoreCulling; }
+ set { if (m_ignoreCulling == value) return; m_havePropertiesChanged = true; m_ignoreCulling = value; }
+ }
+ [SerializeField]
+ protected bool m_ignoreCulling = true; // Not implemented yet.
+
+
+ /// <summary>
+ /// Controls how the face and outline textures will be applied to the text object.
+ /// </summary>
+ public TextureMappingOptions horizontalMapping
+ {
+ get { return m_horizontalMapping; }
+ set { if (m_horizontalMapping == value) return; m_havePropertiesChanged = true; m_horizontalMapping = value; SetVerticesDirty(); }
+ }
+ [SerializeField]
+ protected TextureMappingOptions m_horizontalMapping = TextureMappingOptions.Character;
+
+
+ /// <summary>
+ /// Controls how the face and outline textures will be applied to the text object.
+ /// </summary>
+ public TextureMappingOptions verticalMapping
+ {
+ get { return m_verticalMapping; }
+ set { if (m_verticalMapping == value) return; m_havePropertiesChanged = true; m_verticalMapping = value; SetVerticesDirty(); }
+ }
+ [SerializeField]
+ protected TextureMappingOptions m_verticalMapping = TextureMappingOptions.Character;
+
+
+ /// <summary>
+ /// Controls the UV Offset for the various texture mapping mode on the text object.
+ /// </summary>
+ //public Vector2 mappingUvOffset
+ //{
+ // get { return m_uvOffset; }
+ // set { if (m_uvOffset == value) return; m_havePropertiesChanged = true; m_uvOffset = value; SetVerticesDirty(); }
+ //}
+ //[SerializeField]
+ //protected Vector2 m_uvOffset = Vector2.zero; // Used to offset UV on Texturing
+
+
+ /// <summary>
+ /// Controls the horizontal offset of the UV of the texture mapping mode for each line of the text object.
+ /// </summary>
+ public float mappingUvLineOffset
+ {
+ get { return m_uvLineOffset; }
+ set { if (m_uvLineOffset == value) return; m_havePropertiesChanged = true; m_uvLineOffset = value; SetVerticesDirty(); }
+ }
+ [SerializeField]
+ protected float m_uvLineOffset = 0.0f; // Used for UV line offset per line
+
+
+ /// <summary>
+ /// Determines if the Mesh will be rendered.
+ /// </summary>
+ public TextRenderFlags renderMode
+ {
+ get { return m_renderMode; }
+ set { if (m_renderMode == value) return; m_renderMode = value; m_havePropertiesChanged = true; }
+ }
+ protected TextRenderFlags m_renderMode = TextRenderFlags.Render;
+
+
+ /// <summary>
+ /// Determines the sorting order of the geometry of the text object.
+ /// </summary>
+ public VertexSortingOrder geometrySortingOrder
+ {
+ get { return m_geometrySortingOrder; }
+
+ set { m_geometrySortingOrder = value; m_havePropertiesChanged = true; SetVerticesDirty(); }
+
+ }
+ [SerializeField]
+ protected VertexSortingOrder m_geometrySortingOrder;
+
+ /// <summary>
+ /// Determines if the data structures allocated to contain the geometry of the text object will be reduced in size if the number of characters required to display the text is reduced by more than 256 characters.
+ /// This reduction has the benefit of reducing the amount of vertex data being submitted to the graphic device but results in GC when it occurs.
+ /// </summary>
+ public bool vertexBufferAutoSizeReduction
+ {
+ get { return m_VertexBufferAutoSizeReduction; }
+ set { m_VertexBufferAutoSizeReduction = value; m_havePropertiesChanged = true; SetVerticesDirty(); }
+ }
+ [SerializeField]
+ protected bool m_VertexBufferAutoSizeReduction = true;
+
+ /// <summary>
+ /// The first character which should be made visible in conjunction with the Text Overflow Linked mode.
+ /// </summary>
+ public int firstVisibleCharacter
+ {
+ get { return m_firstVisibleCharacter; }
+ set { if (m_firstVisibleCharacter == value) return; m_havePropertiesChanged = true; m_firstVisibleCharacter = value; SetVerticesDirty(); }
+ }
+ [SerializeField]
+ protected int m_firstVisibleCharacter;
+
+ /// <summary>
+ /// Allows to control how many characters are visible from the input.
+ /// </summary>
+ public int maxVisibleCharacters
+ {
+ get { return m_maxVisibleCharacters; }
+ set { if (m_maxVisibleCharacters == value) return; m_havePropertiesChanged = true; m_maxVisibleCharacters = value; SetVerticesDirty(); }
+ }
+ protected int m_maxVisibleCharacters = 99999;
+
+
+ /// <summary>
+ /// Allows to control how many words are visible from the input.
+ /// </summary>
+ public int maxVisibleWords
+ {
+ get { return m_maxVisibleWords; }
+ set { if (m_maxVisibleWords == value) return; m_havePropertiesChanged = true; m_maxVisibleWords = value; SetVerticesDirty(); }
+ }
+ protected int m_maxVisibleWords = 99999;
+
+
+ /// <summary>
+ /// Allows control over how many lines of text are displayed.
+ /// </summary>
+ public int maxVisibleLines
+ {
+ get { return m_maxVisibleLines; }
+ set { if (m_maxVisibleLines == value) return; m_havePropertiesChanged = true; m_isInputParsingRequired = true; m_maxVisibleLines = value; SetVerticesDirty(); }
+ }
+ protected int m_maxVisibleLines = 99999;
+
+
+ /// <summary>
+ /// Determines if the text's vertical alignment will be adjusted based on visible descender of the text.
+ /// </summary>
+ public bool useMaxVisibleDescender
+ {
+ get { return m_useMaxVisibleDescender; }
+ set { if (m_useMaxVisibleDescender == value) return; m_havePropertiesChanged = true; m_isInputParsingRequired = true; SetVerticesDirty(); }
+ }
+ [SerializeField]
+ protected bool m_useMaxVisibleDescender = true;
+
+
+ /// <summary>
+ /// Controls which page of text is shown
+ /// </summary>
+ public int pageToDisplay
+ {
+ get { return m_pageToDisplay; }
+ set { if (m_pageToDisplay == value) return; m_havePropertiesChanged = true; m_pageToDisplay = value; SetVerticesDirty(); }
+ }
+ [SerializeField]
+ protected int m_pageToDisplay = 1;
+ protected bool m_isNewPage = false;
+
+ /// <summary>
+ /// The margins of the text object.
+ /// </summary>
+ public virtual Vector4 margin
+ {
+ get { return m_margin; }
+ set { if (m_margin == value) return; m_margin = value; ComputeMarginSize(); m_havePropertiesChanged = true; SetVerticesDirty(); }
+ }
+ [SerializeField]
+ protected Vector4 m_margin = new Vector4(0, 0, 0, 0);
+ protected float m_marginLeft;
+ protected float m_marginRight;
+ protected float m_marginWidth; // Width of the RectTransform minus left and right margins.
+ protected float m_marginHeight; // Height of the RectTransform minus top and bottom margins.
+ protected float m_width = -1;
+
+
+ /// <summary>
+ /// Returns data about the text object which includes information about each character, word, line, link, etc.
+ /// </summary>
+ public TMP_TextInfo textInfo
+ {
+ get { return m_textInfo; }
+ }
+ [SerializeField]
+ protected TMP_TextInfo m_textInfo; // Class which holds information about the Text object such as characters, lines, mesh data as well as metrics.
+
+ /// <summary>
+ /// Property tracking if any of the text properties have changed. Flag is set before the text is regenerated.
+ /// </summary>
+ public bool havePropertiesChanged
+ {
+ get { return m_havePropertiesChanged; }
+ set { if (m_havePropertiesChanged == value) return; m_havePropertiesChanged = value; m_isInputParsingRequired = true; SetAllDirty(); }
+ }
+ //[SerializeField]
+ protected bool m_havePropertiesChanged; // Used to track when properties of the text object have changed.
+
+
+ /// <summary>
+ /// Property to handle legacy animation component.
+ /// </summary>
+ public bool isUsingLegacyAnimationComponent
+ {
+ get { return m_isUsingLegacyAnimationComponent; }
+ set { m_isUsingLegacyAnimationComponent = value; }
+ }
+ [SerializeField]
+ protected bool m_isUsingLegacyAnimationComponent;
+
+
+ /// <summary>
+ /// Returns are reference to the Transform
+ /// </summary>
+ public new Transform transform
+ {
+ get
+ {
+ if (m_transform == null)
+ m_transform = GetComponent<Transform>();
+ return m_transform;
+ }
+ }
+ protected Transform m_transform;
+
+
+ /// <summary>
+ /// Returns are reference to the RectTransform
+ /// </summary>
+ public new RectTransform rectTransform
+ {
+ get
+ {
+ if (m_rectTransform == null)
+ m_rectTransform = GetComponent<RectTransform>();
+ return m_rectTransform;
+ }
+ }
+ protected RectTransform m_rectTransform;
+
+
+ /// <summary>
+ /// Enables control over setting the size of the text container to match the text object.
+ /// </summary>
+ public virtual bool autoSizeTextContainer
+ {
+ get;
+ set;
+ }
+ protected bool m_autoSizeTextContainer;
+
+
+ /// <summary>
+ /// The mesh used by the font asset and material assigned to the text object.
+ /// </summary>
+ public virtual Mesh mesh
+ {
+ get { return m_mesh; }
+ }
+ protected Mesh m_mesh;
+
+
+ /// <summary>
+ /// Determines if the geometry of the characters will be quads or volumetric (cubes).
+ /// </summary>
+ public bool isVolumetricText
+ {
+ get { return m_isVolumetricText; }
+ set { if (m_isVolumetricText == value) return; m_havePropertiesChanged = value; m_textInfo.ResetVertexLayout(value); m_isInputParsingRequired = true; SetVerticesDirty(); SetLayoutDirty(); }
+ }
+ [SerializeField]
+ protected bool m_isVolumetricText;
+
+ /// <summary>
+ /// Returns the bounds of the mesh of the text object in world space.
+ /// </summary>
+ public Bounds bounds
+ {
+ get
+ {
+ if (m_mesh == null) return new Bounds();
+
+ return GetCompoundBounds();
+ }
+ }
+
+ /// <summary>
+ /// Returns the bounds of the text of the text object.
+ /// </summary>
+ public Bounds textBounds
+ {
+ get
+ {
+ if (m_textInfo == null) return new Bounds();
+
+ return GetTextBounds();
+ }
+ }
+
+ // *** Unity Event Handling ***
+
+ //[Serializable]
+ //public class TextChangedEvent : UnityEvent { }
+
+ ///// <summary>
+ ///// Event delegate triggered when text has changed and been rendered.
+ ///// </summary>
+ //public TextChangedEvent onTextChanged
+ //{
+ // get { return m_OnTextChanged; }
+ // set { m_OnTextChanged = value; }
+ //}
+ //[SerializeField]
+ //private TextChangedEvent m_OnTextChanged = new TextChangedEvent();
+
+ //protected void SendOnTextChanged()
+ //{
+ // if (onTextChanged != null)
+ // onTextChanged.Invoke();
+ //}
+
+
+ // *** SPECIAL COMPONENTS ***
+
+ /// <summary>
+ /// Component used to control wrapping of text following some arbitrary shape.
+ /// </summary>
+ //public MarginShaper marginShaper
+ //{
+ // get
+ // {
+ // if (m_marginShaper == null) m_marginShaper = GetComponent<MarginShaper>();
+
+ // return m_marginShaper;
+ // }
+ //}
+ //[SerializeField]
+ //protected MarginShaper m_marginShaper;
+
+
+ /// <summary>
+ /// Component used to control and animate sprites in the text object.
+ /// </summary>
+ protected TMP_SpriteAnimator spriteAnimator
+ {
+ get
+ {
+ if (m_spriteAnimator == null)
+ {
+ m_spriteAnimator = GetComponent<TMP_SpriteAnimator>();
+ if (m_spriteAnimator == null) m_spriteAnimator = gameObject.AddComponent<TMP_SpriteAnimator>();
+ }
+
+ return m_spriteAnimator;
+ }
+
+ }
+ [SerializeField]
+ protected TMP_SpriteAnimator m_spriteAnimator;
+
+
+ /// <summary>
+ ///
+ /// </summary>
+ //public TMP_TextShaper textShaper
+ //{
+ // get
+ // {
+ // if (m_textShaper == null)
+ // m_textShaper = GetComponent<TMP_TextShaper>();
+
+ // return m_textShaper;
+ // }
+ //}
+ //[SerializeField]
+ //protected TMP_TextShaper m_textShaper;
+
+ // *** PROPERTIES RELATED TO UNITY LAYOUT SYSTEM ***
+ /// <summary>
+ ///
+ /// </summary>
+ public float flexibleHeight { get { return m_flexibleHeight; } }
+ protected float m_flexibleHeight = -1f;
+
+ /// <summary>
+ ///
+ /// </summary>
+ public float flexibleWidth { get { return m_flexibleWidth; } }
+ protected float m_flexibleWidth = -1f;
+
+ /// <summary>
+ ///
+ /// </summary>
+ public float minWidth { get { return m_minWidth; } }
+ protected float m_minWidth;
+
+ /// <summary>
+ ///
+ /// </summary>
+ public float minHeight { get { return m_minHeight; } }
+ protected float m_minHeight;
+
+ /// <summary>
+ ///
+ /// </summary>
+ public float maxWidth { get { return m_maxWidth; } }
+ protected float m_maxWidth;
+
+ /// <summary>
+ ///
+ /// </summary>
+ public float maxHeight { get { return m_maxHeight; } }
+ protected float m_maxHeight;
+
+ /// <summary>
+ ///
+ /// </summary>
+ protected LayoutElement layoutElement
+ {
+ get
+ {
+ if (m_LayoutElement == null)
+ {
+ m_LayoutElement = GetComponent<LayoutElement>();
+ }
+
+ return m_LayoutElement;
+ }
+ }
+ protected LayoutElement m_LayoutElement;
+
+ /// <summary>
+ /// Computed preferred width of the text object.
+ /// </summary>
+ public virtual float preferredWidth { get { if (!m_isPreferredWidthDirty) return m_preferredWidth; m_preferredWidth = GetPreferredWidth(); return m_preferredWidth; } }
+ protected float m_preferredWidth;
+ protected float m_renderedWidth;
+ protected bool m_isPreferredWidthDirty;
+
+ /// <summary>
+ /// Computed preferred height of the text object.
+ /// </summary>
+ public virtual float preferredHeight { get { if (!m_isPreferredHeightDirty) return m_preferredHeight; m_preferredHeight = GetPreferredHeight(); return m_preferredHeight; } }
+ protected float m_preferredHeight;
+ protected float m_renderedHeight;
+ protected bool m_isPreferredHeightDirty;
+
+ protected bool m_isCalculatingPreferredValues;
+ private int m_recursiveCount;
+
+ /// <summary>
+ /// Compute the rendered width of the text object.
+ /// </summary>
+ public virtual float renderedWidth { get { return GetRenderedWidth(); } }
+
+
+ /// <summary>
+ /// Compute the rendered height of the text object.
+ /// </summary>
+ public virtual float renderedHeight { get { return GetRenderedHeight(); } }
+
+
+ /// <summary>
+ ///
+ /// </summary>
+ public int layoutPriority { get { return m_layoutPriority; } }
+ protected int m_layoutPriority = 0;
+
+ protected bool m_isCalculateSizeRequired = false;
+ protected bool m_isLayoutDirty;
+
+ protected bool m_verticesAlreadyDirty;
+ protected bool m_layoutAlreadyDirty;
+
+ protected bool m_isAwake;
+ internal bool m_isWaitingOnResourceLoad;
+
+ internal bool m_isInputParsingRequired = false; // Used to determine if the input text needs to be re-parsed.
+
+ // Protected Fields
+ internal enum TextInputSources { Text = 0, SetText = 1, SetCharArray = 2, String = 3 };
+ //[SerializeField]
+ internal TextInputSources m_inputSource;
+ protected string old_text; // Used by SetText to determine if the text has changed.
+ //protected float old_arg0, old_arg1, old_arg2; // Used by SetText to determine if the args have changed.
+
+
+ protected float m_fontScale; // Scaling of the font based on Atlas true Font Size and Rendered Font Size.
+ protected float m_fontScaleMultiplier; // Used for handling of superscript and subscript.
+
+ protected char[] m_htmlTag = new char[128]; // Maximum length of rich text tag. This is preallocated to avoid GC.
+ protected RichTextTagAttribute[] m_xmlAttribute = new RichTextTagAttribute[8];
+
+ protected float[] m_attributeParameterValues = new float[16];
+
+ protected float tag_LineIndent = 0;
+ protected float tag_Indent = 0;
+ protected TMP_RichTextTagStack<float> m_indentStack = new TMP_RichTextTagStack<float>(new float[16]);
+ protected bool tag_NoParsing;
+ //protected TMP_LinkInfo tag_LinkInfo = new TMP_LinkInfo();
+
+ protected bool m_isParsingText;
+ protected Matrix4x4 m_FXMatrix;
+ protected bool m_isFXMatrixSet;
+
+
+ protected UnicodeChar[] m_TextParsingBuffer; // This array holds the characters to be processed by GenerateMesh();
+
+ protected struct UnicodeChar
+ {
+ public int unicode;
+ public int stringIndex;
+ public int length;
+ }
+ //protected UnicodeChar[] m_InternalParsingBuffer;
+
+ private TMP_CharacterInfo[] m_internalCharacterInfo; // Used by functions to calculate preferred values.
+ protected char[] m_input_CharArray = new char[256]; // This array hold the characters from the SetText();
+ private int m_charArray_Length = 0;
+ protected int m_totalCharacterCount;
+
+ // Structures used to save the state of the text layout in conjunction with line breaking / word wrapping.
+ protected WordWrapState m_SavedWordWrapState = new WordWrapState();
+ protected WordWrapState m_SavedLineState = new WordWrapState();
+ //protected WordWrapState m_SavedAlignment = new WordWrapState ();
+
+
+ // Fields whose state is saved in conjunction with text parsing and word wrapping.
+ protected int m_characterCount;
+ //protected int m_visibleCharacterCount;
+ //protected int m_visibleSpriteCount;
+ protected int m_firstCharacterOfLine;
+ protected int m_firstVisibleCharacterOfLine;
+ protected int m_lastCharacterOfLine;
+ protected int m_lastVisibleCharacterOfLine;
+ protected int m_lineNumber;
+ protected int m_lineVisibleCharacterCount;
+ protected int m_pageNumber;
+ protected float m_maxAscender;
+ protected float m_maxCapHeight;
+ protected float m_maxDescender;
+ protected float m_maxLineAscender;
+ protected float m_maxLineDescender;
+ protected float m_startOfLineAscender;
+ //protected float m_maxFontScale;
+ protected float m_lineOffset;
+ protected Extents m_meshExtents;
+
+
+ // Fields used for vertex colors
+ protected Color32 m_htmlColor = new Color(255, 255, 255, 128);
+ protected TMP_RichTextTagStack<Color32> m_colorStack = new TMP_RichTextTagStack<Color32>(new Color32[16]);
+ protected TMP_RichTextTagStack<Color32> m_underlineColorStack = new TMP_RichTextTagStack<Color32>(new Color32[16]);
+ protected TMP_RichTextTagStack<Color32> m_strikethroughColorStack = new TMP_RichTextTagStack<Color32>(new Color32[16]);
+ protected TMP_RichTextTagStack<Color32> m_highlightColorStack = new TMP_RichTextTagStack<Color32>(new Color32[16]);
+
+ protected TMP_ColorGradient m_colorGradientPreset;
+ protected TMP_RichTextTagStack<TMP_ColorGradient> m_colorGradientStack = new TMP_RichTextTagStack<TMP_ColorGradient>(new TMP_ColorGradient[16]);
+
+ protected float m_tabSpacing = 0;
+ protected float m_spacing = 0;
+
+
+ //protected bool IsRectTransformDriven;
+
+
+ // STYLE TAGS
+ protected TMP_RichTextTagStack<int> m_styleStack = new TMP_RichTextTagStack<int>(new int[16]);
+ protected TMP_RichTextTagStack<int> m_actionStack = new TMP_RichTextTagStack<int>(new int[16]);
+
+ protected float m_padding = 0;
+ protected float m_baselineOffset; // Used for superscript and subscript.
+ protected TMP_RichTextTagStack<float> m_baselineOffsetStack = new TMP_RichTextTagStack<float>(new float[16]);
+ protected float m_xAdvance; // Tracks x advancement from character to character.
+
+ protected TMP_TextElementType m_textElementType;
+ protected TMP_TextElement m_cached_TextElement; // Glyph / Character information is cached into this variable which is faster than having to fetch from the Dictionary multiple times.
+ protected TMP_Character m_cached_Underline_Character; // Same as above but for the underline character which is used for Underline.
+ protected TMP_Character m_cached_Ellipsis_Character;
+
+ protected TMP_SpriteAsset m_defaultSpriteAsset;
+ protected TMP_SpriteAsset m_currentSpriteAsset;
+ protected int m_spriteCount = 0;
+ protected int m_spriteIndex;
+ protected int m_spriteAnimationID;
+ //protected TMP_XmlTagStack<int> m_spriteAnimationStack = new TMP_XmlTagStack<int>(new int[16]);
+
+
+ /// <summary>
+ /// Method which derived classes need to override to load Font Assets.
+ /// </summary>
+ protected virtual void LoadFontAsset() { }
+
+ /// <summary>
+ /// Function called internally when a new shared material is assigned via the fontSharedMaterial property.
+ /// </summary>
+ /// <param name="mat"></param>
+ protected virtual void SetSharedMaterial(Material mat) { }
+
+ /// <summary>
+ /// Function called internally when a new material is assigned via the fontMaterial property.
+ /// </summary>
+ protected virtual Material GetMaterial(Material mat) { return null; }
+
+ /// <summary>
+ /// Function called internally when assigning a new base material.
+ /// </summary>
+ /// <param name="mat"></param>
+ protected virtual void SetFontBaseMaterial(Material mat) { }
+
+ /// <summary>
+ /// Method which returns an array containing the materials used by the text object.
+ /// </summary>
+ /// <returns></returns>
+ protected virtual Material[] GetSharedMaterials() { return null; }
+
+ /// <summary>
+ ///
+ /// </summary>
+ protected virtual void SetSharedMaterials(Material[] materials) { }
+
+ /// <summary>
+ /// Method returning instances of the materials used by the text object.
+ /// </summary>
+ /// <returns></returns>
+ protected virtual Material[] GetMaterials(Material[] mats) { return null; }
+
+ /// <summary>
+ /// Method to set the materials of the text and sub text objects.
+ /// </summary>
+ /// <param name="mats"></param>
+ //protected virtual void SetMaterials (Material[] mats) { }
+
+ /// <summary>
+ /// Function used to create an instance of the material
+ /// </summary>
+ /// <param name="source"></param>
+ /// <returns></returns>
+ protected virtual Material CreateMaterialInstance(Material source)
+ {
+ Material mat = new Material(source);
+ mat.shaderKeywords = source.shaderKeywords;
+ mat.name += " (Instance)";
+
+ return mat;
+ }
+
+ protected void SetVertexColorGradient(TMP_ColorGradient gradient)
+ {
+ if (gradient == null) return;
+
+ m_fontColorGradient.bottomLeft = gradient.bottomLeft;
+ m_fontColorGradient.bottomRight = gradient.bottomRight;
+ m_fontColorGradient.topLeft = gradient.topLeft;
+ m_fontColorGradient.topRight = gradient.topRight;
+
+ SetVerticesDirty();
+ }
+
+ /// <summary>
+ /// Function to control the sorting of the geometry of the text object.
+ /// </summary>
+ protected void SetTextSortingOrder(VertexSortingOrder order)
+ {
+
+ }
+
+ /// <summary>
+ /// Function to sort the geometry of the text object in accordance to the provided order.
+ /// </summary>
+ /// <param name="order"></param>
+ protected void SetTextSortingOrder(int[] order)
+ {
+
+ }
+
+ /// <summary>
+ /// Function called internally to set the face color of the material. This will results in an instance of the material.
+ /// </summary>
+ /// <param name="color"></param>
+ protected virtual void SetFaceColor(Color32 color) { }
+
+ /// <summary>
+ /// Function called internally to set the outline color of the material. This will results in an instance of the material.
+ /// </summary>
+ /// <param name="color"></param>
+ protected virtual void SetOutlineColor(Color32 color) { }
+
+ /// <summary>
+ /// Function called internally to set the outline thickness property of the material. This will results in an instance of the material.
+ /// </summary>
+ /// <param name="thickness"></param>
+ protected virtual void SetOutlineThickness(float thickness) { }
+
+ /// <summary>
+ /// Set the Render Queue and ZTest mode on the current material
+ /// </summary>
+ protected virtual void SetShaderDepth() { }
+
+ /// <summary>
+ /// Set the culling mode on the material.
+ /// </summary>
+ protected virtual void SetCulling() { }
+
+ /// <summary>
+ /// Get the padding value for the currently assigned material
+ /// </summary>
+ /// <returns></returns>
+ protected virtual float GetPaddingForMaterial() { return 0; }
+
+
+ /// <summary>
+ /// Get the padding value for the given material
+ /// </summary>
+ /// <returns></returns>
+ protected virtual float GetPaddingForMaterial(Material mat) { return 0; }
+
+
+ /// <summary>
+ /// Method to return the local corners of the Text Container or RectTransform.
+ /// </summary>
+ /// <returns></returns>
+ protected virtual Vector3[] GetTextContainerLocalCorners() { return null; }
+
+
+ // PUBLIC FUNCTIONS
+ protected bool m_ignoreActiveState;
+ /// <summary>
+ /// Function to force the regeneration of the text object.
+ /// </summary>
+ public virtual void ForceMeshUpdate() { }
+
+
+ /// <summary>
+ /// Method used for resetting vertex layout when switching to and from Volumetric Text mode.
+ /// </summary>
+ /// <param name="updateMesh"></param>
+ //protected virtual void ResetVertexLayout() { }
+
+
+ /// <summary>
+ /// Function to force the regeneration of the text object.
+ /// </summary>
+ /// <param name="ignoreActiveState">If set to true, the text object will be regenerated regardless of is active state.</param>
+ public virtual void ForceMeshUpdate(bool ignoreActiveState) { }
+
+
+ /// <summary>
+ /// Internal function used by the Text Input Field to populate TMP_TextInfo data.
+ /// </summary>
+ internal void SetTextInternal(string text)
+ {
+ m_text = text;
+ m_renderMode = TextRenderFlags.DontRender;
+ m_isInputParsingRequired = true;
+ ForceMeshUpdate();
+ m_renderMode = TextRenderFlags.Render;
+ }
+
+ /// <summary>
+ /// Function to force the regeneration of the text object.
+ /// </summary>
+ /// <param name="flags"> Flags to control which portions of the geometry gets uploaded.</param>
+ //public virtual void ForceMeshUpdate(TMP_VertexDataUpdateFlags flags) { }
+
+
+ /// <summary>
+ /// Function to update the geometry of the main and sub text objects.
+ /// </summary>
+ /// <param name="mesh"></param>
+ /// <param name="index"></param>
+ public virtual void UpdateGeometry(Mesh mesh, int index) { }
+
+
+ /// <summary>
+ /// Function to push the updated vertex data into the mesh and renderer.
+ /// </summary>
+ public virtual void UpdateVertexData(TMP_VertexDataUpdateFlags flags) { }
+
+
+ /// <summary>
+ /// Function to push the updated vertex data into the mesh and renderer.
+ /// </summary>
+ public virtual void UpdateVertexData() { }
+
+
+ /// <summary>
+ /// Function to push a new set of vertices to the mesh.
+ /// </summary>
+ /// <param name="vertices"></param>
+ public virtual void SetVertices(Vector3[] vertices) { }
+
+
+ /// <summary>
+ /// Function to be used to force recomputing of character padding when Shader / Material properties have been changed via script.
+ /// </summary>
+ public virtual void UpdateMeshPadding() { }
+
+
+ /// <summary>
+ ///
+ /// </summary>
+ //public virtual new void UpdateGeometry() { }
+
+
+ /// <summary>
+ /// Tweens the CanvasRenderer color associated with this Graphic.
+ /// </summary>
+ /// <param name="targetColor">Target color.</param>
+ /// <param name="duration">Tween duration.</param>
+ /// <param name="ignoreTimeScale">Should ignore Time.scale?</param>
+ /// <param name="useAlpha">Should also Tween the alpha channel?</param>
+ public override void CrossFadeColor(Color targetColor, float duration, bool ignoreTimeScale, bool useAlpha)
+ {
+ base.CrossFadeColor(targetColor, duration, ignoreTimeScale, useAlpha);
+ InternalCrossFadeColor(targetColor, duration, ignoreTimeScale, useAlpha);
+ }
+
+
+ /// <summary>
+ /// Tweens the alpha of the CanvasRenderer color associated with this Graphic.
+ /// </summary>
+ /// <param name="alpha">Target alpha.</param>
+ /// <param name="duration">Duration of the tween in seconds.</param>
+ /// <param name="ignoreTimeScale">Should ignore Time.scale?</param>
+ public override void CrossFadeAlpha(float alpha, float duration, bool ignoreTimeScale)
+ {
+ base.CrossFadeAlpha(alpha, duration, ignoreTimeScale);
+ InternalCrossFadeAlpha(alpha, duration, ignoreTimeScale);
+ }
+
+
+ /// <summary>
+ ///
+ /// </summary>
+ /// <param name="targetColor"></param>
+ /// <param name="duration"></param>
+ /// <param name="ignoreTimeScale"></param>
+ /// <param name="useAlpha"></param>
+ /// <param name="useRGB"></param>
+ protected virtual void InternalCrossFadeColor(Color targetColor, float duration, bool ignoreTimeScale, bool useAlpha) { }
+
+
+ /// <summary>
+ ///
+ /// </summary>
+ /// <param name="alpha"></param>
+ /// <param name="duration"></param>
+ /// <param name="ignoreTimeScale"></param>
+ protected virtual void InternalCrossFadeAlpha(float alpha, float duration, bool ignoreTimeScale) { }
+
+
+ /// <summary>
+ /// Method to parse the input text based on its source
+ /// </summary>
+ protected void ParseInputText()
+ {
+ //Debug.Log("Re-parsing Text.");
+ ////Profiler.BeginSample("ParseInputText()");
+
+ m_isInputParsingRequired = false;
+
+ switch (m_inputSource)
+ {
+ case TextInputSources.String:
+ case TextInputSources.Text:
+ StringToCharArray(m_text, ref m_TextParsingBuffer);
+ break;
+ case TextInputSources.SetText:
+ SetTextArrayToCharArray(m_input_CharArray, ref m_TextParsingBuffer);
+ break;
+ case TextInputSources.SetCharArray:
+ break;
+ }
+
+ SetArraySizes(m_TextParsingBuffer);
+ ////Profiler.EndSample();
+ }
+
+
+ /// <summary>
+ ///
+ /// </summary>
+ /// <param name="text"></param>
+ public void SetText(string text)
+ {
+ SetText(text, true);
+ }
+
+
+ /// <summary>
+ ///
+ /// </summary>
+ /// <param name="text"></param>
+ public void SetText(string text, bool syncTextInputBox)
+ {
+ //if (text == old_text) return;
+
+ //old_text = text;
+
+ m_inputSource = TextInputSources.SetCharArray;
+
+ StringToCharArray(text, ref m_TextParsingBuffer);
+
+ #if UNITY_EDITOR
+ // Set the text in the Text Input Box in the Unity Editor only.
+ // TODO: Could revise to convert to string literal
+ if (syncTextInputBox)
+ m_text = text;
+ #endif
+
+ m_isInputParsingRequired = true;
+ m_havePropertiesChanged = true;
+ m_isCalculateSizeRequired = true;
+
+ SetVerticesDirty();
+ SetLayoutDirty();
+ }
+
+
+ /// <summary>
+ /// <para>Formatted string containing a pattern and a value representing the text to be rendered.</para>
+ /// <para>ex. TextMeshPro.SetText ("Number is {0:1}.", 5.56f);</para>
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ /// <param name="text">String containing the pattern."</param>
+ /// <param name="arg0">Value is a float.</param>
+ public void SetText(string text, float arg0)
+ {
+ SetText(text, arg0, 255, 255);
+ }
+
+ /// <summary>
+ /// <para>Formatted string containing a pattern and a value representing the text to be rendered.</para>
+ /// <para>ex. TextMeshPro.SetText ("First number is {0} and second is {1:2}.", 10, 5.756f);</para>
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ /// <param name="text">String containing the pattern."</param>
+ /// <param name="arg0">Value is a float.</param>
+ /// <param name="arg1">Value is a float.</param>
+ public void SetText(string text, float arg0, float arg1)
+ {
+ SetText(text, arg0, arg1, 255);
+ }
+
+ /// <summary>
+ /// <para>Formatted string containing a pattern and a value representing the text to be rendered.</para>
+ /// <para>ex. TextMeshPro.SetText ("A = {0}, B = {1} and C = {2}.", 2, 5, 7);</para>
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ /// <param name="text">String containing the pattern."</param>
+ /// <param name="arg0">Value is a float.</param>
+ /// <param name="arg1">Value is a float.</param>
+ /// <param name="arg2">Value is a float.</param>
+ public void SetText(string text, float arg0, float arg1, float arg2)
+ {
+ int decimalPrecision = 0;
+ int index = 0;
+
+ for (int i = 0; i < text.Length; i++)
+ {
+ char c = text[i];
+
+ if (c == 123) // '{'
+ {
+ // Check if user is requesting some decimal precision. Format is {0:2}
+ if (text[i + 2] == 58) // ':'
+ {
+ decimalPrecision = text[i + 3] - 48;
+ }
+
+ switch (text[i + 1] - 48)
+ {
+ case 0: // 1st Arg
+ AddFloatToCharArray(arg0, ref index, decimalPrecision);
+ break;
+ case 1: // 2nd Arg
+ AddFloatToCharArray(arg1, ref index, decimalPrecision);
+ break;
+ case 2: // 3rd Arg
+ AddFloatToCharArray(arg2, ref index, decimalPrecision);
+ break;
+ }
+
+ if (text[i + 2] == 58)
+ i += 4;
+ else
+ i += 2;
+
+ continue;
+ }
+ m_input_CharArray[index] = c;
+ index += 1;
+ }
+
+ m_input_CharArray[index] = (char)0;
+ m_charArray_Length = index; // Set the length to where this '0' termination is.
+
+ #if UNITY_EDITOR
+ // Create new string to be displayed in the Input Text Box of the Editor Panel.
+ m_text = new string(m_input_CharArray, 0, index);
+ #endif
+
+ m_inputSource = TextInputSources.SetText;
+ m_isInputParsingRequired = true;
+ m_havePropertiesChanged = true;
+ m_isCalculateSizeRequired = true;
+
+ SetVerticesDirty();
+ SetLayoutDirty();
+ }
+
+
+ /// <summary>
+ /// Set the text using a StringBuilder.
+ /// </summary>
+ /// <description>
+ /// Using a StringBuilder instead of concatenating strings prevents memory pollution with temporary objects.
+ /// </description>
+ /// <param name="text">StringBuilder with text to display.</param>
+ public void SetText(StringBuilder text)
+ {
+ m_inputSource = TextInputSources.SetCharArray;
+
+ #if UNITY_EDITOR
+ // Set the text in the Text Input Box in the Unity Editor only.
+ m_text = text.ToString();
+ #endif
+
+ StringBuilderToIntArray(text, ref m_TextParsingBuffer);
+
+ m_isInputParsingRequired = true;
+ m_havePropertiesChanged = true;
+ m_isCalculateSizeRequired = true;
+
+ SetVerticesDirty();
+ SetLayoutDirty();
+ }
+
+
+ /// <summary>
+ /// Character array containing the text to be displayed.
+ /// </summary>
+ /// <param name="sourceText"></param>
+ public void SetCharArray(char[] sourceText)
+ {
+ // Initialize internal character buffer if necessary
+ if (m_TextParsingBuffer == null) m_TextParsingBuffer = new UnicodeChar[8];
+
+ #if UNITY_EDITOR
+ // Create new string to be displayed in the Input Text Box of the Editor Panel.
+ if (sourceText == null || sourceText.Length == 0)
+ m_text = string.Empty;
+ else
+ m_text = new string(sourceText);
+ #endif
+
+ // Clear the Style stack.
+ m_styleStack.Clear();
+
+ int writeIndex = 0;
+
+ for (int i = 0; sourceText != null && i < sourceText.Length; i++)
+ {
+ if (sourceText[i] == 92 && i < sourceText.Length - 1)
+ {
+ switch ((int)sourceText[i + 1])
+ {
+ case 110: // \n LineFeed
+ if (writeIndex == m_TextParsingBuffer.Length) ResizeInternalArray(ref m_TextParsingBuffer);
+
+ m_TextParsingBuffer[writeIndex].unicode = 10;
+ i += 1;
+ writeIndex += 1;
+ continue;
+ case 114: // \r LineFeed
+ if (writeIndex == m_TextParsingBuffer.Length) ResizeInternalArray(ref m_TextParsingBuffer);
+
+ m_TextParsingBuffer[writeIndex].unicode = 13;
+ i += 1;
+ writeIndex += 1;
+ continue;
+ case 116: // \t Tab
+ if (writeIndex == m_TextParsingBuffer.Length) ResizeInternalArray(ref m_TextParsingBuffer);
+
+ m_TextParsingBuffer[writeIndex].unicode = 9;
+ i += 1;
+ writeIndex += 1;
+ continue;
+ }
+ }
+
+ // Handle inline replacement of <stlye> and <br> tags.
+ if (sourceText[i] == 60)
+ {
+ if (IsTagName(ref sourceText, "<BR>", i))
+ {
+ if (writeIndex == m_TextParsingBuffer.Length) ResizeInternalArray(ref m_TextParsingBuffer);
+
+ m_TextParsingBuffer[writeIndex].unicode = 10; ;
+ writeIndex += 1;
+ i += 3;
+
+ continue;
+ }
+ else if (IsTagName(ref sourceText, "<STYLE=", i))
+ {
+ if (ReplaceOpeningStyleTag(ref sourceText, i, out int srcOffset, ref m_TextParsingBuffer, ref writeIndex))
+ {
+ i = srcOffset;
+ continue;
+ }
+ }
+ else if (IsTagName(ref sourceText, "</STYLE>", i))
+ {
+ ReplaceClosingStyleTag(ref sourceText, i, ref m_TextParsingBuffer, ref writeIndex);
+
+ // Strip </style> even if style is invalid.
+ i += 7;
+ continue;
+ }
+ }
+
+ if (writeIndex == m_TextParsingBuffer.Length) ResizeInternalArray(ref m_TextParsingBuffer);
+
+ m_TextParsingBuffer[writeIndex].unicode = sourceText[i];
+ writeIndex += 1;
+ }
+
+ if (writeIndex == m_TextParsingBuffer.Length) ResizeInternalArray(ref m_TextParsingBuffer);
+
+ m_TextParsingBuffer[writeIndex].unicode = 0;
+
+ m_inputSource = TextInputSources.SetCharArray;
+ m_isInputParsingRequired = true;
+ m_havePropertiesChanged = true;
+ m_isCalculateSizeRequired = true;
+
+ SetVerticesDirty();
+ SetLayoutDirty();
+ }
+
+
+ /// <summary>
+ /// Character array containing the text to be displayed.
+ /// </summary>
+ /// <param name="sourceText"></param>
+ public void SetCharArray(char[] sourceText, int start, int length)
+ {
+ // Initialize internal character buffer if necessary
+ if (m_TextParsingBuffer == null) m_TextParsingBuffer = new UnicodeChar[8];
+
+ #if UNITY_EDITOR
+ // Create new string to be displayed in the Input Text Box of the Editor Panel.
+ if (sourceText == null || sourceText.Length == 0 || length == 0)
+ {
+ m_text = string.Empty;
+ start = 0;
+ length = 0;
+ }
+ else
+ {
+ // TODO: Add potential range check on start + length relative to array size.
+ m_text = new string(sourceText, start, length);
+ }
+ #endif
+
+ // Clear the Style stack.
+ m_styleStack.Clear();
+
+ int writeIndex = 0;
+
+ int i = start;
+ int end = start + length;
+ for (; i < end; i++)
+ {
+ if (sourceText[i] == 92 && i < length - 1)
+ {
+ switch ((int)sourceText[i + 1])
+ {
+ case 110: // \n LineFeed
+ if (writeIndex == m_TextParsingBuffer.Length) ResizeInternalArray(ref m_TextParsingBuffer);
+
+ m_TextParsingBuffer[writeIndex].unicode = 10;
+ i += 1;
+ writeIndex += 1;
+ continue;
+ case 114: // \r LineFeed
+ if (writeIndex == m_TextParsingBuffer.Length) ResizeInternalArray(ref m_TextParsingBuffer);
+
+ m_TextParsingBuffer[writeIndex].unicode = 13;
+ i += 1;
+ writeIndex += 1;
+ continue;
+ case 116: // \t Tab
+ if (writeIndex == m_TextParsingBuffer.Length) ResizeInternalArray(ref m_TextParsingBuffer);
+
+ m_TextParsingBuffer[writeIndex].unicode = 9;
+ i += 1;
+ writeIndex += 1;
+ continue;
+ }
+ }
+
+ // Handle inline replacement of <stlye> and <br> tags.
+ if (sourceText[i] == 60)
+ {
+ if (IsTagName(ref sourceText, "<BR>", i))
+ {
+ if (writeIndex == m_TextParsingBuffer.Length) ResizeInternalArray(ref m_TextParsingBuffer);
+
+ m_TextParsingBuffer[writeIndex].unicode = 10;
+ writeIndex += 1;
+ i += 3;
+
+ continue;
+ }
+ else if (IsTagName(ref sourceText, "<STYLE=", i))
+ {
+ if (ReplaceOpeningStyleTag(ref sourceText, i, out int srcOffset, ref m_TextParsingBuffer, ref writeIndex))
+ {
+ i = srcOffset;
+ continue;
+ }
+ }
+ else if (IsTagName(ref sourceText, "</STYLE>", i))
+ {
+ ReplaceClosingStyleTag(ref sourceText, i, ref m_TextParsingBuffer, ref writeIndex);
+
+ // Strip </style> even if style is invalid.
+ i += 7;
+ continue;
+ }
+ }
+
+ if (writeIndex == m_TextParsingBuffer.Length) ResizeInternalArray(ref m_TextParsingBuffer);
+
+ m_TextParsingBuffer[writeIndex].unicode = sourceText[i];
+ writeIndex += 1;
+ }
+
+ if (writeIndex == m_TextParsingBuffer.Length) ResizeInternalArray(ref m_TextParsingBuffer);
+
+ m_TextParsingBuffer[writeIndex].unicode = 0;
+
+ m_inputSource = TextInputSources.SetCharArray;
+ m_havePropertiesChanged = true;
+ m_isInputParsingRequired = true;
+ m_isCalculateSizeRequired = true;
+
+ SetVerticesDirty();
+ SetLayoutDirty();
+ }
+
+
+ /// <summary>
+ /// Character array containing the text to be displayed.
+ /// </summary>
+ /// <param name="sourceText"></param>
+ public void SetCharArray(int[] sourceText, int start, int length)
+ {
+ // Initialize internal character buffer if necessary
+ if (m_TextParsingBuffer == null) m_TextParsingBuffer = new UnicodeChar[8];
+
+ #if UNITY_EDITOR
+ // Create new string to be displayed in the Input Text Box of the Editor Panel.
+ if (sourceText == null || sourceText.Length == 0 || length == 0)
+ {
+ m_text = string.Empty;
+ start = 0;
+ length = 0;
+ }
+ else
+ {
+ m_text = sourceText.IntToString(start, length);
+ }
+ #endif
+
+ // Clear the Style stack.
+ m_styleStack.Clear();
+
+ int writeIndex = 0;
+
+ int end = start + length;
+ for (int i = start; i < end && i < sourceText.Length; i++)
+ {
+ if (sourceText[i] == 92 && i < length - 1)
+ {
+ switch ((int)sourceText[i + 1])
+ {
+ case 110: // \n LineFeed
+ if (writeIndex == m_TextParsingBuffer.Length) ResizeInternalArray(ref m_TextParsingBuffer);
+
+ m_TextParsingBuffer[writeIndex].unicode = 10;
+ i += 1;
+ writeIndex += 1;
+ continue;
+ case 114: // \r LineFeed
+ if (writeIndex == m_TextParsingBuffer.Length) ResizeInternalArray(ref m_TextParsingBuffer);
+
+ m_TextParsingBuffer[writeIndex].unicode = 13;
+ i += 1;
+ writeIndex += 1;
+ continue;
+ case 116: // \t Tab
+ if (writeIndex == m_TextParsingBuffer.Length) ResizeInternalArray(ref m_TextParsingBuffer);
+
+ m_TextParsingBuffer[writeIndex].unicode = 9;
+ i += 1;
+ writeIndex += 1;
+ continue;
+ }
+ }
+
+ // Handle inline replacement of <stlye> and <br> tags.
+ if (sourceText[i] == 60)
+ {
+ if (IsTagName(ref sourceText, "<BR>", i))
+ {
+ if (writeIndex == m_TextParsingBuffer.Length) ResizeInternalArray(ref m_TextParsingBuffer);
+
+ m_TextParsingBuffer[writeIndex].unicode = 10;
+ writeIndex += 1;
+ i += 3;
+
+ continue;
+ }
+ else if (IsTagName(ref sourceText, "<STYLE=", i))
+ {
+ if (ReplaceOpeningStyleTag(ref sourceText, i, out int srcOffset, ref m_TextParsingBuffer, ref writeIndex))
+ {
+ i = srcOffset;
+ continue;
+ }
+ }
+ else if (IsTagName(ref sourceText, "</STYLE>", i))
+ {
+ ReplaceClosingStyleTag(ref sourceText, i, ref m_TextParsingBuffer, ref writeIndex);
+
+ // Strip </style> even if style is invalid.
+ i += 7;
+ continue;
+ }
+ }
+
+ if (writeIndex == m_TextParsingBuffer.Length) ResizeInternalArray(ref m_TextParsingBuffer);
+
+ m_TextParsingBuffer[writeIndex].unicode = sourceText[i];
+ writeIndex += 1;
+ }
+
+ if (writeIndex == m_TextParsingBuffer.Length) ResizeInternalArray(ref m_TextParsingBuffer);
+
+ m_TextParsingBuffer[writeIndex].unicode = 0;
+
+ m_inputSource = TextInputSources.SetCharArray;
+ m_havePropertiesChanged = true;
+ m_isInputParsingRequired = true;
+ m_isCalculateSizeRequired = true;
+
+ SetVerticesDirty();
+ SetLayoutDirty();
+ }
+
+
+ /// <summary>
+ /// Copies Content of formatted SetText() to charBuffer.
+ /// </summary>
+ /// <param name="sourceText"></param>
+ /// <param name="charBuffer"></param>
+ protected void SetTextArrayToCharArray(char[] sourceText, ref UnicodeChar[] charBuffer)
+ {
+ //Debug.Log("SetText Array to Char called.");
+ if (sourceText == null || m_charArray_Length == 0)
+ return;
+
+ if (charBuffer == null) charBuffer = new UnicodeChar[8];
+
+ // Clear the Style stack.
+ m_styleStack.Clear();
+
+ int writeIndex = 0;
+
+ for (int i = 0; i < m_charArray_Length; i++)
+ {
+ // Handle UTF-32 in the input text (string).
+ if (char.IsHighSurrogate(sourceText[i]) && char.IsLowSurrogate(sourceText[i + 1]))
+ {
+ if (writeIndex == charBuffer.Length) ResizeInternalArray(ref charBuffer);
+
+ charBuffer[writeIndex].unicode = char.ConvertToUtf32(sourceText[i], sourceText[i + 1]);
+ i += 1;
+ writeIndex += 1;
+ continue;
+ }
+
+ // Handle inline replacement of <stlye> and <br> tags.
+ if (sourceText[i] == 60)
+ {
+ if (IsTagName(ref sourceText, "<BR>", i))
+ {
+ if (writeIndex == charBuffer.Length) ResizeInternalArray(ref charBuffer);
+
+ charBuffer[writeIndex].unicode = 10;
+ writeIndex += 1;
+ i += 3;
+
+ continue;
+ }
+ else if (IsTagName(ref sourceText, "<STYLE=", i))
+ {
+ if (ReplaceOpeningStyleTag(ref sourceText, i, out int srcOffset, ref charBuffer, ref writeIndex))
+ {
+ i = srcOffset;
+ continue;
+ }
+ }
+ else if (IsTagName(ref sourceText, "</STYLE>", i))
+ {
+ ReplaceClosingStyleTag(ref sourceText, i, ref charBuffer, ref writeIndex);
+
+ // Strip </style> even if style is invalid.
+ i += 7;
+ continue;
+ }
+ }
+
+ if (writeIndex == charBuffer.Length) ResizeInternalArray(ref charBuffer);
+
+ charBuffer[writeIndex].unicode = sourceText[i];
+ writeIndex += 1;
+ }
+
+ if (writeIndex == charBuffer.Length) ResizeInternalArray(ref charBuffer);
+
+ charBuffer[writeIndex].unicode = 0;
+ }
+
+
+ /// <summary>
+ /// Method to store the content of a string into an integer array.
+ /// </summary>
+ /// <param name="sourceText"></param>
+ /// <param name="charBuffer"></param>
+ protected void StringToCharArray(string sourceText, ref UnicodeChar[] charBuffer)
+ {
+ if (sourceText == null)
+ {
+ charBuffer[0].unicode = 0;
+ return;
+ }
+
+ if (charBuffer == null) charBuffer = new UnicodeChar[8];
+
+ // Clear the Style stack.
+ m_styleStack.SetDefault(0);
+
+ int writeIndex = 0;
+
+ for (int i = 0; i < sourceText.Length; i++)
+ {
+ if (m_inputSource == TextInputSources.Text && sourceText[i] == 92 && sourceText.Length > i + 1)
+ {
+ switch ((int)sourceText[i + 1])
+ {
+ case 85: // \U00000000 for UTF-32 Unicode
+ if (sourceText.Length > i + 9)
+ {
+ if (writeIndex == charBuffer.Length) ResizeInternalArray(ref charBuffer);
+
+ charBuffer[writeIndex].unicode = GetUTF32(sourceText, i + 2);
+ charBuffer[writeIndex].stringIndex = i;
+ charBuffer[writeIndex].length = 10;
+
+ i += 9;
+ writeIndex += 1;
+ continue;
+ }
+ break;
+ case 92: // \ escape
+ if (!m_parseCtrlCharacters) break;
+
+ if (sourceText.Length <= i + 2) break;
+
+ if (writeIndex + 2 > charBuffer.Length) ResizeInternalArray(ref charBuffer);
+
+ charBuffer[writeIndex].unicode = sourceText[i + 1];
+ charBuffer[writeIndex + 1].unicode = sourceText[i + 2];
+ i += 2;
+ writeIndex += 2;
+ continue;
+ case 110: // \n LineFeed
+ if (!m_parseCtrlCharacters) break;
+
+ if (writeIndex == charBuffer.Length) ResizeInternalArray(ref charBuffer);
+
+ charBuffer[writeIndex].unicode = 10;
+ charBuffer[writeIndex].stringIndex = i;
+ charBuffer[writeIndex].length = 1;
+
+ i += 1;
+ writeIndex += 1;
+ continue;
+ case 114: // \r
+ if (!m_parseCtrlCharacters) break;
+
+ if (writeIndex == charBuffer.Length) ResizeInternalArray(ref charBuffer);
+
+ charBuffer[writeIndex].unicode = 13;
+ charBuffer[writeIndex].stringIndex = i;
+ charBuffer[writeIndex].length = 1;
+
+ i += 1;
+ writeIndex += 1;
+ continue;
+ case 116: // \t Tab
+ if (!m_parseCtrlCharacters) break;
+
+ if (writeIndex == charBuffer.Length) ResizeInternalArray(ref charBuffer);
+
+ charBuffer[writeIndex].unicode = 9;
+ charBuffer[writeIndex].stringIndex = i;
+ charBuffer[writeIndex].length = 1;
+
+ i += 1;
+ writeIndex += 1;
+ continue;
+ case 117: // \u0000 for UTF-16 Unicode
+ if (sourceText.Length > i + 5)
+ {
+ if (writeIndex == charBuffer.Length) ResizeInternalArray(ref charBuffer);
+
+ charBuffer[writeIndex].unicode = GetUTF16(sourceText, i + 2);
+ charBuffer[writeIndex].stringIndex = i;
+ charBuffer[writeIndex].length = 6;
+
+ i += 5;
+ writeIndex += 1;
+ continue;
+ }
+ break;
+ }
+ }
+
+ // Handle UTF-32 in the input text (string). // Not sure this is needed //
+ if (char.IsHighSurrogate(sourceText[i]) && char.IsLowSurrogate(sourceText[i + 1]))
+ {
+ if (writeIndex == charBuffer.Length) ResizeInternalArray(ref charBuffer);
+
+ charBuffer[writeIndex].unicode = char.ConvertToUtf32(sourceText[i], sourceText[i + 1]);
+ charBuffer[writeIndex].stringIndex = i;
+ charBuffer[writeIndex].length = 2;
+
+ i += 1;
+ writeIndex += 1;
+ continue;
+ }
+
+ //// Handle inline replacement of <stlye> and <br> tags.
+ if (sourceText[i] == 60 && m_isRichText)
+ {
+ if (IsTagName(ref sourceText, "<BR>", i))
+ {
+ if (writeIndex == charBuffer.Length) ResizeInternalArray(ref charBuffer);
+
+ charBuffer[writeIndex].unicode = 10;
+ charBuffer[writeIndex].stringIndex = i;
+ charBuffer[writeIndex].length = 1;
+
+ writeIndex += 1;
+ i += 3;
+
+ continue;
+ }
+ else if (IsTagName(ref sourceText, "<STYLE=", i))
+ {
+ if (ReplaceOpeningStyleTag(ref sourceText, i, out int srcOffset, ref charBuffer, ref writeIndex))
+ {
+ i = srcOffset;
+ continue;
+ }
+ }
+ else if (IsTagName(ref sourceText, "</STYLE>", i))
+ {
+ ReplaceClosingStyleTag(ref sourceText, i, ref charBuffer, ref writeIndex);
+
+ // Strip </style> even if style is invalid.
+ i += 7;
+ continue;
+ }
+ }
+
+ if (writeIndex == charBuffer.Length) ResizeInternalArray(ref charBuffer);
+
+ charBuffer[writeIndex].unicode = sourceText[i];
+ charBuffer[writeIndex].stringIndex = i;
+ charBuffer[writeIndex].length = 1;
+
+ writeIndex += 1;
+ }
+
+ if (writeIndex == charBuffer.Length) ResizeInternalArray(ref charBuffer);
+
+ charBuffer[writeIndex].unicode = 0;
+ }
+
+
+ /// <summary>
+ /// Copy contents of StringBuilder into int array.
+ /// </summary>
+ /// <param name="sourceText">Text to copy.</param>
+ /// <param name="charBuffer">Array to store contents.</param>
+ protected void StringBuilderToIntArray(StringBuilder sourceText, ref UnicodeChar[] charBuffer)
+ {
+ if (sourceText == null)
+ {
+ charBuffer[0].unicode = 0;
+ return;
+ }
+
+ if (charBuffer == null) charBuffer = new UnicodeChar[8];
+
+ // Clear the Style stack.
+ m_styleStack.Clear();
+
+ #if UNITY_EDITOR
+ // Create new string to be displayed in the Input Text Box of the Editor Panel.
+ m_text = sourceText.ToString();
+ #endif
+
+ int writeIndex = 0;
+
+ for (int i = 0; i < sourceText.Length; i++)
+ {
+ if (m_parseCtrlCharacters && sourceText[i] == 92 && sourceText.Length > i + 1)
+ {
+ switch ((int)sourceText[i + 1])
+ {
+ case 85: // \U00000000 for UTF-32 Unicode
+ if (sourceText.Length > i + 9)
+ {
+ if (writeIndex == charBuffer.Length) ResizeInternalArray(ref charBuffer);
+
+ charBuffer[writeIndex].unicode = GetUTF32(sourceText, i + 2);
+ i += 9;
+ writeIndex += 1;
+ continue;
+ }
+ break;
+ case 92: // \ escape
+ if (sourceText.Length <= i + 2) break;
+
+ if (writeIndex + 2 > charBuffer.Length) ResizeInternalArray(ref charBuffer);
+
+ charBuffer[writeIndex].unicode = sourceText[i + 1];
+ charBuffer[writeIndex + 1].unicode = sourceText[i + 2];
+ i += 2;
+ writeIndex += 2;
+ continue;
+ case 110: // \n LineFeed
+ if (writeIndex == charBuffer.Length) ResizeInternalArray(ref charBuffer);
+
+ charBuffer[writeIndex].unicode = 10;
+ i += 1;
+ writeIndex += 1;
+ continue;
+ case 114: // \r
+ if (writeIndex == charBuffer.Length) ResizeInternalArray(ref charBuffer);
+
+ charBuffer[writeIndex].unicode = 13;
+ i += 1;
+ writeIndex += 1;
+ continue;
+ case 116: // \t Tab
+ if (writeIndex == charBuffer.Length) ResizeInternalArray(ref charBuffer);
+
+ charBuffer[writeIndex].unicode = 9;
+ i += 1;
+ writeIndex += 1;
+ continue;
+ case 117: // \u0000 for UTF-16 Unicode
+ if (sourceText.Length > i + 5)
+ {
+ if (writeIndex == charBuffer.Length) ResizeInternalArray(ref charBuffer);
+
+ charBuffer[writeIndex].unicode = GetUTF16(sourceText, i + 2);
+ i += 5;
+ writeIndex += 1;
+ continue;
+ }
+ break;
+ }
+ }
+
+ // Handle UTF-32 in the input text (string).
+ if (char.IsHighSurrogate(sourceText[i]) && char.IsLowSurrogate(sourceText[i + 1]))
+ {
+ if (writeIndex == charBuffer.Length) ResizeInternalArray(ref charBuffer);
+
+ charBuffer[writeIndex].unicode = char.ConvertToUtf32(sourceText[i], sourceText[i + 1]);
+ i += 1;
+ writeIndex += 1;
+ continue;
+ }
+
+ // Handle inline replacement of <stlye> and <br> tags.
+ if (sourceText[i] == 60)
+ {
+ if (IsTagName(ref sourceText, "<BR>", i))
+ {
+ if (writeIndex == charBuffer.Length) ResizeInternalArray(ref charBuffer);
+
+ charBuffer[writeIndex].unicode = 10;
+ writeIndex += 1;
+ i += 3;
+
+ continue;
+ }
+ else if (IsTagName(ref sourceText, "<STYLE=", i))
+ {
+ if (ReplaceOpeningStyleTag(ref sourceText, i, out int srcOffset, ref charBuffer, ref writeIndex))
+ {
+ i = srcOffset;
+ continue;
+ }
+ }
+ else if (IsTagName(ref sourceText, "</STYLE>", i))
+ {
+ ReplaceClosingStyleTag(ref sourceText, i, ref charBuffer, ref writeIndex);
+
+ // Strip </style> even if style is invalid.
+ i += 7;
+ continue;
+ }
+ }
+
+ if (writeIndex == charBuffer.Length) ResizeInternalArray(ref charBuffer);
+
+ charBuffer[writeIndex].unicode = sourceText[i];
+ writeIndex += 1;
+ }
+
+ if (writeIndex == charBuffer.Length) ResizeInternalArray(ref charBuffer);
+
+ charBuffer[writeIndex].unicode = 0;
+ }
+
+
+ /// <summary>
+ /// Method to handle inline replacement of style tag by opening style definition.
+ /// </summary>
+ /// <param name="sourceText"></param>
+ /// <param name="srcIndex"></param>
+ /// <param name="srcOffset"></param>
+ /// <param name="charBuffer"></param>
+ /// <param name="writeIndex"></param>
+ /// <returns></returns>
+ bool ReplaceOpeningStyleTag(ref string sourceText, int srcIndex, out int srcOffset, ref UnicodeChar[] charBuffer, ref int writeIndex)
+ {
+ // Validate <style> tag.
+ int hashCode = GetTagHashCode(ref sourceText, srcIndex + 7, out srcOffset);
+
+ TMP_Style style = TMP_StyleSheet.GetStyle(hashCode);
+
+ // Return if we don't have a valid style.
+ if (style == null || srcOffset == 0) return false;
+
+ m_styleStack.Add(style.hashCode);
+
+ int styleLength = style.styleOpeningTagArray.Length;
+
+ // Replace <style> tag with opening definition
+ int[] openingTagArray = style.styleOpeningTagArray;
+
+ for (int i = 0; i < styleLength; i++)
+ {
+ int c = openingTagArray[i];
+
+ if (c == 60)
+ {
+ if (IsTagName(ref openingTagArray, "<BR>", i))
+ {
+ if (writeIndex == charBuffer.Length) ResizeInternalArray(ref charBuffer);
+
+ charBuffer[writeIndex].unicode = 10;
+ writeIndex += 1;
+ i += 3;
+
+ continue;
+ }
+ else if (IsTagName(ref openingTagArray, "<STYLE=", i))
+ {
+ if (ReplaceOpeningStyleTag(ref openingTagArray, i, out int offset, ref charBuffer, ref writeIndex))
+ {
+ i = offset;
+ continue;
+ }
+ }
+ else if (IsTagName(ref openingTagArray, "</STYLE>", i))
+ {
+ ReplaceClosingStyleTag(ref openingTagArray, i, ref charBuffer, ref writeIndex);
+
+ // Strip </style> even if style is invalid.
+ i += 7;
+ continue;
+ }
+ }
+
+ if (writeIndex == charBuffer.Length) ResizeInternalArray(ref charBuffer);
+
+ charBuffer[writeIndex].unicode = c;
+ writeIndex += 1;
+ }
+
+ return true;
+ }
+
+
+ /// <summary>
+ /// Method to handle inline replacement of style tag by opening style definition.
+ /// </summary>
+ /// <param name="sourceText"></param>
+ /// <param name="srcIndex"></param>
+ /// <param name="srcOffset"></param>
+ /// <param name="charBuffer"></param>
+ /// <param name="writeIndex"></param>
+ /// <returns></returns>
+ bool ReplaceOpeningStyleTag(ref int[] sourceText, int srcIndex, out int srcOffset, ref UnicodeChar[] charBuffer, ref int writeIndex)
+ {
+ // Validate <style> tag.
+ int hashCode = GetTagHashCode(ref sourceText, srcIndex + 7, out srcOffset);
+
+ TMP_Style style = TMP_StyleSheet.GetStyle(hashCode);
+
+ // Return if we don't have a valid style.
+ if (style == null || srcOffset == 0) return false;
+
+ m_styleStack.Add(style.hashCode);
+
+ int styleLength = style.styleOpeningTagArray.Length;
+
+ // Replace <style> tag with opening definition
+ int[] openingTagArray = style.styleOpeningTagArray;
+
+ for (int i = 0; i < styleLength; i++)
+ {
+ int c = openingTagArray[i];
+
+ if (c == 60)
+ {
+ if (IsTagName(ref openingTagArray, "<BR>", i))
+ {
+ if (writeIndex == charBuffer.Length) ResizeInternalArray(ref charBuffer);
+
+ charBuffer[writeIndex].unicode = 10;
+ writeIndex += 1;
+ i += 3;
+
+ continue;
+ }
+ else if (IsTagName(ref openingTagArray, "<STYLE=", i))
+ {
+ if (ReplaceOpeningStyleTag(ref openingTagArray, i, out int offset, ref charBuffer, ref writeIndex))
+ {
+ i = offset;
+ continue;
+ }
+ }
+ else if (IsTagName(ref openingTagArray, "</STYLE>", i))
+ {
+ ReplaceClosingStyleTag(ref openingTagArray, i, ref charBuffer, ref writeIndex);
+
+ // Strip </style> even if style is invalid.
+ i += 7;
+ continue;
+ }
+ }
+
+ if (writeIndex == charBuffer.Length) ResizeInternalArray(ref charBuffer);
+
+ charBuffer[writeIndex].unicode = c;
+ writeIndex += 1;
+ }
+
+ return true;
+ }
+
+
+ /// <summary>
+ /// Method to handle inline replacement of style tag by opening style definition.
+ /// </summary>
+ /// <param name="sourceText"></param>
+ /// <param name="srcIndex"></param>
+ /// <param name="srcOffset"></param>
+ /// <param name="charBuffer"></param>
+ /// <param name="writeIndex"></param>
+ /// <returns></returns>
+ bool ReplaceOpeningStyleTag(ref char[] sourceText, int srcIndex, out int srcOffset, ref UnicodeChar[] charBuffer, ref int writeIndex)
+ {
+ // Validate <style> tag.
+ int hashCode = GetTagHashCode(ref sourceText, srcIndex + 7, out srcOffset);
+
+ TMP_Style style = TMP_StyleSheet.GetStyle(hashCode);
+
+ // Return if we don't have a valid style.
+ if (style == null || srcOffset == 0) return false;
+
+ m_styleStack.Add(style.hashCode);
+
+ int styleLength = style.styleOpeningTagArray.Length;
+
+ // Replace <style> tag with opening definition
+ int[] openingTagArray = style.styleOpeningTagArray;
+
+ for (int i = 0; i < styleLength; i++)
+ {
+ int c = openingTagArray[i];
+
+ if (c == 60)
+ {
+ if (IsTagName(ref openingTagArray, "<BR>", i))
+ {
+ if (writeIndex == charBuffer.Length) ResizeInternalArray(ref charBuffer);
+
+ charBuffer[writeIndex].unicode = 10;
+ writeIndex += 1;
+ i += 3;
+
+ continue;
+ }
+ else if (IsTagName(ref openingTagArray, "<STYLE=", i))
+ {
+ if (ReplaceOpeningStyleTag(ref openingTagArray, i, out int offset, ref charBuffer, ref writeIndex))
+ {
+ i = offset;
+ continue;
+ }
+ }
+ else if (IsTagName(ref openingTagArray, "</STYLE>", i))
+ {
+ ReplaceClosingStyleTag(ref openingTagArray, i, ref charBuffer, ref writeIndex);
+
+ // Strip </style> even if style is invalid.
+ i += 7;
+ continue;
+ }
+ }
+
+ if (writeIndex == charBuffer.Length) ResizeInternalArray(ref charBuffer);
+
+ charBuffer[writeIndex].unicode = c;
+ writeIndex += 1;
+ }
+
+ return true;
+ }
+
+
+ /// <summary>
+ /// Method to handle inline replacement of style tag by opening style definition.
+ /// </summary>
+ /// <param name="sourceText"></param>
+ /// <param name="srcIndex"></param>
+ /// <param name="srcOffset"></param>
+ /// <param name="charBuffer"></param>
+ /// <param name="writeIndex"></param>
+ /// <returns></returns>
+ bool ReplaceOpeningStyleTag(ref StringBuilder sourceText, int srcIndex, out int srcOffset, ref UnicodeChar[] charBuffer, ref int writeIndex)
+ {
+ // Validate <style> tag.
+ int hashCode = GetTagHashCode(ref sourceText, srcIndex + 7, out srcOffset);
+
+ TMP_Style style = TMP_StyleSheet.GetStyle(hashCode);
+
+ // Return if we don't have a valid style.
+ if (style == null || srcOffset == 0) return false;
+
+ m_styleStack.Add(style.hashCode);
+
+ int styleLength = style.styleOpeningTagArray.Length;
+
+ // Replace <style> tag with opening definition
+ int[] openingTagArray = style.styleOpeningTagArray;
+
+ for (int i = 0; i < styleLength; i++)
+ {
+ int c = openingTagArray[i];
+
+ if (c == 60)
+ {
+ if (IsTagName(ref openingTagArray, "<BR>", i))
+ {
+ if (writeIndex == charBuffer.Length) ResizeInternalArray(ref charBuffer);
+
+ charBuffer[writeIndex].unicode = 10;
+ writeIndex += 1;
+ i += 3;
+
+ continue;
+ }
+ else if (IsTagName(ref openingTagArray, "<STYLE=", i))
+ {
+ if (ReplaceOpeningStyleTag(ref openingTagArray, i, out int offset, ref charBuffer, ref writeIndex))
+ {
+ i = offset;
+ continue;
+ }
+ }
+ else if (IsTagName(ref openingTagArray, "</STYLE>", i))
+ {
+ ReplaceClosingStyleTag(ref openingTagArray, i, ref charBuffer, ref writeIndex);
+
+ // Strip </style> even if style is invalid.
+ i += 7;
+ continue;
+ }
+ }
+
+ if (writeIndex == charBuffer.Length) ResizeInternalArray(ref charBuffer);
+
+ charBuffer[writeIndex].unicode = c;
+ writeIndex += 1;
+ }
+
+ return true;
+ }
+
+
+ /// <summary>
+ /// Method to handle inline replacement of style tag by closing style definition.
+ /// </summary>
+ /// <param name="sourceText"></param>
+ /// <param name="srcIndex"></param>
+ /// <param name="charBuffer"></param>
+ /// <param name="writeIndex"></param>
+ /// <returns></returns>
+ bool ReplaceClosingStyleTag(ref string sourceText, int srcIndex, ref UnicodeChar[] charBuffer, ref int writeIndex)
+ {
+ // Get style from the Style Stack
+ int hashCode = m_styleStack.CurrentItem();
+ TMP_Style style = TMP_StyleSheet.GetStyle(hashCode);
+
+ m_styleStack.Remove();
+
+ // Return if we don't have a valid style.
+ if (style == null) return false;
+
+ int styleLength = style.styleClosingTagArray.Length;
+
+ // Replace <style> tag with opening definition
+ int[] closingTagArray = style.styleClosingTagArray;
+
+ for (int i = 0; i < styleLength; i++)
+ {
+ int c = closingTagArray[i];
+
+ if (c == 60)
+ {
+ if (IsTagName(ref closingTagArray, "<BR>", i))
+ {
+ if (writeIndex == charBuffer.Length) ResizeInternalArray(ref charBuffer);
+
+ charBuffer[writeIndex].unicode = 10;
+ writeIndex += 1;
+ i += 3;
+
+ continue;
+ }
+ else if (IsTagName(ref closingTagArray, "<STYLE=", i))
+ {
+ if (ReplaceOpeningStyleTag(ref closingTagArray, i, out int offset, ref charBuffer, ref writeIndex))
+ {
+ i = offset;
+ continue;
+ }
+ }
+ else if (IsTagName(ref closingTagArray, "</STYLE>", i))
+ {
+ ReplaceClosingStyleTag(ref closingTagArray, i, ref charBuffer, ref writeIndex);
+
+ // Strip </style> even if style is invalid.
+ i += 7;
+ continue;
+ }
+ }
+
+ if (writeIndex == charBuffer.Length) ResizeInternalArray(ref charBuffer);
+
+ charBuffer[writeIndex].unicode = c;
+ writeIndex += 1;
+ }
+
+ return true;
+ }
+
+
+ /// <summary>
+ /// Method to handle inline replacement of style tag by closing style definition.
+ /// </summary>
+ /// <param name="sourceText"></param>
+ /// <param name="srcIndex"></param>
+ /// <param name="charBuffer"></param>
+ /// <param name="writeIndex"></param>
+ /// <returns></returns>
+ bool ReplaceClosingStyleTag(ref int[] sourceText, int srcIndex, ref UnicodeChar[] charBuffer, ref int writeIndex)
+ {
+ // Get style from the Style Stack
+ int hashCode = m_styleStack.CurrentItem();
+ TMP_Style style = TMP_StyleSheet.GetStyle(hashCode);
+
+ m_styleStack.Remove();
+
+ // Return if we don't have a valid style.
+ if (style == null) return false;
+
+ int styleLength = style.styleClosingTagArray.Length;
+
+ // Replace <style> tag with opening definition
+ int[] closingTagArray = style.styleClosingTagArray;
+
+ for (int i = 0; i < styleLength; i++)
+ {
+ int c = closingTagArray[i];
+
+ if (c == 60)
+ {
+ if (IsTagName(ref closingTagArray, "<BR>", i))
+ {
+ if (writeIndex == charBuffer.Length) ResizeInternalArray(ref charBuffer);
+
+ charBuffer[writeIndex].unicode = 10;
+ writeIndex += 1;
+ i += 3;
+
+ continue;
+ }
+ else if (IsTagName(ref closingTagArray, "<STYLE=", i))
+ {
+ if (ReplaceOpeningStyleTag(ref closingTagArray, i, out int offset, ref charBuffer, ref writeIndex))
+ {
+ i = offset;
+ continue;
+ }
+ }
+ else if (IsTagName(ref closingTagArray, "</STYLE>", i))
+ {
+ ReplaceClosingStyleTag(ref closingTagArray, i, ref charBuffer, ref writeIndex);
+
+ // Strip </style> even if style is invalid.
+ i += 7;
+ continue;
+ }
+ }
+
+ if (writeIndex == charBuffer.Length) ResizeInternalArray(ref charBuffer);
+
+ charBuffer[writeIndex].unicode = c;
+ writeIndex += 1;
+ }
+
+ return true;
+ }
+
+
+ /// <summary>
+ /// Method to handle inline replacement of style tag by closing style definition.
+ /// </summary>
+ /// <param name="sourceText"></param>
+ /// <param name="srcIndex"></param>
+ /// <param name="charBuffer"></param>
+ /// <param name="writeIndex"></param>
+ /// <returns></returns>
+ bool ReplaceClosingStyleTag(ref char[] sourceText, int srcIndex, ref UnicodeChar[] charBuffer, ref int writeIndex)
+ {
+ // Get style from the Style Stack
+ int hashCode = m_styleStack.CurrentItem();
+ TMP_Style style = TMP_StyleSheet.GetStyle(hashCode);
+
+ m_styleStack.Remove();
+
+ // Return if we don't have a valid style.
+ if (style == null) return false;
+
+ int styleLength = style.styleClosingTagArray.Length;
+
+ // Replace <style> tag with opening definition
+ int[] closingTagArray = style.styleClosingTagArray;
+
+ for (int i = 0; i < styleLength; i++)
+ {
+ int c = closingTagArray[i];
+
+ if (c == 60)
+ {
+ if (IsTagName(ref closingTagArray, "<BR>", i))
+ {
+ if (writeIndex == charBuffer.Length) ResizeInternalArray(ref charBuffer);
+
+ charBuffer[writeIndex].unicode = 10;
+ writeIndex += 1;
+ i += 3;
+
+ continue;
+ }
+ else if (IsTagName(ref closingTagArray, "<STYLE=", i))
+ {
+ if (ReplaceOpeningStyleTag(ref closingTagArray, i, out int offset, ref charBuffer, ref writeIndex))
+ {
+ i = offset;
+ continue;
+ }
+ }
+ else if (IsTagName(ref closingTagArray, "</STYLE>", i))
+ {
+ ReplaceClosingStyleTag(ref closingTagArray, i, ref charBuffer, ref writeIndex);
+
+ // Strip </style> even if style is invalid.
+ i += 7;
+ continue;
+ }
+ }
+
+ if (writeIndex == charBuffer.Length) ResizeInternalArray(ref charBuffer);
+
+ charBuffer[writeIndex].unicode = c;
+ writeIndex += 1;
+ }
+
+ return true;
+ }
+
+ /// <summary>
+ /// Method to handle inline replacement of style tag by closing style definition.
+ /// </summary>
+ /// <param name="sourceText"></param>
+ /// <param name="srcIndex"></param>
+ /// <param name="charBuffer"></param>
+ /// <param name="writeIndex"></param>
+ /// <returns></returns>
+ bool ReplaceClosingStyleTag(ref StringBuilder sourceText, int srcIndex, ref UnicodeChar[] charBuffer, ref int writeIndex)
+ {
+ // Get style from the Style Stack
+ int hashCode = m_styleStack.CurrentItem();
+ TMP_Style style = TMP_StyleSheet.GetStyle(hashCode);
+
+ m_styleStack.Remove();
+
+ // Return if we don't have a valid style.
+ if (style == null) return false;
+
+ int styleLength = style.styleClosingTagArray.Length;
+
+ // Replace <style> tag with opening definition
+ int[] closingTagArray = style.styleClosingTagArray;
+
+ for (int i = 0; i < styleLength; i++)
+ {
+ int c = closingTagArray[i];
+
+ if (c == 60)
+ {
+ if (IsTagName(ref closingTagArray, "<BR>", i))
+ {
+ if (writeIndex == charBuffer.Length) ResizeInternalArray(ref charBuffer);
+
+ charBuffer[writeIndex].unicode = 10;
+ writeIndex += 1;
+ i += 3;
+
+ continue;
+ }
+ else if (IsTagName(ref closingTagArray, "<STYLE=", i))
+ {
+ if (ReplaceOpeningStyleTag(ref closingTagArray, i, out int offset, ref charBuffer, ref writeIndex))
+ {
+ i = offset;
+ continue;
+ }
+ }
+ else if (IsTagName(ref closingTagArray, "</STYLE>", i))
+ {
+ ReplaceClosingStyleTag(ref closingTagArray, i, ref charBuffer, ref writeIndex);
+
+ // Strip </style> even if style is invalid.
+ i += 7;
+ continue;
+ }
+ }
+
+ if (writeIndex == charBuffer.Length) ResizeInternalArray(ref charBuffer);
+
+ charBuffer[writeIndex].unicode = c;
+ writeIndex += 1;
+ }
+
+ return true;
+ }
+
+
+ /// <summary>
+ /// Method to check for a matching rich text tag.
+ /// </summary>
+ /// <param name="text"></param>
+ /// <param name="tag"></param>
+ /// <param name="index"></param>
+ /// <returns></returns>
+ bool IsTagName (ref string text, string tag, int index)
+ {
+ if (text.Length < index + tag.Length) return false;
+
+ for (int i = 0; i < tag.Length; i++)
+ {
+ if (TMP_TextUtilities.ToUpperFast(text[index + i]) != tag[i]) return false;
+ }
+
+ return true;
+ }
+
+ /// <summary>
+ /// Method to check for a matching rich text tag.
+ /// </summary>
+ /// <param name="text"></param>
+ /// <param name="tag"></param>
+ /// <param name="index"></param>
+ /// <returns></returns>
+ bool IsTagName(ref char[] text, string tag, int index)
+ {
+ if (text.Length < index + tag.Length) return false;
+
+ for (int i = 0; i < tag.Length; i++)
+ {
+ if (TMP_TextUtilities.ToUpperFast(text[index + i]) != tag[i]) return false;
+ }
+
+ return true;
+ }
+
+ /// <summary>
+ /// Method to check for a matching rich text tag.
+ /// </summary>
+ /// <param name="text"></param>
+ /// <param name="tag"></param>
+ /// <param name="index"></param>
+ /// <returns></returns>
+ bool IsTagName(ref int[] text, string tag, int index)
+ {
+ if (text.Length < index + tag.Length) return false;
+
+ for (int i = 0; i < tag.Length; i++)
+ {
+ if (TMP_TextUtilities.ToUpperFast((char)text[index + i]) != tag[i]) return false;
+ }
+
+ return true;
+ }
+
+ /// <summary>
+ /// Method to check for a matching rich text tag.
+ /// </summary>
+ /// <param name="text"></param>
+ /// <param name="tag"></param>
+ /// <param name="index"></param>
+ /// <returns></returns>
+ bool IsTagName(ref StringBuilder text, string tag, int index)
+ {
+ if (text.Length < index + tag.Length) return false;
+
+ for (int i = 0; i < tag.Length; i++)
+ {
+ if (TMP_TextUtilities.ToUpperFast(text[index + i]) != tag[i]) return false;
+ }
+
+ return true;
+ }
+
+ /// <summary>
+ /// Get Hashcode for a given tag.
+ /// </summary>
+ /// <param name="text"></param>
+ /// <param name="index"></param>
+ /// <param name="closeIndex"></param>
+ /// <returns></returns>
+ int GetTagHashCode(ref string text, int index, out int closeIndex)
+ {
+ int hashCode = 0;
+ closeIndex = 0;
+
+ for (int i = index; i < text.Length; i++)
+ {
+ // Skip quote '"' character
+ if (text[i] == 34) continue;
+
+ // Break at '>'
+ if (text[i] == 62) { closeIndex = i; break; }
+
+ hashCode = (hashCode << 5) + hashCode ^ text[i];
+ }
+
+ return hashCode;
+ }
+
+ /// <summary>
+ /// Get Hashcode for a given tag.
+ /// </summary>
+ /// <param name="text"></param>
+ /// <param name="index"></param>
+ /// <param name="closeIndex"></param>
+ /// <returns></returns>
+ int GetTagHashCode(ref char[] text, int index, out int closeIndex)
+ {
+ int hashCode = 0;
+ closeIndex = 0;
+
+ for (int i = index; i < text.Length; i++)
+ {
+ // Skip quote '"' character
+ if (text[i] == 34) continue;
+
+ // Break at '>'
+ if (text[i] == 62) { closeIndex = i; break; }
+
+ hashCode = (hashCode << 5) + hashCode ^ text[i];
+ }
+
+ return hashCode;
+ }
+
+ /// <summary>
+ /// Get Hashcode for a given tag.
+ /// </summary>
+ /// <param name="text"></param>
+ /// <param name="index"></param>
+ /// <param name="closeIndex"></param>
+ /// <returns></returns>
+ int GetTagHashCode(ref int[] text, int index, out int closeIndex)
+ {
+ int hashCode = 0;
+ closeIndex = 0;
+
+ for (int i = index; i < text.Length; i++)
+ {
+ // Skip quote '"' character
+ if (text[i] == 34) continue;
+
+ // Break at '>'
+ if (text[i] == 62) { closeIndex = i; break; }
+
+ hashCode = (hashCode << 5) + hashCode ^ text[i];
+ }
+
+ return hashCode;
+ }
+
+ /// <summary>
+ /// Get Hashcode for a given tag.
+ /// </summary>
+ /// <param name="text"></param>
+ /// <param name="index"></param>
+ /// <param name="closeIndex"></param>
+ /// <returns></returns>
+ int GetTagHashCode(ref StringBuilder text, int index, out int closeIndex)
+ {
+ int hashCode = 0;
+ closeIndex = 0;
+
+ for (int i = index; i < text.Length; i++)
+ {
+ // Skip quote '"' character
+ if (text[i] == 34) continue;
+
+ // Break at '>'
+ if (text[i] == 62) { closeIndex = i; break; }
+
+ hashCode = (hashCode << 5) + hashCode ^ text[i];
+ }
+
+ return hashCode;
+ }
+
+ /// <summary>
+ ///
+ /// </summary>
+ void ResizeInternalArray <T>(ref T[] array)
+ {
+ int size = Mathf.NextPowerOfTwo(array.Length + 1);
+
+ System.Array.Resize(ref array, size);
+ }
+
+
+ private readonly float[] k_Power = { 5e-1f, 5e-2f, 5e-3f, 5e-4f, 5e-5f, 5e-6f, 5e-7f, 5e-8f, 5e-9f, 5e-10f }; // Used by FormatText to enable rounding and avoid using Mathf.Pow.
+
+ /// <summary>
+ /// Function used in conjunction with SetText()
+ /// </summary>
+ /// <param name="number"></param>
+ /// <param name="index"></param>
+ /// <param name="precision"></param>
+ protected void AddFloatToCharArray(double number, ref int index, int precision)
+ {
+ if (number < 0)
+ {
+ m_input_CharArray[index++] = '-';
+ number = -number;
+ }
+
+ number += k_Power[Mathf.Min(9, precision)];
+
+ double integer = Math.Truncate(number);
+
+ AddIntToCharArray(integer, ref index, precision);
+
+ if (precision > 0)
+ {
+ // Add the decimal point
+ m_input_CharArray[index++] = '.';
+
+ number -= integer;
+ for (int p = 0; p < precision; p++)
+ {
+ number *= 10;
+ long d = (long)(number);
+
+ m_input_CharArray[index++] = (char)(d + 48);
+ number -= d;
+ }
+ }
+ }
+
+
+ /// <summary>
+ /// // Function used in conjunction with SetText()
+ /// </summary>
+ /// <param name="number"></param>
+ /// <param name="index"></param>
+ /// <param name="precision"></param>
+ protected void AddIntToCharArray(double number, ref int index, int precision)
+ {
+ if (number < 0)
+ {
+ m_input_CharArray[index++] = '-';
+ number = -number;
+ }
+
+ int i = index;
+ do
+ {
+ m_input_CharArray[i++] = (char)(number % 10 + 48);
+ number /= 10;
+ } while (number > 0.999d);
+
+ int lastIndex = i;
+
+ // Reverse string
+ while (index + 1 < i)
+ {
+ i -= 1;
+ char t = m_input_CharArray[index];
+ m_input_CharArray[index] = m_input_CharArray[i];
+ m_input_CharArray[i] = t;
+ index += 1;
+ }
+ index = lastIndex;
+ }
+
+
+ /// <summary>
+ /// Method used to determine the number of visible characters and required buffer allocations.
+ /// </summary>
+ /// <param name="chars"></param>
+ /// <returns></returns>
+ protected virtual int SetArraySizes(UnicodeChar[] chars) { return 0; }
+
+
+ /// <summary>
+ /// Method which parses the text input, does the layout of the text as well as generating the geometry.
+ /// </summary>
+ protected virtual void GenerateTextMesh() { }
+
+
+ /// <summary>
+ /// Function to Calculate the Preferred Width and Height of the text object.
+ /// </summary>
+ /// <returns></returns>
+ public Vector2 GetPreferredValues()
+ {
+ if (m_isInputParsingRequired || m_isTextTruncated)
+ {
+ m_isCalculatingPreferredValues = true;
+ ParseInputText();
+ }
+
+ // CALCULATE PREFERRED WIDTH
+ float preferredWidth = GetPreferredWidth();
+
+ // CALCULATE PREFERRED HEIGHT
+ float preferredHeight = GetPreferredHeight();
+
+ return new Vector2(preferredWidth, preferredHeight);
+ }
+
+
+ /// <summary>
+ /// Function to Calculate the Preferred Width and Height of the text object given the provided width and height.
+ /// </summary>
+ /// <returns></returns>
+ public Vector2 GetPreferredValues(float width, float height)
+ {
+ if (m_isInputParsingRequired || m_isTextTruncated)
+ {
+ m_isCalculatingPreferredValues = true;
+ ParseInputText();
+ }
+
+ Vector2 margin = new Vector2(width, height);
+
+ // CALCULATE PREFERRED WIDTH
+ float preferredWidth = GetPreferredWidth(margin);
+
+ // CALCULATE PREFERRED HEIGHT
+ float preferredHeight = GetPreferredHeight(margin);
+
+ return new Vector2(preferredWidth, preferredHeight);
+ }
+
+
+ /// <summary>
+ /// Function to Calculate the Preferred Width and Height of the text object given a certain string.
+ /// </summary>
+ /// <param name="text"></param>
+ /// <returns></returns>
+ public Vector2 GetPreferredValues(string text)
+ {
+ m_isCalculatingPreferredValues = true;
+
+ StringToCharArray(text, ref m_TextParsingBuffer);
+ SetArraySizes(m_TextParsingBuffer);
+
+ Vector2 margin = k_LargePositiveVector2;
+
+ // CALCULATE PREFERRED WIDTH
+ float preferredWidth = GetPreferredWidth(margin);
+
+ // CALCULATE PREFERRED HEIGHT
+ float preferredHeight = GetPreferredHeight(margin);
+
+ return new Vector2(preferredWidth, preferredHeight);
+ }
+
+
+ /// <summary>
+ /// Function to Calculate the Preferred Width and Height of the text object given a certain string and size of text container.
+ /// </summary>
+ /// <param name="text"></param>
+ /// <returns></returns>
+ public Vector2 GetPreferredValues(string text, float width, float height)
+ {
+ m_isCalculatingPreferredValues = true;
+
+ StringToCharArray(text, ref m_TextParsingBuffer);
+ SetArraySizes(m_TextParsingBuffer);
+
+ Vector2 margin = new Vector2(width, height);
+
+ // CALCULATE PREFERRED WIDTH
+ float preferredWidth = GetPreferredWidth(margin);
+
+ // CALCULATE PREFERRED HEIGHT
+ float preferredHeight = GetPreferredHeight(margin);
+
+ return new Vector2(preferredWidth, preferredHeight);
+ }
+
+
+ /// <summary>
+ /// Method to calculate the preferred width of a text object.
+ /// </summary>
+ /// <returns></returns>
+ protected float GetPreferredWidth()
+ {
+ if (TMP_Settings.instance == null) return 0;
+
+ float fontSize = m_enableAutoSizing ? m_fontSizeMax : m_fontSize;
+
+ // Reset auto sizing point size bounds
+ m_minFontSize = m_fontSizeMin;
+ m_maxFontSize = m_fontSizeMax;
+ m_charWidthAdjDelta = 0;
+
+ // Set Margins to Infinity
+ Vector2 margin = k_LargePositiveVector2;
+
+ if (m_isInputParsingRequired || m_isTextTruncated)
+ {
+ m_isCalculatingPreferredValues = true;
+ ParseInputText();
+ }
+
+ m_recursiveCount = 0;
+ float preferredWidth = CalculatePreferredValues(fontSize, margin, true).x;
+
+ m_isPreferredWidthDirty = false;
+
+ //Debug.Log("GetPreferredWidth() Called at frame " + Time.frameCount + ". Returning width of " + preferredWidth);
+
+ return preferredWidth;
+ }
+
+
+ /// <summary>
+ /// Method to calculate the preferred width of a text object.
+ /// </summary>
+ /// <param name="margin"></param>
+ /// <returns></returns>
+ protected float GetPreferredWidth(Vector2 margin)
+ {
+ float fontSize = m_enableAutoSizing ? m_fontSizeMax : m_fontSize;
+
+ // Reset auto sizing point size bounds
+ m_minFontSize = m_fontSizeMin;
+ m_maxFontSize = m_fontSizeMax;
+ m_charWidthAdjDelta = 0;
+
+ m_recursiveCount = 0;
+ float preferredWidth = CalculatePreferredValues(fontSize, margin, true).x;
+
+ //Debug.Log("GetPreferredWidth() Called. Returning width of " + preferredWidth);
+
+ return preferredWidth;
+ }
+
+
+ /// <summary>
+ /// Method to calculate the preferred height of a text object.
+ /// </summary>
+ /// <returns></returns>
+ protected float GetPreferredHeight()
+ {
+ if (TMP_Settings.instance == null) return 0;
+
+ float fontSize = m_enableAutoSizing ? m_fontSizeMax : m_fontSize;
+
+ // Reset auto sizing point size bounds
+ m_minFontSize = m_fontSizeMin;
+ m_maxFontSize = m_fontSizeMax;
+ m_charWidthAdjDelta = 0;
+
+ Vector2 margin = new Vector2(m_marginWidth != 0 ? m_marginWidth : k_LargePositiveFloat, k_LargePositiveFloat);
+
+ if (m_isInputParsingRequired || m_isTextTruncated)
+ {
+ m_isCalculatingPreferredValues = true;
+ ParseInputText();
+ }
+
+ m_recursiveCount = 0;
+ float preferredHeight = CalculatePreferredValues(fontSize, margin, !m_enableAutoSizing).y;
+
+ m_isPreferredHeightDirty = false;
+
+ //Debug.Log("GetPreferredHeight() Called. Returning height of " + preferredHeight);
+
+ return preferredHeight;
+ }
+
+
+ /// <summary>
+ /// Method to calculate the preferred height of a text object.
+ /// </summary>
+ /// <param name="margin"></param>
+ /// <returns></returns>
+ protected float GetPreferredHeight(Vector2 margin)
+ {
+ float fontSize = m_enableAutoSizing ? m_fontSizeMax : m_fontSize;
+
+ // Reset auto sizing point size bounds
+ m_minFontSize = m_fontSizeMin;
+ m_maxFontSize = m_fontSizeMax;
+ m_charWidthAdjDelta = 0;
+
+ m_recursiveCount = 0;
+ float preferredHeight = CalculatePreferredValues(fontSize, margin, true).y;
+
+ //Debug.Log("GetPreferredHeight() Called. Returning height of " + preferredHeight);
+
+ return preferredHeight;
+ }
+
+
+ /// <summary>
+ /// Method returning the rendered width and height of the text object.
+ /// </summary>
+ /// <returns></returns>
+ public Vector2 GetRenderedValues()
+ {
+ return GetTextBounds().size;
+ }
+
+ /// <summary>
+ ///
+ /// </summary>
+ /// <param name="onlyVisibleCharacters">Should returned value only factor in visible characters and exclude those greater than maxVisibleCharacters for instance.</param>
+ /// <returns></returns>
+ public Vector2 GetRenderedValues(bool onlyVisibleCharacters)
+ {
+ return GetTextBounds(onlyVisibleCharacters).size;
+ }
+
+
+ /// <summary>
+ /// Method returning the rendered width of the text object.
+ /// </summary>
+ /// <returns></returns>
+ protected float GetRenderedWidth()
+ {
+ return GetRenderedValues().x;
+ }
+
+ /// <summary>
+ /// Method returning the rendered width of the text object.
+ /// </summary>
+ /// <returns></returns>
+ protected float GetRenderedWidth(bool onlyVisibleCharacters)
+ {
+ return GetRenderedValues(onlyVisibleCharacters).x;
+ }
+
+ /// <summary>
+ /// Method returning the rendered height of the text object.
+ /// </summary>
+ /// <returns></returns>
+ protected float GetRenderedHeight()
+ {
+ return GetRenderedValues().y;
+ }
+
+ /// <summary>
+ /// Method returning the rendered height of the text object.
+ /// </summary>
+ /// <returns></returns>
+ protected float GetRenderedHeight(bool onlyVisibleCharacters)
+ {
+ return GetRenderedValues(onlyVisibleCharacters).y;
+ }
+
+
+ /// <summary>
+ /// Method to calculate the preferred width and height of the text object.
+ /// </summary>
+ /// <returns></returns>
+ protected virtual Vector2 CalculatePreferredValues(float defaultFontSize, Vector2 marginSize, bool ignoreTextAutoSizing)
+ {
+ //Debug.Log("*** CalculatePreferredValues() ***"); // ***** Frame: " + Time.frameCount);
+
+ ////Profiler.BeginSample("TMP Generate Text - Phase I");
+
+ // Early exit if no font asset was assigned. This should not be needed since LiberationSans SDF will be assigned by default.
+ if (m_fontAsset == null || m_fontAsset.characterLookupTable == null)
+ {
+ Debug.LogWarning("Can't Generate Mesh! No Font Asset has been assigned to Object ID: " + this.GetInstanceID());
+
+ return Vector2.zero;
+ }
+
+ // Early exit if we don't have any Text to generate.
+ if (m_TextParsingBuffer == null || m_TextParsingBuffer.Length == 0 || m_TextParsingBuffer[0].unicode == (char)0)
+ {
+ return Vector2.zero;
+ }
+
+ m_currentFontAsset = m_fontAsset;
+ m_currentMaterial = m_sharedMaterial;
+ m_currentMaterialIndex = 0;
+ m_materialReferenceStack.SetDefault(new MaterialReference(0, m_currentFontAsset, null, m_currentMaterial, m_padding));
+
+ // Total character count is computed when the text is parsed.
+ int totalCharacterCount = m_totalCharacterCount; // m_VisibleCharacters.Count;
+
+ if (m_internalCharacterInfo == null || totalCharacterCount > m_internalCharacterInfo.Length)
+ {
+ m_internalCharacterInfo = new TMP_CharacterInfo[totalCharacterCount > 1024 ? totalCharacterCount + 256 : Mathf.NextPowerOfTwo(totalCharacterCount)];
+ }
+
+ // Calculate the scale of the font based on selected font size and sampling point size.
+ // baseScale is calculated using the font asset assigned to the text object.
+ float baseScale = m_fontScale = (defaultFontSize / m_fontAsset.faceInfo.pointSize * m_fontAsset.faceInfo.scale * (m_isOrthographic ? 1 : 0.1f));
+ float currentElementScale = baseScale;
+ m_fontScaleMultiplier = 1;
+
+ m_currentFontSize = defaultFontSize;
+ m_sizeStack.SetDefault(m_currentFontSize);
+ float fontSizeDelta = 0;
+
+ int charCode = 0; // Holds the character code of the currently being processed character.
+
+ m_FontStyleInternal = m_fontStyle; // Set the default style.
+
+ m_lineJustification = m_textAlignment; // Sets the line justification mode to match editor alignment.
+ m_lineJustificationStack.SetDefault(m_lineJustification);
+
+ float bold_xAdvance_multiplier = 1; // Used to increase spacing between character when style is bold.
+
+ m_baselineOffset = 0; // Used by subscript characters.
+ m_baselineOffsetStack.Clear();
+
+ m_lineOffset = 0; // Amount of space between lines (font line spacing + m_linespacing).
+ m_lineHeight = TMP_Math.FLOAT_UNSET;
+ float lineGap = m_currentFontAsset.faceInfo.lineHeight - (m_currentFontAsset.faceInfo.ascentLine - m_currentFontAsset.faceInfo.descentLine);
+
+ m_cSpacing = 0; // Amount of space added between characters as a result of the use of the <cspace> tag.
+ m_monoSpacing = 0;
+ float lineOffsetDelta = 0;
+ m_xAdvance = 0; // Used to track the position of each character.
+ float maxXAdvance = 0; // Used to determine Preferred Width.
+
+ tag_LineIndent = 0; // Used for indentation of text.
+ tag_Indent = 0;
+ m_indentStack.SetDefault(0);
+ tag_NoParsing = false;
+ //m_isIgnoringAlignment = false;
+
+ m_characterCount = 0; // Total characters in the char[]
+
+
+ // Tracking of line information
+ m_firstCharacterOfLine = 0;
+ m_maxLineAscender = k_LargeNegativeFloat;
+ m_maxLineDescender = k_LargePositiveFloat;
+ m_lineNumber = 0;
+
+ float marginWidth = marginSize.x;
+ //float marginHeight = marginSize.y;
+ m_marginLeft = 0;
+ m_marginRight = 0;
+ m_width = -1;
+
+ // Used by Unity's Auto Layout system.
+ float renderedWidth = 0;
+ float renderedHeight = 0;
+ float linebreakingWidth = 0;
+ m_isCalculatingPreferredValues = true;
+
+ // Tracking of the highest Ascender
+ m_maxAscender = 0;
+ m_maxDescender = 0;
+
+
+ // Initialize struct to track states of word wrapping
+ bool isFirstWord = true;
+ bool isLastBreakingChar = false;
+ WordWrapState savedLineState = new WordWrapState();
+ SaveWordWrappingState(ref savedLineState, 0, 0);
+ WordWrapState savedWordWrapState = new WordWrapState();
+ int wrappingIndex = 0;
+
+ // Counter to prevent recursive lockup when computing preferred values.
+ m_recursiveCount += 1;
+
+ // Parse through Character buffer to read HTML tags and begin creating mesh.
+ for (int i = 0; m_TextParsingBuffer[i].unicode != 0; i++)
+ {
+ charCode = (int)m_TextParsingBuffer[i].unicode;
+
+ // Parse Rich Text Tag
+ #region Parse Rich Text Tag
+ if (m_isRichText && charCode == 60) // '<'
+ {
+ m_isParsingText = true;
+ m_textElementType = TMP_TextElementType.Character;
+
+ // Check if Tag is valid. If valid, skip to the end of the validated tag.
+ if (ValidateHtmlTag(m_TextParsingBuffer, i + 1, out int endTagIndex))
+ {
+ i = endTagIndex;
+
+ // Continue to next character or handle the sprite element
+ if (m_textElementType == TMP_TextElementType.Character)
+ continue;
+ }
+ }
+ else
+ {
+ m_textElementType = m_textInfo.characterInfo[m_characterCount].elementType;
+ m_currentMaterialIndex = m_textInfo.characterInfo[m_characterCount].materialReferenceIndex;
+ m_currentFontAsset = m_textInfo.characterInfo[m_characterCount].fontAsset;
+ }
+ #endregion End Parse Rich Text Tag
+
+ int prev_MaterialIndex = m_currentMaterialIndex;
+ bool isUsingAltTypeface = m_textInfo.characterInfo[m_characterCount].isUsingAlternateTypeface;
+
+ m_isParsingText = false;
+
+ // Handle Font Styles like LowerCase, UpperCase and SmallCaps.
+ #region Handling of LowerCase, UpperCase and SmallCaps Font Styles
+
+ float smallCapsMultiplier = 1.0f;
+
+ if (m_textElementType == TMP_TextElementType.Character)
+ {
+ if (/*(m_fontStyle & FontStyles.UpperCase) == FontStyles.UpperCase ||*/ (m_FontStyleInternal & FontStyles.UpperCase) == FontStyles.UpperCase)
+ {
+ // If this character is lowercase, switch to uppercase.
+ if (char.IsLower((char)charCode))
+ charCode = char.ToUpper((char)charCode);
+
+ }
+ else if (/*(m_fontStyle & FontStyles.LowerCase) == FontStyles.LowerCase ||*/ (m_FontStyleInternal & FontStyles.LowerCase) == FontStyles.LowerCase)
+ {
+ // If this character is uppercase, switch to lowercase.
+ if (char.IsUpper((char)charCode))
+ charCode = char.ToLower((char)charCode);
+ }
+ else if (/*(m_fontStyle & FontStyles.SmallCaps) == FontStyles.SmallCaps ||*/ (m_FontStyleInternal & FontStyles.SmallCaps) == FontStyles.SmallCaps)
+ {
+ if (char.IsLower((char)charCode))
+ {
+ smallCapsMultiplier = 0.8f;
+ charCode = char.ToUpper((char)charCode);
+ }
+ }
+ }
+ #endregion
+
+
+ // Look up Character Data from Dictionary and cache it.
+ #region Look up Character Data
+ if (m_textElementType == TMP_TextElementType.Sprite)
+ {
+ // If a sprite is used as a fallback then get a reference to it and set the color to white.
+ m_currentSpriteAsset = m_textInfo.characterInfo[m_characterCount].spriteAsset;
+ m_spriteIndex = m_textInfo.characterInfo[m_characterCount].spriteIndex;
+
+ TMP_SpriteCharacter sprite = m_currentSpriteAsset.spriteCharacterTable[m_spriteIndex];
+ if (sprite == null) continue;
+
+ // Sprites are assigned in the E000 Private Area + sprite Index
+ if (charCode == 60)
+ charCode = 57344 + m_spriteIndex;
+
+ m_currentFontAsset = m_fontAsset;
+
+ // The sprite scale calculations are based on the font asset assigned to the text object.
+ // Sprite scale is used to determine line height
+ // Current element scale represents a modified scale to normalize the sprite based on the font baseline to ascender.
+ float spriteScale = (m_currentFontSize / m_fontAsset.faceInfo.pointSize * m_fontAsset.faceInfo.scale * (m_isOrthographic ? 1 : 0.1f));
+ currentElementScale = m_fontAsset.faceInfo.ascentLine / sprite.glyph.metrics.height * sprite.scale * spriteScale;
+
+ m_cached_TextElement = sprite;
+
+ m_internalCharacterInfo[m_characterCount].elementType = TMP_TextElementType.Sprite;
+ m_internalCharacterInfo[m_characterCount].scale = spriteScale;
+ //m_internalCharacterInfo[m_characterCount].spriteAsset = m_currentSpriteAsset;
+ //m_internalCharacterInfo[m_characterCount].fontAsset = m_currentFontAsset;
+ //m_internalCharacterInfo[m_characterCount].materialReferenceIndex = m_currentMaterialIndex;
+
+ m_currentMaterialIndex = prev_MaterialIndex;
+ }
+ else if (m_textElementType == TMP_TextElementType.Character)
+ {
+ m_cached_TextElement = m_textInfo.characterInfo[m_characterCount].textElement;
+ if (m_cached_TextElement == null) continue;
+
+ //m_currentFontAsset = m_textInfo.characterInfo[m_characterCount].fontAsset;
+ //m_currentMaterial = m_textInfo.characterInfo[m_characterCount].material;
+ m_currentMaterialIndex = m_textInfo.characterInfo[m_characterCount].materialReferenceIndex;
+
+ // Re-calculate font scale as the font asset may have changed.
+ m_fontScale = m_currentFontSize * smallCapsMultiplier / m_currentFontAsset.faceInfo.pointSize * m_currentFontAsset.faceInfo.scale * (m_isOrthographic ? 1 : 0.1f);
+
+ currentElementScale = m_fontScale * m_fontScaleMultiplier * m_cached_TextElement.scale;
+
+ m_internalCharacterInfo[m_characterCount].elementType = TMP_TextElementType.Character;
+
+ }
+ #endregion
+
+
+ // Handle Soft Hyphen
+ #region Handle Soft Hyphen
+ float old_scale = currentElementScale;
+ if (charCode == 0xAD)
+ {
+ currentElementScale = 0;
+ }
+ #endregion
+
+
+ // Store some of the text object's information
+ m_internalCharacterInfo[m_characterCount].character = (char)charCode;
+
+
+ // Handle Kerning if Enabled.
+ #region Handle Kerning
+ TMP_GlyphValueRecord glyphAdjustments = new TMP_GlyphValueRecord();
+ float characterSpacingAdjustment = m_characterSpacing;
+ if (m_enableKerning)
+ {
+ if (m_characterCount < totalCharacterCount - 1)
+ {
+ uint firstGlyphIndex = m_cached_TextElement.glyphIndex;
+ uint secondGlyphIndex = m_textInfo.characterInfo[m_characterCount + 1].textElement.glyphIndex;
+ long key = new GlyphPairKey(firstGlyphIndex, secondGlyphIndex).key;
+
+ if (m_currentFontAsset.fontFeatureTable.m_GlyphPairAdjustmentRecordLookupDictionary.TryGetValue(key, out TMP_GlyphPairAdjustmentRecord adjustmentPair))
+ {
+ glyphAdjustments = adjustmentPair.firstAdjustmentRecord.glyphValueRecord;
+ characterSpacingAdjustment = (adjustmentPair.featureLookupFlags & FontFeatureLookupFlags.IgnoreSpacingAdjustments) == FontFeatureLookupFlags.IgnoreSpacingAdjustments ? 0 : characterSpacingAdjustment;
+ }
+ }
+
+ if (m_characterCount >= 1)
+ {
+ uint firstGlyphIndex = m_textInfo.characterInfo[m_characterCount - 1].textElement.glyphIndex;
+ uint secondGlyphIndex = m_cached_TextElement.glyphIndex;
+ long key = new GlyphPairKey(firstGlyphIndex, secondGlyphIndex).key;
+
+ if (m_currentFontAsset.fontFeatureTable.m_GlyphPairAdjustmentRecordLookupDictionary.TryGetValue(key, out TMP_GlyphPairAdjustmentRecord adjustmentPair))
+ {
+ glyphAdjustments += adjustmentPair.secondAdjustmentRecord.glyphValueRecord;
+ characterSpacingAdjustment = (adjustmentPair.featureLookupFlags & FontFeatureLookupFlags.IgnoreSpacingAdjustments) == FontFeatureLookupFlags.IgnoreSpacingAdjustments ? 0 : characterSpacingAdjustment;
+ }
+ }
+ }
+ #endregion
+
+
+ // Initial Implementation for RTL support.
+ #region Handle Right-to-Left
+ //if (m_isRightToLeft)
+ //{
+ // m_xAdvance -= ((m_cached_TextElement.xAdvance * bold_xAdvance_multiplier + m_characterSpacing + m_wordSpacing + m_currentFontAsset.normalSpacingOffset) * currentElementScale + m_cSpacing) * (1 - m_charWidthAdjDelta);
+
+ // if (char.IsWhiteSpace((char)charCode) || charCode == 0x200B)
+ // m_xAdvance -= m_wordSpacing * currentElementScale;
+ //}
+ #endregion
+
+
+ // Handle Mono Spacing
+ #region Handle Mono Spacing
+ float monoAdvance = 0;
+ if (m_monoSpacing != 0)
+ {
+ monoAdvance = (m_monoSpacing / 2 - (m_cached_TextElement.glyph.metrics.width / 2 + m_cached_TextElement.glyph.metrics.horizontalBearingX) * currentElementScale);
+ m_xAdvance += monoAdvance;
+ }
+ #endregion
+
+
+ // Set Padding based on selected font style
+ #region Handle Style Padding
+ if (m_textElementType == TMP_TextElementType.Character && !isUsingAltTypeface && ((m_FontStyleInternal & FontStyles.Bold) == FontStyles.Bold)) // Checks for any combination of Bold Style.
+ {
+ bold_xAdvance_multiplier = 1 + m_currentFontAsset.boldSpacing * 0.01f;
+ }
+ else
+ {
+ //style_padding = m_currentFontAsset.normalStyle * 2;
+ bold_xAdvance_multiplier = 1.0f;
+ }
+ #endregion Handle Style Padding
+
+ m_internalCharacterInfo[m_characterCount].baseLine = 0 - m_lineOffset + m_baselineOffset;
+
+
+ // Compute and save text element Ascender and maximum line Ascender.
+ float elementAscender = m_currentFontAsset.faceInfo.ascentLine * (m_textElementType == TMP_TextElementType.Character ? currentElementScale / smallCapsMultiplier : m_internalCharacterInfo[m_characterCount].scale) + m_baselineOffset;
+ m_internalCharacterInfo[m_characterCount].ascender = elementAscender - m_lineOffset;
+ m_maxLineAscender = elementAscender > m_maxLineAscender ? elementAscender : m_maxLineAscender;
+
+ // Compute and save text element Descender and maximum line Descender.
+ float elementDescender = m_currentFontAsset.faceInfo.descentLine * (m_textElementType == TMP_TextElementType.Character ? currentElementScale / smallCapsMultiplier: m_internalCharacterInfo[m_characterCount].scale) + m_baselineOffset;
+ float elementDescenderII = m_internalCharacterInfo[m_characterCount].descender = elementDescender - m_lineOffset;
+ m_maxLineDescender = elementDescender < m_maxLineDescender ? elementDescender : m_maxLineDescender;
+
+ // Adjust maxLineAscender and maxLineDescender if style is superscript or subscript
+ if ((m_FontStyleInternal & FontStyles.Subscript) == FontStyles.Subscript || (m_FontStyleInternal & FontStyles.Superscript) == FontStyles.Superscript)
+ {
+ float baseAscender = (elementAscender - m_baselineOffset) / m_currentFontAsset.faceInfo.subscriptSize;
+ elementAscender = m_maxLineAscender;
+ m_maxLineAscender = baseAscender > m_maxLineAscender ? baseAscender : m_maxLineAscender;
+
+ float baseDescender = (elementDescender - m_baselineOffset) / m_currentFontAsset.faceInfo.subscriptSize;
+ elementDescender = m_maxLineDescender;
+ m_maxLineDescender = baseDescender < m_maxLineDescender ? baseDescender : m_maxLineDescender;
+ }
+
+ if (m_lineNumber == 0) m_maxAscender = m_maxAscender > elementAscender ? m_maxAscender : elementAscender;
+ //if (m_lineOffset == 0) pageAscender = pageAscender > elementAscender ? pageAscender : elementAscender;
+
+ // Setup Mesh for visible text elements. ie. not a SPACE / LINEFEED / CARRIAGE RETURN.
+ #region Handle Visible Characters
+ if (charCode == 9 || charCode == 0xA0 || charCode == 0x2007 || (!char.IsWhiteSpace((char)charCode) && charCode != 0x200B) || m_textElementType == TMP_TextElementType.Sprite)
+ {
+ // Check if Character exceeds the width of the Text Container
+ #region Handle Line Breaking, Text Auto-Sizing and Horizontal Overflow
+ float width = m_width != -1 ? Mathf.Min(marginWidth + 0.0001f - m_marginLeft - m_marginRight, m_width) : marginWidth + 0.0001f - m_marginLeft - m_marginRight;
+
+ bool isJustifiedOrFlush = ((_HorizontalAlignmentOptions)m_lineJustification & _HorizontalAlignmentOptions.Flush) == _HorizontalAlignmentOptions.Flush || ((_HorizontalAlignmentOptions)m_lineJustification & _HorizontalAlignmentOptions.Justified) == _HorizontalAlignmentOptions.Justified;
+
+ // Calculate the line breaking width of the text.
+ linebreakingWidth = m_xAdvance + m_cached_TextElement.glyph.metrics.horizontalAdvance * (1 - m_charWidthAdjDelta) * (charCode != 0xAD ? currentElementScale : old_scale);
+
+ // Check if Character exceeds the width of the Text Container
+ if (linebreakingWidth > width * (isJustifiedOrFlush ? 1.05f : 1.0f))
+ {
+ // Word Wrapping
+ #region Handle Word Wrapping
+ if (enableWordWrapping && m_characterCount != m_firstCharacterOfLine)
+ {
+ // Check if word wrapping is still possible
+ #region Line Breaking Check
+ if (wrappingIndex == savedWordWrapState.previous_WordBreak || isFirstWord)
+ {
+ // Word wrapping is no longer possible. Shrink size of text if auto-sizing is enabled.
+ #region Text Auto-Sizing
+ if (ignoreTextAutoSizing == false && m_currentFontSize > m_fontSizeMin)
+ {
+ // Handle Character Width Adjustments
+ #region Character Width Adjustments
+ if (m_charWidthAdjDelta < m_charWidthMaxAdj / 100)
+ {
+ m_recursiveCount = 0;
+ m_charWidthAdjDelta += 0.01f;
+ return CalculatePreferredValues(defaultFontSize, marginSize, false);
+ }
+ #endregion
+
+ // Adjust Point Size
+ m_maxFontSize = defaultFontSize;
+
+ defaultFontSize -= Mathf.Max((defaultFontSize - m_minFontSize) / 2, 0.05f);
+ defaultFontSize = (int)(Mathf.Max(defaultFontSize, m_fontSizeMin) * 20 + 0.5f) / 20f;
+
+ if (m_recursiveCount > 20) return new Vector2(renderedWidth, renderedHeight);
+ return CalculatePreferredValues(defaultFontSize, marginSize, false);
+ }
+ #endregion
+
+ // Word wrapping is no longer possible, now breaking up individual words.
+ if (m_isCharacterWrappingEnabled == false)
+ {
+ m_isCharacterWrappingEnabled = true;
+ }
+ else
+ isLastBreakingChar = true;
+ }
+ #endregion
+
+ // Restore to previously stored state of last valid (space character or linefeed)
+ i = RestoreWordWrappingState(ref savedWordWrapState);
+ wrappingIndex = i; // Used to detect when line length can no longer be reduced.
+
+ // Handling for Soft Hyphen
+ if (m_TextParsingBuffer[i].unicode == 0xAD) // && !m_isCharacterWrappingEnabled) // && ellipsisIndex != i && !m_isCharacterWrappingEnabled)
+ {
+ m_isTextTruncated = true;
+ m_TextParsingBuffer[i].unicode = 0x2D;
+ return CalculatePreferredValues(defaultFontSize, marginSize, true);
+ }
+
+ // Check if Line Spacing of previous line needs to be adjusted.
+ if (m_lineNumber > 0 && !TMP_Math.Approximately(m_maxLineAscender, m_startOfLineAscender) && m_lineHeight == TMP_Math.FLOAT_UNSET)
+ {
+ //Debug.Log("(1) Adjusting Line Spacing on line #" + m_lineNumber);
+ float offsetDelta = m_maxLineAscender - m_startOfLineAscender;
+ //AdjustLineOffset(m_firstCharacterOfLine, m_characterCount, offsetDelta);
+ m_lineOffset += offsetDelta;
+ savedWordWrapState.lineOffset = m_lineOffset;
+ savedWordWrapState.previousLineAscender = m_maxLineAscender;
+
+ // TODO - Add check for character exceeding vertical bounds
+ }
+ //m_isNewPage = false;
+
+ // Calculate lineAscender & make sure if last character is superscript or subscript that we check that as well.
+ float lineAscender = m_maxLineAscender - m_lineOffset;
+ float lineDescender = m_maxLineDescender - m_lineOffset;
+
+
+ // Update maxDescender and maxVisibleDescender
+ m_maxDescender = m_maxDescender < lineDescender ? m_maxDescender : lineDescender;
+
+
+ m_firstCharacterOfLine = m_characterCount; // Store first character of the next line.
+
+ // Compute Preferred Width & Height
+ renderedWidth += m_xAdvance;
+
+ if (m_enableWordWrapping)
+ renderedHeight = m_maxAscender - m_maxDescender;
+ else
+ renderedHeight = Mathf.Max(renderedHeight, lineAscender - lineDescender);
+
+
+ // Store the state of the line before starting on the new line.
+ SaveWordWrappingState(ref savedLineState, i, m_characterCount - 1);
+
+ m_lineNumber += 1;
+ //isStartOfNewLine = true;
+
+ // Check to make sure Array is large enough to hold a new line.
+ //if (m_lineNumber >= m_internalTextInfo.lineInfo.Length)
+ // ResizeLineExtents(m_lineNumber);
+
+ // Apply Line Spacing based on scale of the last character of the line.
+ if (m_lineHeight == TMP_Math.FLOAT_UNSET)
+ {
+ float ascender = m_internalCharacterInfo[m_characterCount].ascender - m_internalCharacterInfo[m_characterCount].baseLine;
+ lineOffsetDelta = 0 - m_maxLineDescender + ascender + (lineGap + m_lineSpacing + m_lineSpacingDelta) * baseScale;
+ m_lineOffset += lineOffsetDelta;
+
+ m_startOfLineAscender = ascender;
+ }
+ else
+ m_lineOffset += m_lineHeight + m_lineSpacing * baseScale;
+
+ m_maxLineAscender = k_LargeNegativeFloat;
+ m_maxLineDescender = k_LargePositiveFloat;
+
+ m_xAdvance = 0 + tag_Indent;
+
+ continue;
+ }
+ #endregion End Word Wrapping
+
+ // Text Auto-Sizing (text exceeding Width of container.
+ #region Handle Text Auto-Sizing
+ if (ignoreTextAutoSizing == false && defaultFontSize > m_fontSizeMin)
+ {
+ // Handle Character Width Adjustments
+ #region Character Width Adjustments
+ if (m_charWidthAdjDelta < m_charWidthMaxAdj / 100)
+ {
+ m_recursiveCount = 0;
+ m_charWidthAdjDelta += 0.01f;
+ return CalculatePreferredValues(defaultFontSize, marginSize, false);
+ }
+ #endregion
+
+ // Adjust Point Size
+ m_maxFontSize = defaultFontSize;
+
+ defaultFontSize -= Mathf.Max((defaultFontSize - m_minFontSize) / 2, 0.05f);
+ defaultFontSize = (int)(Mathf.Max(defaultFontSize, m_fontSizeMin) * 20 + 0.5f) / 20f;
+
+ if (m_recursiveCount > 20) return new Vector2(renderedWidth, renderedHeight);
+ return CalculatePreferredValues(defaultFontSize, marginSize, false);
+ }
+ #endregion End Text Auto-Sizing
+ }
+ #endregion End Check for Characters Exceeding Width of Text Container
+
+ }
+ #endregion Handle Visible Characters
+
+
+ // Check if Line Spacing of previous line needs to be adjusted.
+ #region Adjust Line Spacing
+ if (m_lineNumber > 0 && !TMP_Math.Approximately(m_maxLineAscender, m_startOfLineAscender) && m_lineHeight == TMP_Math.FLOAT_UNSET && !m_isNewPage)
+ {
+ //Debug.Log("Inline - Adjusting Line Spacing on line #" + m_lineNumber);
+ //float gap = 0; // Compute gap.
+
+ float offsetDelta = m_maxLineAscender - m_startOfLineAscender;
+ //AdjustLineOffset(m_firstCharacterOfLine, m_characterCount, offsetDelta);
+ elementDescenderII -= offsetDelta;
+ m_lineOffset += offsetDelta;
+
+ m_startOfLineAscender += offsetDelta;
+ savedWordWrapState.lineOffset = m_lineOffset;
+ savedWordWrapState.previousLineAscender = m_startOfLineAscender;
+ }
+ #endregion
+
+
+ // Check if text Exceeds the vertical bounds of the margin area.
+ #region Check Vertical Bounds & Auto-Sizing
+ /*
+ if (m_maxAscender - elementDescenderII > marginHeight + 0.0001f)
+ {
+ // Handle Line spacing adjustments
+ #region Line Spacing Adjustments
+ if (m_enableAutoSizing && m_lineSpacingDelta > m_lineSpacingMax && m_lineNumber > 0)
+ {
+ //loopCountA = 0;
+
+ //m_lineSpacingDelta -= 1;
+ //GenerateTextMesh();
+ //return;
+ }
+ #endregion
+
+
+ // Handle Text Auto-sizing resulting from text exceeding vertical bounds.
+ #region Text Auto-Sizing (Text greater than vertical bounds)
+ if (m_enableAutoSizing && m_fontSize > m_fontSizeMin)
+ {
+ m_maxFontSize = m_fontSize;
+
+ m_fontSize -= Mathf.Max((m_fontSize - m_minFontSize) / 2, 0.05f);
+ m_fontSize = (int)(Mathf.Max(m_fontSize, m_fontSizeMin) * 20 + 0.5f) / 20f;
+
+ //m_recursiveCount = 0;
+ //if (loopCountA > 20) return; // Added to debug
+ CalculatePreferredValues(m_fontSize, marginSize, false);
+ return Vector2.zero;
+ }
+ #endregion Text Auto-Sizing
+ }
+ */
+ #endregion Check Vertical Bounds
+
+
+ // Handle xAdvance & Tabulation Stops. Tab stops at every 25% of Font Size.
+ #region XAdvance, Tabulation & Stops
+ if (charCode == 9)
+ {
+ float tabSize = m_currentFontAsset.faceInfo.tabWidth * currentElementScale;
+ float tabs = Mathf.Ceil(m_xAdvance / tabSize) * tabSize;
+ m_xAdvance = tabs > m_xAdvance ? tabs : m_xAdvance + tabSize;
+ }
+ else if (m_monoSpacing != 0)
+ {
+ m_xAdvance += (m_monoSpacing - monoAdvance + ((characterSpacingAdjustment + m_currentFontAsset.normalSpacingOffset) * currentElementScale) + m_cSpacing) * (1 - m_charWidthAdjDelta);
+
+ if (char.IsWhiteSpace((char)charCode) || charCode == 0x200B)
+ m_xAdvance += m_wordSpacing * currentElementScale;
+ }
+ else
+ {
+ m_xAdvance += ((m_cached_TextElement.glyph.metrics.horizontalAdvance * bold_xAdvance_multiplier + characterSpacingAdjustment + m_currentFontAsset.normalSpacingOffset + glyphAdjustments.xAdvance) * currentElementScale + m_cSpacing) * (1 - m_charWidthAdjDelta);
+
+ if (char.IsWhiteSpace((char)charCode) || charCode == 0x200B)
+ m_xAdvance += m_wordSpacing * currentElementScale;
+ }
+
+
+ #endregion Tabulation & Stops
+
+
+ // Handle Carriage Return
+ #region Carriage Return
+ if (charCode == 13)
+ {
+ maxXAdvance = Mathf.Max(maxXAdvance, renderedWidth + m_xAdvance);
+ renderedWidth = 0;
+ m_xAdvance = 0 + tag_Indent;
+ }
+ #endregion Carriage Return
+
+
+ // Handle Line Spacing Adjustments + Word Wrapping & special case for last line.
+ #region Check for Line Feed and Last Character
+ if (charCode == 10 || m_characterCount == totalCharacterCount - 1)
+ {
+ // Check if Line Spacing of previous line needs to be adjusted.
+ if (m_lineNumber > 0 && !TMP_Math.Approximately(m_maxLineAscender, m_startOfLineAscender) && m_lineHeight == TMP_Math.FLOAT_UNSET)
+ {
+ //Debug.Log("(2) Adjusting Line Spacing on line #" + m_lineNumber);
+ float offsetDelta = m_maxLineAscender - m_startOfLineAscender;
+ //AdjustLineOffset(m_firstCharacterOfLine, m_characterCount, offsetDelta);
+ elementDescenderII -= offsetDelta;
+ m_lineOffset += offsetDelta;
+ }
+
+ // Calculate lineAscender & make sure if last character is superscript or subscript that we check that as well.
+ //float lineAscender = m_maxLineAscender - m_lineOffset;
+ float lineDescender = m_maxLineDescender - m_lineOffset;
+
+ // Update maxDescender and maxVisibleDescender
+ m_maxDescender = m_maxDescender < lineDescender ? m_maxDescender : lineDescender;
+
+ m_firstCharacterOfLine = m_characterCount + 1;
+
+ // Store PreferredWidth paying attention to linefeed and last character of text.
+ if (charCode == 10 && m_characterCount != totalCharacterCount - 1)
+ {
+ maxXAdvance = Mathf.Max(maxXAdvance, renderedWidth + linebreakingWidth);
+ renderedWidth = 0;
+ }
+ else
+ renderedWidth = Mathf.Max(maxXAdvance, renderedWidth + linebreakingWidth);
+
+
+ renderedHeight = m_maxAscender - m_maxDescender;
+
+
+ // Add new line if not last lines or character.
+ if (charCode == 10)
+ {
+ // Store the state of the line before starting on the new line.
+ SaveWordWrappingState(ref savedLineState, i, m_characterCount);
+ // Store the state of the last Character before the new line.
+ SaveWordWrappingState(ref savedWordWrapState, i, m_characterCount);
+
+ m_lineNumber += 1;
+
+ // Apply Line Spacing
+ if (m_lineHeight == TMP_Math.FLOAT_UNSET)
+ {
+ lineOffsetDelta = 0 - m_maxLineDescender + elementAscender + (lineGap + m_lineSpacing + m_paragraphSpacing + m_lineSpacingDelta) * baseScale;
+ m_lineOffset += lineOffsetDelta;
+ }
+ else
+ m_lineOffset += m_lineHeight + (m_lineSpacing + m_paragraphSpacing) * baseScale;
+
+ m_maxLineAscender = k_LargeNegativeFloat;
+ m_maxLineDescender = k_LargePositiveFloat;
+ m_startOfLineAscender = elementAscender;
+
+ m_xAdvance = 0 + tag_LineIndent + tag_Indent;
+
+ m_characterCount += 1;
+ continue;
+ }
+ }
+ #endregion Check for Linefeed or Last Character
+
+
+ // Save State of Mesh Creation for handling of Word Wrapping
+ #region Save Word Wrapping State
+ if (m_enableWordWrapping || m_overflowMode == TextOverflowModes.Truncate || m_overflowMode == TextOverflowModes.Ellipsis)
+ {
+ if ((char.IsWhiteSpace((char)charCode) || charCode == 0x200B || charCode == 0x2D || charCode == 0xAD) && !m_isNonBreakingSpace && charCode != 0xA0 && charCode != 0x2011 && charCode != 0x202F && charCode != 0x2060)
+ {
+ // We store the state of numerous variables for the most recent Space, LineFeed or Carriage Return to enable them to be restored
+ // for Word Wrapping.
+ SaveWordWrappingState(ref savedWordWrapState, i, m_characterCount);
+ m_isCharacterWrappingEnabled = false;
+ isFirstWord = false;
+ }
+ // Handling for East Asian languages
+ else if ((charCode > 0x1100 && charCode < 0x11ff || /* Hangul Jamo */
+ charCode > 0x2E80 && charCode < 0x9FFF || /* CJK */
+ charCode > 0xA960 && charCode < 0xA97F || /* Hangul Jame Extended-A */
+ charCode > 0xAC00 && charCode < 0xD7FF || /* Hangul Syllables */
+ charCode > 0xF900 && charCode < 0xFAFF || /* CJK Compatibility Ideographs */
+ charCode > 0xFE30 && charCode < 0xFE4F || /* CJK Compatibility Forms */
+ charCode > 0xFF00 && charCode < 0xFFEF) /* CJK Halfwidth */
+ && !m_isNonBreakingSpace)
+ {
+ if (isFirstWord || isLastBreakingChar || TMP_Settings.linebreakingRules.leadingCharacters.ContainsKey(charCode) == false &&
+ (m_characterCount < totalCharacterCount - 1 &&
+ TMP_Settings.linebreakingRules.followingCharacters.ContainsKey(m_internalCharacterInfo[m_characterCount + 1].character) == false))
+ {
+ SaveWordWrappingState(ref savedWordWrapState, i, m_characterCount);
+ m_isCharacterWrappingEnabled = false;
+ isFirstWord = false;
+ }
+ }
+ else if ((isFirstWord || m_isCharacterWrappingEnabled == true || isLastBreakingChar))
+ SaveWordWrappingState(ref savedWordWrapState, i, m_characterCount);
+ }
+ #endregion Save Word Wrapping State
+
+ m_characterCount += 1;
+ }
+
+ // Check Auto Sizing and increase font size to fill text container.
+ #region Check Auto-Sizing (Upper Font Size Bounds)
+ fontSizeDelta = m_maxFontSize - m_minFontSize;
+ if (!m_isCharacterWrappingEnabled && ignoreTextAutoSizing == false && fontSizeDelta > 0.051f && defaultFontSize < m_fontSizeMax)
+ {
+ m_minFontSize = defaultFontSize;
+ defaultFontSize += Mathf.Max((m_maxFontSize - defaultFontSize) / 2, 0.05f);
+ defaultFontSize = (int)(Mathf.Min(defaultFontSize, m_fontSizeMax) * 20 + 0.5f) / 20f;
+
+ if (m_recursiveCount > 20) return new Vector2(renderedWidth, renderedHeight);
+ return CalculatePreferredValues(defaultFontSize, marginSize, false);
+ }
+ #endregion End Auto-sizing Check
+
+
+ m_isCharacterWrappingEnabled = false;
+ m_isCalculatingPreferredValues = false;
+
+ // Adjust Preferred Width and Height to account for Margins.
+ renderedWidth += m_margin.x > 0 ? m_margin.x : 0;
+ renderedWidth += m_margin.z > 0 ? m_margin.z : 0;
+
+ renderedHeight += m_margin.y > 0 ? m_margin.y : 0;
+ renderedHeight += m_margin.w > 0 ? m_margin.w : 0;
+
+ // Round Preferred Values to nearest 5/100.
+ renderedWidth = (int)(renderedWidth * 100 + 1f) / 100f;
+ renderedHeight = (int)(renderedHeight * 100 + 1f) / 100f;
+
+ //Debug.Log("Preferred Values: (" + renderedWidth + ", " + renderedHeight + ") with Recursive count of " + m_recursiveCount);
+
+ ////Profiler.EndSample();
+ return new Vector2(renderedWidth, renderedHeight);
+ }
+
+
+ /// <summary>
+ /// Method returning the compound bounds of the text object and child sub objects.
+ /// </summary>
+ /// <returns></returns>
+ protected virtual Bounds GetCompoundBounds() { return new Bounds(); }
+
+
+ /// <summary>
+ /// Method which returns the bounds of the text object;
+ /// </summary>
+ /// <returns></returns>
+ protected Bounds GetTextBounds()
+ {
+ if (m_textInfo == null || m_textInfo.characterCount > m_textInfo.characterInfo.Length) return new Bounds();
+
+ Extents extent = new Extents(k_LargePositiveVector2, k_LargeNegativeVector2);
+
+ for (int i = 0; i < m_textInfo.characterCount && i < m_textInfo.characterInfo.Length; i++)
+ {
+ if (!m_textInfo.characterInfo[i].isVisible) continue;
+
+ extent.min.x = Mathf.Min(extent.min.x, m_textInfo.characterInfo[i].bottomLeft.x);
+ extent.min.y = Mathf.Min(extent.min.y, m_textInfo.characterInfo[i].descender);
+
+ extent.max.x = Mathf.Max(extent.max.x, m_textInfo.characterInfo[i].xAdvance);
+ extent.max.y = Mathf.Max(extent.max.y, m_textInfo.characterInfo[i].ascender);
+ }
+
+ Vector2 size;
+ size.x = extent.max.x - extent.min.x;
+ size.y = extent.max.y - extent.min.y;
+
+ Vector3 center = (extent.min + extent.max) / 2;
+
+ return new Bounds(center, size);
+ }
+
+
+ /// <summary>
+ /// Method which returns the bounds of the text object;
+ /// </summary>
+ /// <param name="onlyVisibleCharacters"></param>
+ /// <returns></returns>
+ protected Bounds GetTextBounds(bool onlyVisibleCharacters)
+ {
+ if (m_textInfo == null) return new Bounds();
+
+ Extents extent = new Extents(k_LargePositiveVector2, k_LargeNegativeVector2);
+
+ for (int i = 0; i < m_textInfo.characterCount; i++)
+ {
+ if ((i > maxVisibleCharacters || m_textInfo.characterInfo[i].lineNumber > m_maxVisibleLines) && onlyVisibleCharacters) break;
+
+ if (onlyVisibleCharacters && !m_textInfo.characterInfo[i].isVisible) continue;
+
+ extent.min.x = Mathf.Min(extent.min.x, m_textInfo.characterInfo[i].origin);
+ extent.min.y = Mathf.Min(extent.min.y, m_textInfo.characterInfo[i].descender);
+
+ extent.max.x = Mathf.Max(extent.max.x, m_textInfo.characterInfo[i].xAdvance);
+ extent.max.y = Mathf.Max(extent.max.y, m_textInfo.characterInfo[i].ascender);
+ }
+
+ Vector2 size;
+ size.x = extent.max.x - extent.min.x;
+ size.y = extent.max.y - extent.min.y;
+
+ Vector2 center = (extent.min + extent.max) / 2;
+
+ return new Bounds(center, size);
+ }
+
+
+ /// <summary>
+ /// Method to adjust line spacing as a result of using different fonts or font point size.
+ /// </summary>
+ /// <param name="startIndex"></param>
+ /// <param name="endIndex"></param>
+ /// <param name="offset"></param>
+ protected virtual void AdjustLineOffset(int startIndex, int endIndex, float offset) { }
+
+
+ /// <summary>
+ /// Function to increase the size of the Line Extents Array.
+ /// </summary>
+ /// <param name="size"></param>
+ protected void ResizeLineExtents(int size)
+ {
+ size = size > 1024 ? size + 256 : Mathf.NextPowerOfTwo(size + 1);
+
+ TMP_LineInfo[] temp_lineInfo = new TMP_LineInfo[size];
+ for (int i = 0; i < size; i++)
+ {
+ if (i < m_textInfo.lineInfo.Length)
+ temp_lineInfo[i] = m_textInfo.lineInfo[i];
+ else
+ {
+ temp_lineInfo[i].lineExtents.min = k_LargePositiveVector2;
+ temp_lineInfo[i].lineExtents.max = k_LargeNegativeVector2;
+
+ temp_lineInfo[i].ascender = k_LargeNegativeFloat;
+ temp_lineInfo[i].descender = k_LargePositiveFloat;
+ }
+ }
+
+ m_textInfo.lineInfo = temp_lineInfo;
+ }
+ protected static Vector2 k_LargePositiveVector2 = new Vector2(TMP_Math.INT_MAX, TMP_Math.INT_MAX);
+ protected static Vector2 k_LargeNegativeVector2 = new Vector2(TMP_Math.INT_MIN, TMP_Math.INT_MIN);
+ protected static float k_LargePositiveFloat = TMP_Math.FLOAT_MAX;
+ protected static float k_LargeNegativeFloat = TMP_Math.FLOAT_MIN;
+ protected static int k_LargePositiveInt = TMP_Math.INT_MAX;
+ protected static int k_LargeNegativeInt = TMP_Math.INT_MIN;
+
+ /// <summary>
+ /// Function used to evaluate the length of a text string.
+ /// </summary>
+ /// <param name="text"></param>
+ /// <returns></returns>
+ public virtual TMP_TextInfo GetTextInfo(string text) { return null; }
+
+
+ /// <summary>
+ /// Function to force an update of the margin size.
+ /// </summary>
+ public virtual void ComputeMarginSize() { }
+
+
+ /// <summary>
+ /// Function used in conjunction with GetTextInfo to figure out Array allocations.
+ /// </summary>
+ /// <param name="chars"></param>
+ /// <returns></returns>
+ //protected int GetArraySizes(int[] chars)
+ //{
+ // //Debug.Log("Set Array Size called.");
+
+ // //int visibleCount = 0;
+ // //int totalCount = 0;
+ // int tagEnd = 0;
+
+ // m_totalCharacterCount = 0;
+ // m_isUsingBold = false;
+ // m_isParsingText = false;
+
+
+ // //m_VisibleCharacters.Clear();
+
+ // for (int i = 0; chars[i] != 0; i++)
+ // {
+ // int c = chars[i];
+
+ // if (m_isRichText && c == 60) // if Char '<'
+ // {
+ // // Check if Tag is Valid
+ // if (ValidateHtmlTag(chars, i + 1, out tagEnd))
+ // {
+ // i = tagEnd;
+ // //if ((m_style & FontStyles.Underline) == FontStyles.Underline) visibleCount += 3;
+
+ // if ((m_style & FontStyles.Bold) == FontStyles.Bold) m_isUsingBold = true;
+
+ // continue;
+ // }
+ // }
+
+ // //if (!char.IsWhiteSpace((char)c) && c != 0x200B)
+ // //{
+ // //visibleCount += 1;
+ // //}
+
+ // //m_VisibleCharacters.Add((char)c);
+ // m_totalCharacterCount += 1;
+ // }
+
+ // return m_totalCharacterCount;
+ //}
+
+
+ /// <summary>
+ /// Save the State of various variables used in the mesh creation loop in conjunction with Word Wrapping
+ /// </summary>
+ /// <param name="state"></param>
+ /// <param name="index"></param>
+ /// <param name="count"></param>
+ protected void SaveWordWrappingState(ref WordWrapState state, int index, int count)
+ {
+ // Multi Font & Material support related
+ state.currentFontAsset = m_currentFontAsset;
+ state.currentSpriteAsset = m_currentSpriteAsset;
+ state.currentMaterial = m_currentMaterial;
+ state.currentMaterialIndex = m_currentMaterialIndex;
+
+ state.previous_WordBreak = index;
+ state.total_CharacterCount = count;
+ state.visible_CharacterCount = m_lineVisibleCharacterCount;
+ //state.visible_CharacterCount = m_visibleCharacterCount;
+ //state.visible_SpriteCount = m_visibleSpriteCount;
+ state.visible_LinkCount = m_textInfo.linkCount;
+
+ state.firstCharacterIndex = m_firstCharacterOfLine;
+ state.firstVisibleCharacterIndex = m_firstVisibleCharacterOfLine;
+ state.lastVisibleCharIndex = m_lastVisibleCharacterOfLine;
+
+ state.fontStyle = m_FontStyleInternal;
+ state.fontScale = m_fontScale;
+ //state.maxFontScale = m_maxFontScale;
+ state.fontScaleMultiplier = m_fontScaleMultiplier;
+ state.currentFontSize = m_currentFontSize;
+
+ state.xAdvance = m_xAdvance;
+ state.maxCapHeight = m_maxCapHeight;
+ state.maxAscender = m_maxAscender;
+ state.maxDescender = m_maxDescender;
+ state.maxLineAscender = m_maxLineAscender;
+ state.maxLineDescender = m_maxLineDescender;
+ state.previousLineAscender = m_startOfLineAscender;
+ state.preferredWidth = m_preferredWidth;
+ state.preferredHeight = m_preferredHeight;
+ state.meshExtents = m_meshExtents;
+
+ state.lineNumber = m_lineNumber;
+ state.lineOffset = m_lineOffset;
+ state.baselineOffset = m_baselineOffset;
+
+ //state.alignment = m_lineJustification;
+ state.vertexColor = m_htmlColor;
+ state.underlineColor = m_underlineColor;
+ state.strikethroughColor = m_strikethroughColor;
+ state.highlightColor = m_highlightColor;
+
+ state.isNonBreakingSpace = m_isNonBreakingSpace;
+ state.tagNoParsing = tag_NoParsing;
+
+ // XML Tag Stack
+ state.basicStyleStack = m_fontStyleStack;
+ state.colorStack = m_colorStack;
+ state.underlineColorStack = m_underlineColorStack;
+ state.strikethroughColorStack = m_strikethroughColorStack;
+ state.highlightColorStack = m_highlightColorStack;
+ state.colorGradientStack = m_colorGradientStack;
+ state.sizeStack = m_sizeStack;
+ state.indentStack = m_indentStack;
+ state.fontWeightStack = m_FontWeightStack;
+ state.styleStack = m_styleStack;
+ state.baselineStack = m_baselineOffsetStack;
+ state.actionStack = m_actionStack;
+ state.materialReferenceStack = m_materialReferenceStack;
+ state.lineJustificationStack = m_lineJustificationStack;
+ //state.spriteAnimationStack = m_spriteAnimationStack;
+
+ state.spriteAnimationID = m_spriteAnimationID;
+
+ if (m_lineNumber < m_textInfo.lineInfo.Length)
+ state.lineInfo = m_textInfo.lineInfo[m_lineNumber];
+ }
+
+
+ /// <summary>
+ /// Restore the State of various variables used in the mesh creation loop.
+ /// </summary>
+ /// <param name="state"></param>
+ /// <returns></returns>
+ protected int RestoreWordWrappingState(ref WordWrapState state)
+ {
+ int index = state.previous_WordBreak;
+
+ // Multi Font & Material support related
+ m_currentFontAsset = state.currentFontAsset;
+ m_currentSpriteAsset = state.currentSpriteAsset;
+ m_currentMaterial = state.currentMaterial;
+ m_currentMaterialIndex = state.currentMaterialIndex;
+
+ m_characterCount = state.total_CharacterCount + 1;
+ m_lineVisibleCharacterCount = state.visible_CharacterCount;
+ //m_visibleCharacterCount = state.visible_CharacterCount;
+ //m_visibleSpriteCount = state.visible_SpriteCount;
+ m_textInfo.linkCount = state.visible_LinkCount;
+
+ m_firstCharacterOfLine = state.firstCharacterIndex;
+ m_firstVisibleCharacterOfLine = state.firstVisibleCharacterIndex;
+ m_lastVisibleCharacterOfLine = state.lastVisibleCharIndex;
+
+ m_FontStyleInternal = state.fontStyle;
+ m_fontScale = state.fontScale;
+ m_fontScaleMultiplier = state.fontScaleMultiplier;
+ //m_maxFontScale = state.maxFontScale;
+ m_currentFontSize = state.currentFontSize;
+
+ m_xAdvance = state.xAdvance;
+ m_maxCapHeight = state.maxCapHeight;
+ m_maxAscender = state.maxAscender;
+ m_maxDescender = state.maxDescender;
+ m_maxLineAscender = state.maxLineAscender;
+ m_maxLineDescender = state.maxLineDescender;
+ m_startOfLineAscender = state.previousLineAscender;
+ m_preferredWidth = state.preferredWidth;
+ m_preferredHeight = state.preferredHeight;
+ m_meshExtents = state.meshExtents;
+
+ m_lineNumber = state.lineNumber;
+ m_lineOffset = state.lineOffset;
+ m_baselineOffset = state.baselineOffset;
+
+ //m_lineJustification = state.alignment;
+ m_htmlColor = state.vertexColor;
+ m_underlineColor = state.underlineColor;
+ m_strikethroughColor = state.strikethroughColor;
+ m_highlightColor = state.highlightColor;
+
+ m_isNonBreakingSpace = state.isNonBreakingSpace;
+ tag_NoParsing = state.tagNoParsing;
+
+ // XML Tag Stack
+ m_fontStyleStack = state.basicStyleStack;
+ m_colorStack = state.colorStack;
+ m_underlineColorStack = state.underlineColorStack;
+ m_strikethroughColorStack = state.strikethroughColorStack;
+ m_highlightColorStack = state.highlightColorStack;
+ m_colorGradientStack = state.colorGradientStack;
+ m_sizeStack = state.sizeStack;
+ m_indentStack = state.indentStack;
+ m_FontWeightStack = state.fontWeightStack;
+ m_styleStack = state.styleStack;
+ m_baselineOffsetStack = state.baselineStack;
+ m_actionStack = state.actionStack;
+ m_materialReferenceStack = state.materialReferenceStack;
+ m_lineJustificationStack = state.lineJustificationStack;
+ //m_spriteAnimationStack = state.spriteAnimationStack;
+
+ m_spriteAnimationID = state.spriteAnimationID;
+
+ if (m_lineNumber < m_textInfo.lineInfo.Length)
+ m_textInfo.lineInfo[m_lineNumber] = state.lineInfo;
+
+ return index;
+ }
+
+
+ /// <summary>
+ /// Store vertex information for each character.
+ /// </summary>
+ /// <param name="style_padding">Style_padding.</param>
+ /// <param name="vertexColor">Vertex color.</param>
+ protected virtual void SaveGlyphVertexInfo(float padding, float style_padding, Color32 vertexColor)
+ {
+ // Save the Vertex Position for the Character
+ #region Setup Mesh Vertices
+ m_textInfo.characterInfo[m_characterCount].vertex_BL.position = m_textInfo.characterInfo[m_characterCount].bottomLeft;
+ m_textInfo.characterInfo[m_characterCount].vertex_TL.position = m_textInfo.characterInfo[m_characterCount].topLeft;
+ m_textInfo.characterInfo[m_characterCount].vertex_TR.position = m_textInfo.characterInfo[m_characterCount].topRight;
+ m_textInfo.characterInfo[m_characterCount].vertex_BR.position = m_textInfo.characterInfo[m_characterCount].bottomRight;
+ #endregion
+
+
+ #region Setup Vertex Colors
+ // Alpha is the lower of the vertex color or tag color alpha used.
+ vertexColor.a = m_fontColor32.a < vertexColor.a ? (byte)(m_fontColor32.a) : (byte)(vertexColor.a);
+
+ // Handle Vertex Colors & Vertex Color Gradient
+ if (!m_enableVertexGradient)
+ {
+ m_textInfo.characterInfo[m_characterCount].vertex_BL.color = vertexColor;
+ m_textInfo.characterInfo[m_characterCount].vertex_TL.color = vertexColor;
+ m_textInfo.characterInfo[m_characterCount].vertex_TR.color = vertexColor;
+ m_textInfo.characterInfo[m_characterCount].vertex_BR.color = vertexColor;
+ }
+ else
+ {
+ if (!m_overrideHtmlColors && m_colorStack.m_Index > 1)
+ {
+ m_textInfo.characterInfo[m_characterCount].vertex_BL.color = vertexColor;
+ m_textInfo.characterInfo[m_characterCount].vertex_TL.color = vertexColor;
+ m_textInfo.characterInfo[m_characterCount].vertex_TR.color = vertexColor;
+ m_textInfo.characterInfo[m_characterCount].vertex_BR.color = vertexColor;
+ }
+ else // Handle Vertex Color Gradient
+ {
+ // Use Vertex Color Gradient Preset (if one is assigned)
+ if (m_fontColorGradientPreset != null)
+ {
+ m_textInfo.characterInfo[m_characterCount].vertex_BL.color = m_fontColorGradientPreset.bottomLeft * vertexColor;
+ m_textInfo.characterInfo[m_characterCount].vertex_TL.color = m_fontColorGradientPreset.topLeft * vertexColor;
+ m_textInfo.characterInfo[m_characterCount].vertex_TR.color = m_fontColorGradientPreset.topRight * vertexColor;
+ m_textInfo.characterInfo[m_characterCount].vertex_BR.color = m_fontColorGradientPreset.bottomRight * vertexColor;
+ }
+ else
+ {
+ m_textInfo.characterInfo[m_characterCount].vertex_BL.color = m_fontColorGradient.bottomLeft * vertexColor;
+ m_textInfo.characterInfo[m_characterCount].vertex_TL.color = m_fontColorGradient.topLeft * vertexColor;
+ m_textInfo.characterInfo[m_characterCount].vertex_TR.color = m_fontColorGradient.topRight * vertexColor;
+ m_textInfo.characterInfo[m_characterCount].vertex_BR.color = m_fontColorGradient.bottomRight * vertexColor;
+ }
+ }
+ }
+
+ if (m_colorGradientPreset != null)
+ {
+ m_textInfo.characterInfo[m_characterCount].vertex_BL.color *= m_colorGradientPreset.bottomLeft;
+ m_textInfo.characterInfo[m_characterCount].vertex_TL.color *= m_colorGradientPreset.topLeft;
+ m_textInfo.characterInfo[m_characterCount].vertex_TR.color *= m_colorGradientPreset.topRight;
+ m_textInfo.characterInfo[m_characterCount].vertex_BR.color *= m_colorGradientPreset.bottomRight;
+ }
+ #endregion
+
+ // Apply style_padding only if this is a SDF Shader.
+ if (!m_isSDFShader)
+ style_padding = 0f;
+
+
+ // Setup UVs for the Character
+ #region Setup UVs
+
+ Vector2 uv0;
+ uv0.x = (m_cached_TextElement.glyph.glyphRect.x - padding - style_padding) / m_currentFontAsset.atlasWidth;
+ uv0.y = (m_cached_TextElement.glyph.glyphRect.y - padding - style_padding) / m_currentFontAsset.atlasHeight;
+
+ Vector2 uv1;
+ uv1.x = uv0.x;
+ uv1.y = (m_cached_TextElement.glyph.glyphRect.y + padding + style_padding + m_cached_TextElement.glyph.glyphRect.height) / m_currentFontAsset.atlasHeight;
+
+ Vector2 uv2;
+ uv2.x = (m_cached_TextElement.glyph.glyphRect.x + padding + style_padding + m_cached_TextElement.glyph.glyphRect.width) / m_currentFontAsset.atlasWidth;
+ uv2.y = uv1.y;
+
+ Vector2 uv3;
+ uv3.x = uv2.x;
+ uv3.y = uv0.y;
+
+ // Store UV Information
+ m_textInfo.characterInfo[m_characterCount].vertex_BL.uv = uv0;
+ m_textInfo.characterInfo[m_characterCount].vertex_TL.uv = uv1;
+ m_textInfo.characterInfo[m_characterCount].vertex_TR.uv = uv2;
+ m_textInfo.characterInfo[m_characterCount].vertex_BR.uv = uv3;
+ #endregion Setup UVs
+
+
+ // Normal
+ #region Setup Normals & Tangents
+ //Vector3 normal = new Vector3(0, 0, -1);
+ //m_textInfo.characterInfo[m_characterCount].vertex_BL.normal = normal;
+ //m_textInfo.characterInfo[m_characterCount].vertex_TL.normal = normal;
+ //m_textInfo.characterInfo[m_characterCount].vertex_TR.normal = normal;
+ //m_textInfo.characterInfo[m_characterCount].vertex_BR.normal = normal;
+
+ // Tangents
+ //Vector4 tangent = new Vector4(-1, 0, 0, 1);
+ //m_textInfo.characterInfo[m_characterCount].vertex_BL.tangent = tangent;
+ //m_textInfo.characterInfo[m_characterCount].vertex_TL.tangent = tangent;
+ //m_textInfo.characterInfo[m_characterCount].vertex_TR.tangent = tangent;
+ //m_textInfo.characterInfo[m_characterCount].vertex_BR.tangent = tangent;
+ #endregion end Normals & Tangents
+ }
+
+
+ /// <summary>
+ /// Store vertex information for each sprite.
+ /// </summary>
+ /// <param name="padding"></param>
+ /// <param name="style_padding"></param>
+ /// <param name="vertexColor"></param>
+ protected virtual void SaveSpriteVertexInfo(Color32 vertexColor)
+ {
+ // Save the Vertex Position for the Character
+ #region Setup Mesh Vertices
+ m_textInfo.characterInfo[m_characterCount].vertex_BL.position = m_textInfo.characterInfo[m_characterCount].bottomLeft;
+ m_textInfo.characterInfo[m_characterCount].vertex_TL.position = m_textInfo.characterInfo[m_characterCount].topLeft;
+ m_textInfo.characterInfo[m_characterCount].vertex_TR.position = m_textInfo.characterInfo[m_characterCount].topRight;
+ m_textInfo.characterInfo[m_characterCount].vertex_BR.position = m_textInfo.characterInfo[m_characterCount].bottomRight;
+ #endregion
+
+ // Vertex Color Alpha
+ if (m_tintAllSprites) m_tintSprite = true;
+ Color32 spriteColor = m_tintSprite ? m_spriteColor.Multiply(vertexColor) : m_spriteColor;
+ spriteColor.a = spriteColor.a < m_fontColor32.a ? spriteColor.a = spriteColor.a < vertexColor.a ? spriteColor.a : vertexColor.a : m_fontColor32.a;
+
+ Color32 c0 = spriteColor;
+ Color32 c1 = spriteColor;
+ Color32 c2 = spriteColor;
+ Color32 c3 = spriteColor;
+
+ if (m_enableVertexGradient)
+ {
+ if (m_fontColorGradientPreset != null)
+ {
+ c0 = m_tintSprite ? c0.Multiply(m_fontColorGradientPreset.bottomLeft) : c0;
+ c1 = m_tintSprite ? c1.Multiply(m_fontColorGradientPreset.topLeft) : c1;
+ c2 = m_tintSprite ? c2.Multiply(m_fontColorGradientPreset.topRight) : c2;
+ c3 = m_tintSprite ? c3.Multiply(m_fontColorGradientPreset.bottomRight) : c3;
+ }
+ else
+ {
+ c0 = m_tintSprite ? c0.Multiply(m_fontColorGradient.bottomLeft) : c0;
+ c1 = m_tintSprite ? c1.Multiply(m_fontColorGradient.topLeft) : c1;
+ c2 = m_tintSprite ? c2.Multiply(m_fontColorGradient.topRight) : c2;
+ c3 = m_tintSprite ? c3.Multiply(m_fontColorGradient.bottomRight) : c3;
+ }
+ }
+
+ if (m_colorGradientPreset != null)
+ {
+ c0 = m_tintSprite ? c0.Multiply(m_colorGradientPreset.bottomLeft) : c0;
+ c1 = m_tintSprite ? c1.Multiply(m_colorGradientPreset.topLeft) : c1;
+ c2 = m_tintSprite ? c2.Multiply(m_colorGradientPreset.topRight) : c2;
+ c3 = m_tintSprite ? c3.Multiply(m_colorGradientPreset.bottomRight) : c3;
+ }
+
+ m_textInfo.characterInfo[m_characterCount].vertex_BL.color = c0;
+ m_textInfo.characterInfo[m_characterCount].vertex_TL.color = c1;
+ m_textInfo.characterInfo[m_characterCount].vertex_TR.color = c2;
+ m_textInfo.characterInfo[m_characterCount].vertex_BR.color = c3;
+
+
+ // Setup UVs for the Character
+ #region Setup UVs
+ Vector2 uv0 = new Vector2((float)m_cached_TextElement.glyph.glyphRect.x / m_currentSpriteAsset.spriteSheet.width, (float)m_cached_TextElement.glyph.glyphRect.y / m_currentSpriteAsset.spriteSheet.height); // bottom left
+ Vector2 uv1 = new Vector2(uv0.x, (float)(m_cached_TextElement.glyph.glyphRect.y + m_cached_TextElement.glyph.glyphRect.height) / m_currentSpriteAsset.spriteSheet.height); // top left
+ Vector2 uv2 = new Vector2((float)(m_cached_TextElement.glyph.glyphRect.x + m_cached_TextElement.glyph.glyphRect.width) / m_currentSpriteAsset.spriteSheet.width, uv1.y); // top right
+ Vector2 uv3 = new Vector2(uv2.x, uv0.y); // bottom right
+
+ // Store UV Information
+ m_textInfo.characterInfo[m_characterCount].vertex_BL.uv = uv0;
+ m_textInfo.characterInfo[m_characterCount].vertex_TL.uv = uv1;
+ m_textInfo.characterInfo[m_characterCount].vertex_TR.uv = uv2;
+ m_textInfo.characterInfo[m_characterCount].vertex_BR.uv = uv3;
+ #endregion Setup UVs
+
+
+ // Normal
+ #region Setup Normals & Tangents
+ //Vector3 normal = new Vector3(0, 0, -1);
+ //m_textInfo.characterInfo[m_characterCount].vertex_BL.normal = normal;
+ //m_textInfo.characterInfo[m_characterCount].vertex_TL.normal = normal;
+ //m_textInfo.characterInfo[m_characterCount].vertex_TR.normal = normal;
+ //m_textInfo.characterInfo[m_characterCount].vertex_BR.normal = normal;
+
+ // Tangents
+ //Vector4 tangent = new Vector4(-1, 0, 0, 1);
+ //m_textInfo.characterInfo[m_characterCount].vertex_BL.tangent = tangent;
+ //m_textInfo.characterInfo[m_characterCount].vertex_TL.tangent = tangent;
+ //m_textInfo.characterInfo[m_characterCount].vertex_TR.tangent = tangent;
+ //m_textInfo.characterInfo[m_characterCount].vertex_BR.tangent = tangent;
+ #endregion end Normals & Tangents
+
+ }
+
+
+ /// <summary>
+ /// Store vertex attributes into the appropriate TMP_MeshInfo.
+ /// </summary>
+ /// <param name="i"></param>
+ /// <param name="index_X4"></param>
+ protected virtual void FillCharacterVertexBuffers(int i, int index_X4)
+ {
+ int materialIndex = m_textInfo.characterInfo[i].materialReferenceIndex;
+ index_X4 = m_textInfo.meshInfo[materialIndex].vertexCount;
+
+ // Make sure buffers allocation are sufficient to hold the vertex data
+ //if (m_textInfo.meshInfo[materialIndex].vertices.Length < index_X4 + 4)
+ // m_textInfo.meshInfo[materialIndex].ResizeMeshInfo(Mathf.NextPowerOfTwo(index_X4 + 4));
+
+
+ TMP_CharacterInfo[] characterInfoArray = m_textInfo.characterInfo;
+ m_textInfo.characterInfo[i].vertexIndex = index_X4;
+
+ // Setup Vertices for Characters
+ m_textInfo.meshInfo[materialIndex].vertices[0 + index_X4] = characterInfoArray[i].vertex_BL.position;
+ m_textInfo.meshInfo[materialIndex].vertices[1 + index_X4] = characterInfoArray[i].vertex_TL.position;
+ m_textInfo.meshInfo[materialIndex].vertices[2 + index_X4] = characterInfoArray[i].vertex_TR.position;
+ m_textInfo.meshInfo[materialIndex].vertices[3 + index_X4] = characterInfoArray[i].vertex_BR.position;
+
+
+ // Setup UVS0
+ m_textInfo.meshInfo[materialIndex].uvs0[0 + index_X4] = characterInfoArray[i].vertex_BL.uv;
+ m_textInfo.meshInfo[materialIndex].uvs0[1 + index_X4] = characterInfoArray[i].vertex_TL.uv;
+ m_textInfo.meshInfo[materialIndex].uvs0[2 + index_X4] = characterInfoArray[i].vertex_TR.uv;
+ m_textInfo.meshInfo[materialIndex].uvs0[3 + index_X4] = characterInfoArray[i].vertex_BR.uv;
+
+
+ // Setup UVS2
+ m_textInfo.meshInfo[materialIndex].uvs2[0 + index_X4] = characterInfoArray[i].vertex_BL.uv2;
+ m_textInfo.meshInfo[materialIndex].uvs2[1 + index_X4] = characterInfoArray[i].vertex_TL.uv2;
+ m_textInfo.meshInfo[materialIndex].uvs2[2 + index_X4] = characterInfoArray[i].vertex_TR.uv2;
+ m_textInfo.meshInfo[materialIndex].uvs2[3 + index_X4] = characterInfoArray[i].vertex_BR.uv2;
+
+
+ // Setup UVS4
+ //m_textInfo.meshInfo[0].uvs4[0 + index_X4] = characterInfoArray[i].vertex_BL.uv4;
+ //m_textInfo.meshInfo[0].uvs4[1 + index_X4] = characterInfoArray[i].vertex_TL.uv4;
+ //m_textInfo.meshInfo[0].uvs4[2 + index_X4] = characterInfoArray[i].vertex_TR.uv4;
+ //m_textInfo.meshInfo[0].uvs4[3 + index_X4] = characterInfoArray[i].vertex_BR.uv4;
+
+
+ // setup Vertex Colors
+ m_textInfo.meshInfo[materialIndex].colors32[0 + index_X4] = characterInfoArray[i].vertex_BL.color;
+ m_textInfo.meshInfo[materialIndex].colors32[1 + index_X4] = characterInfoArray[i].vertex_TL.color;
+ m_textInfo.meshInfo[materialIndex].colors32[2 + index_X4] = characterInfoArray[i].vertex_TR.color;
+ m_textInfo.meshInfo[materialIndex].colors32[3 + index_X4] = characterInfoArray[i].vertex_BR.color;
+
+ m_textInfo.meshInfo[materialIndex].vertexCount = index_X4 + 4;
+ }
+
+
+ protected virtual void FillCharacterVertexBuffers(int i, int index_X4, bool isVolumetric)
+ {
+ int materialIndex = m_textInfo.characterInfo[i].materialReferenceIndex;
+ index_X4 = m_textInfo.meshInfo[materialIndex].vertexCount;
+
+ TMP_CharacterInfo[] characterInfoArray = m_textInfo.characterInfo;
+ m_textInfo.characterInfo[i].vertexIndex = index_X4;
+
+ // Setup Vertices for Characters
+ m_textInfo.meshInfo[materialIndex].vertices[0 + index_X4] = characterInfoArray[i].vertex_BL.position;
+ m_textInfo.meshInfo[materialIndex].vertices[1 + index_X4] = characterInfoArray[i].vertex_TL.position;
+ m_textInfo.meshInfo[materialIndex].vertices[2 + index_X4] = characterInfoArray[i].vertex_TR.position;
+ m_textInfo.meshInfo[materialIndex].vertices[3 + index_X4] = characterInfoArray[i].vertex_BR.position;
+
+ if (isVolumetric)
+ {
+ Vector3 depth = new Vector3(0, 0, m_fontSize * m_fontScale);
+ m_textInfo.meshInfo[materialIndex].vertices[4 + index_X4] = characterInfoArray[i].vertex_BL.position + depth;
+ m_textInfo.meshInfo[materialIndex].vertices[5 + index_X4] = characterInfoArray[i].vertex_TL.position + depth;
+ m_textInfo.meshInfo[materialIndex].vertices[6 + index_X4] = characterInfoArray[i].vertex_TR.position + depth;
+ m_textInfo.meshInfo[materialIndex].vertices[7 + index_X4] = characterInfoArray[i].vertex_BR.position + depth;
+ }
+
+ // Setup UVS0
+ m_textInfo.meshInfo[materialIndex].uvs0[0 + index_X4] = characterInfoArray[i].vertex_BL.uv;
+ m_textInfo.meshInfo[materialIndex].uvs0[1 + index_X4] = characterInfoArray[i].vertex_TL.uv;
+ m_textInfo.meshInfo[materialIndex].uvs0[2 + index_X4] = characterInfoArray[i].vertex_TR.uv;
+ m_textInfo.meshInfo[materialIndex].uvs0[3 + index_X4] = characterInfoArray[i].vertex_BR.uv;
+
+ if (isVolumetric)
+ {
+ m_textInfo.meshInfo[materialIndex].uvs0[4 + index_X4] = characterInfoArray[i].vertex_BL.uv;
+ m_textInfo.meshInfo[materialIndex].uvs0[5 + index_X4] = characterInfoArray[i].vertex_TL.uv;
+ m_textInfo.meshInfo[materialIndex].uvs0[6 + index_X4] = characterInfoArray[i].vertex_TR.uv;
+ m_textInfo.meshInfo[materialIndex].uvs0[7 + index_X4] = characterInfoArray[i].vertex_BR.uv;
+ }
+
+
+ // Setup UVS2
+ m_textInfo.meshInfo[materialIndex].uvs2[0 + index_X4] = characterInfoArray[i].vertex_BL.uv2;
+ m_textInfo.meshInfo[materialIndex].uvs2[1 + index_X4] = characterInfoArray[i].vertex_TL.uv2;
+ m_textInfo.meshInfo[materialIndex].uvs2[2 + index_X4] = characterInfoArray[i].vertex_TR.uv2;
+ m_textInfo.meshInfo[materialIndex].uvs2[3 + index_X4] = characterInfoArray[i].vertex_BR.uv2;
+
+ if (isVolumetric)
+ {
+ m_textInfo.meshInfo[materialIndex].uvs2[4 + index_X4] = characterInfoArray[i].vertex_BL.uv2;
+ m_textInfo.meshInfo[materialIndex].uvs2[5 + index_X4] = characterInfoArray[i].vertex_TL.uv2;
+ m_textInfo.meshInfo[materialIndex].uvs2[6 + index_X4] = characterInfoArray[i].vertex_TR.uv2;
+ m_textInfo.meshInfo[materialIndex].uvs2[7 + index_X4] = characterInfoArray[i].vertex_BR.uv2;
+ }
+
+
+ // Setup UVS4
+ //m_textInfo.meshInfo[0].uvs4[0 + index_X4] = characterInfoArray[i].vertex_BL.uv4;
+ //m_textInfo.meshInfo[0].uvs4[1 + index_X4] = characterInfoArray[i].vertex_TL.uv4;
+ //m_textInfo.meshInfo[0].uvs4[2 + index_X4] = characterInfoArray[i].vertex_TR.uv4;
+ //m_textInfo.meshInfo[0].uvs4[3 + index_X4] = characterInfoArray[i].vertex_BR.uv4;
+
+
+ // setup Vertex Colors
+ m_textInfo.meshInfo[materialIndex].colors32[0 + index_X4] = characterInfoArray[i].vertex_BL.color;
+ m_textInfo.meshInfo[materialIndex].colors32[1 + index_X4] = characterInfoArray[i].vertex_TL.color;
+ m_textInfo.meshInfo[materialIndex].colors32[2 + index_X4] = characterInfoArray[i].vertex_TR.color;
+ m_textInfo.meshInfo[materialIndex].colors32[3 + index_X4] = characterInfoArray[i].vertex_BR.color;
+
+ if (isVolumetric)
+ {
+ Color32 backColor = new Color32(255, 255, 128, 255);
+ m_textInfo.meshInfo[materialIndex].colors32[4 + index_X4] = backColor; //characterInfoArray[i].vertex_BL.color;
+ m_textInfo.meshInfo[materialIndex].colors32[5 + index_X4] = backColor; //characterInfoArray[i].vertex_TL.color;
+ m_textInfo.meshInfo[materialIndex].colors32[6 + index_X4] = backColor; //characterInfoArray[i].vertex_TR.color;
+ m_textInfo.meshInfo[materialIndex].colors32[7 + index_X4] = backColor; //characterInfoArray[i].vertex_BR.color;
+ }
+
+ m_textInfo.meshInfo[materialIndex].vertexCount = index_X4 + (!isVolumetric ? 4 : 8);
+ }
+
+
+ /// <summary>
+ /// Fill Vertex Buffers for Sprites
+ /// </summary>
+ /// <param name="i"></param>
+ /// <param name="spriteIndex_X4"></param>
+ protected virtual void FillSpriteVertexBuffers(int i, int index_X4)
+ {
+ int materialIndex = m_textInfo.characterInfo[i].materialReferenceIndex;
+ index_X4 = m_textInfo.meshInfo[materialIndex].vertexCount;
+
+ TMP_CharacterInfo[] characterInfoArray = m_textInfo.characterInfo;
+ m_textInfo.characterInfo[i].vertexIndex = index_X4;
+
+ // Setup Vertices for Characters
+ m_textInfo.meshInfo[materialIndex].vertices[0 + index_X4] = characterInfoArray[i].vertex_BL.position;
+ m_textInfo.meshInfo[materialIndex].vertices[1 + index_X4] = characterInfoArray[i].vertex_TL.position;
+ m_textInfo.meshInfo[materialIndex].vertices[2 + index_X4] = characterInfoArray[i].vertex_TR.position;
+ m_textInfo.meshInfo[materialIndex].vertices[3 + index_X4] = characterInfoArray[i].vertex_BR.position;
+
+
+ // Setup UVS0
+ m_textInfo.meshInfo[materialIndex].uvs0[0 + index_X4] = characterInfoArray[i].vertex_BL.uv;
+ m_textInfo.meshInfo[materialIndex].uvs0[1 + index_X4] = characterInfoArray[i].vertex_TL.uv;
+ m_textInfo.meshInfo[materialIndex].uvs0[2 + index_X4] = characterInfoArray[i].vertex_TR.uv;
+ m_textInfo.meshInfo[materialIndex].uvs0[3 + index_X4] = characterInfoArray[i].vertex_BR.uv;
+
+
+ // Setup UVS2
+ m_textInfo.meshInfo[materialIndex].uvs2[0 + index_X4] = characterInfoArray[i].vertex_BL.uv2;
+ m_textInfo.meshInfo[materialIndex].uvs2[1 + index_X4] = characterInfoArray[i].vertex_TL.uv2;
+ m_textInfo.meshInfo[materialIndex].uvs2[2 + index_X4] = characterInfoArray[i].vertex_TR.uv2;
+ m_textInfo.meshInfo[materialIndex].uvs2[3 + index_X4] = characterInfoArray[i].vertex_BR.uv2;
+
+
+ // Setup UVS4
+ //m_textInfo.meshInfo[0].uvs4[0 + index_X4] = characterInfoArray[i].vertex_BL.uv4;
+ //m_textInfo.meshInfo[0].uvs4[1 + index_X4] = characterInfoArray[i].vertex_TL.uv4;
+ //m_textInfo.meshInfo[0].uvs4[2 + index_X4] = characterInfoArray[i].vertex_TR.uv4;
+ //m_textInfo.meshInfo[0].uvs4[3 + index_X4] = characterInfoArray[i].vertex_BR.uv4;
+
+
+ // setup Vertex Colors
+ m_textInfo.meshInfo[materialIndex].colors32[0 + index_X4] = characterInfoArray[i].vertex_BL.color;
+ m_textInfo.meshInfo[materialIndex].colors32[1 + index_X4] = characterInfoArray[i].vertex_TL.color;
+ m_textInfo.meshInfo[materialIndex].colors32[2 + index_X4] = characterInfoArray[i].vertex_TR.color;
+ m_textInfo.meshInfo[materialIndex].colors32[3 + index_X4] = characterInfoArray[i].vertex_BR.color;
+
+ m_textInfo.meshInfo[materialIndex].vertexCount = index_X4 + 4;
+ }
+
+
+ /// <summary>
+ /// Method to add the underline geometry.
+ /// </summary>
+ /// <param name="start"></param>
+ /// <param name="end"></param>
+ /// <param name="startScale"></param>
+ /// <param name="endScale"></param>
+ /// <param name="maxScale"></param>
+ /// <param name="underlineColor"></param>
+ protected virtual void DrawUnderlineMesh(Vector3 start, Vector3 end, ref int index, float startScale, float endScale, float maxScale, float sdfScale, Color32 underlineColor)
+ {
+ if (m_cached_Underline_Character == null)
+ {
+ if (!TMP_Settings.warningsDisabled)
+ Debug.LogWarning("Unable to add underline since the Font Asset doesn't contain the underline character.", this);
+
+ return;
+ }
+
+ int verticesCount = index + 12;
+ // Check to make sure our current mesh buffer allocations can hold these new Quads.
+ if (verticesCount > m_textInfo.meshInfo[0].vertices.Length)
+ {
+ // Resize Mesh Buffers
+ m_textInfo.meshInfo[0].ResizeMeshInfo(verticesCount / 4);
+ }
+
+ // Adjust the position of the underline based on the lowest character. This matters for subscript character.
+ start.y = Mathf.Min(start.y, end.y);
+ end.y = Mathf.Min(start.y, end.y);
+
+ float segmentWidth = m_cached_Underline_Character.glyph.metrics.width / 2 * maxScale;
+
+ if (end.x - start.x < m_cached_Underline_Character.glyph.metrics.width * maxScale)
+ {
+ segmentWidth = (end.x - start.x) / 2f;
+ }
+
+ float startPadding = m_padding * startScale / maxScale;
+ float endPadding = m_padding * endScale / maxScale;
+
+ float underlineThickness = m_fontAsset.faceInfo.underlineThickness;
+
+ // UNDERLINE VERTICES FOR (3) LINE SEGMENTS
+ #region UNDERLINE VERTICES
+ Vector3[] vertices = m_textInfo.meshInfo[0].vertices;
+
+ // Front Part of the Underline
+ vertices[index + 0] = start + new Vector3(0, 0 - (underlineThickness + m_padding) * maxScale, 0); // BL
+ vertices[index + 1] = start + new Vector3(0, m_padding * maxScale, 0); // TL
+ vertices[index + 2] = vertices[index + 1] + new Vector3(segmentWidth, 0, 0); // TR
+ vertices[index + 3] = vertices[index + 0] + new Vector3(segmentWidth, 0, 0); // BR
+
+ // Middle Part of the Underline
+ vertices[index + 4] = vertices[index + 3]; // BL
+ vertices[index + 5] = vertices[index + 2]; // TL
+ vertices[index + 6] = end + new Vector3(-segmentWidth, m_padding * maxScale, 0); // TR
+ vertices[index + 7] = end + new Vector3(-segmentWidth, -(underlineThickness + m_padding) * maxScale, 0); // BR
+
+ // End Part of the Underline
+ vertices[index + 8] = vertices[index + 7]; // BL
+ vertices[index + 9] = vertices[index + 6]; // TL
+ vertices[index + 10] = end + new Vector3(0, m_padding * maxScale, 0); // TR
+ vertices[index + 11] = end + new Vector3(0, -(underlineThickness + m_padding) * maxScale, 0); // BR
+ #endregion
+
+ // UNDERLINE UV0
+ #region HANDLE UV0
+ Vector2[] uvs0 = m_textInfo.meshInfo[0].uvs0;
+
+ // Calculate UV required to setup the 3 Quads for the Underline.
+ Vector2 uv0 = new Vector2((m_cached_Underline_Character.glyph.glyphRect.x - startPadding) / m_fontAsset.atlasWidth, (m_cached_Underline_Character.glyph.glyphRect.y - m_padding) / m_fontAsset.atlasHeight); // bottom left
+ Vector2 uv1 = new Vector2(uv0.x, (m_cached_Underline_Character.glyph.glyphRect.y + m_cached_Underline_Character.glyph.glyphRect.height + m_padding) / m_fontAsset.atlasHeight); // top left
+ Vector2 uv2 = new Vector2((m_cached_Underline_Character.glyph.glyphRect.x - startPadding + (float)m_cached_Underline_Character.glyph.glyphRect.width / 2) / m_fontAsset.atlasWidth, uv1.y); // Mid Top Left
+ Vector2 uv3 = new Vector2(uv2.x, uv0.y); // Mid Bottom Left
+ Vector2 uv4 = new Vector2((m_cached_Underline_Character.glyph.glyphRect.x + endPadding + (float)m_cached_Underline_Character.glyph.glyphRect.width / 2) / m_fontAsset.atlasWidth, uv1.y); // Mid Top Right
+ Vector2 uv5 = new Vector2(uv4.x, uv0.y); // Mid Bottom right
+ Vector2 uv6 = new Vector2((m_cached_Underline_Character.glyph.glyphRect.x + endPadding + m_cached_Underline_Character.glyph.glyphRect.width) / m_fontAsset.atlasWidth, uv1.y); // End Part - Bottom Right
+ Vector2 uv7 = new Vector2(uv6.x, uv0.y); // End Part - Top Right
+
+ // Left Part of the Underline
+ uvs0[0 + index] = uv0; // BL
+ uvs0[1 + index] = uv1; // TL
+ uvs0[2 + index] = uv2; // TR
+ uvs0[3 + index] = uv3; // BR
+
+ // Middle Part of the Underline
+ uvs0[4 + index] = new Vector2(uv2.x - uv2.x * 0.001f, uv0.y);
+ uvs0[5 + index] = new Vector2(uv2.x - uv2.x * 0.001f, uv1.y);
+ uvs0[6 + index] = new Vector2(uv2.x + uv2.x * 0.001f, uv1.y);
+ uvs0[7 + index] = new Vector2(uv2.x + uv2.x * 0.001f, uv0.y);
+
+ // Right Part of the Underline
+ uvs0[8 + index] = uv5;
+ uvs0[9 + index] = uv4;
+ uvs0[10 + index] = uv6;
+ uvs0[11 + index] = uv7;
+ #endregion
+
+ // UNDERLINE UV2
+ #region HANDLE UV2 - SDF SCALE
+ // UV1 contains Face / Border UV layout.
+ float min_UvX = 0;
+ float max_UvX = (vertices[index + 2].x - start.x) / (end.x - start.x);
+
+ //Calculate the xScale or how much the UV's are getting stretched on the X axis for the middle section of the underline.
+ float xScale = Mathf.Abs(sdfScale);
+
+ Vector2[] uvs2 = m_textInfo.meshInfo[0].uvs2;
+
+ uvs2[0 + index] = PackUV(0, 0, xScale);
+ uvs2[1 + index] = PackUV(0, 1, xScale);
+ uvs2[2 + index] = PackUV(max_UvX, 1, xScale);
+ uvs2[3 + index] = PackUV(max_UvX, 0, xScale);
+
+ min_UvX = (vertices[index + 4].x - start.x) / (end.x - start.x);
+ max_UvX = (vertices[index + 6].x - start.x) / (end.x - start.x);
+
+ uvs2[4 + index] = PackUV(min_UvX, 0, xScale);
+ uvs2[5 + index] = PackUV(min_UvX, 1, xScale);
+ uvs2[6 + index] = PackUV(max_UvX, 1, xScale);
+ uvs2[7 + index] = PackUV(max_UvX, 0, xScale);
+
+ min_UvX = (vertices[index + 8].x - start.x) / (end.x - start.x);
+ max_UvX = (vertices[index + 6].x - start.x) / (end.x - start.x);
+
+ uvs2[8 + index] = PackUV(min_UvX, 0, xScale);
+ uvs2[9 + index] = PackUV(min_UvX, 1, xScale);
+ uvs2[10 + index] = PackUV(1, 1, xScale);
+ uvs2[11 + index] = PackUV(1, 0, xScale);
+ #endregion
+
+ // UNDERLINE VERTEX COLORS
+ #region
+ // Alpha is the lower of the vertex color or tag color alpha used.
+ underlineColor.a = m_fontColor32.a < underlineColor.a ? (byte)(m_fontColor32.a) : (byte)(underlineColor.a);
+
+ Color32[] colors32 = m_textInfo.meshInfo[0].colors32;
+ colors32[0 + index] = underlineColor;
+ colors32[1 + index] = underlineColor;
+ colors32[2 + index] = underlineColor;
+ colors32[3 + index] = underlineColor;
+
+ colors32[4 + index] = underlineColor;
+ colors32[5 + index] = underlineColor;
+ colors32[6 + index] = underlineColor;
+ colors32[7 + index] = underlineColor;
+
+ colors32[8 + index] = underlineColor;
+ colors32[9 + index] = underlineColor;
+ colors32[10 + index] = underlineColor;
+ colors32[11 + index] = underlineColor;
+ #endregion
+
+ index += 12;
+ }
+
+
+ protected virtual void DrawTextHighlight(Vector3 start, Vector3 end, ref int index, Color32 highlightColor)
+ {
+ if (m_cached_Underline_Character == null)
+ {
+ if (!TMP_Settings.warningsDisabled) Debug.LogWarning("Unable to add underline since the Font Asset doesn't contain the underline character.", this);
+ return;
+ }
+
+ int verticesCount = index + 4;
+ // Check to make sure our current mesh buffer allocations can hold these new Quads.
+ if (verticesCount > m_textInfo.meshInfo[0].vertices.Length)
+ {
+ // Resize Mesh Buffers
+ m_textInfo.meshInfo[0].ResizeMeshInfo(verticesCount / 4);
+ }
+
+ // UNDERLINE VERTICES FOR (3) LINE SEGMENTS
+ #region HIGHLIGHT VERTICES
+ Vector3[] vertices = m_textInfo.meshInfo[0].vertices;
+
+ // Front Part of the Underline
+ vertices[index + 0] = start; // BL
+ vertices[index + 1] = new Vector3(start.x, end.y, 0); // TL
+ vertices[index + 2] = end; // TR
+ vertices[index + 3] = new Vector3(end.x, start.y, 0); // BR
+ #endregion
+
+ // UNDERLINE UV0
+ #region HANDLE UV0
+ Vector2[] uvs0 = m_textInfo.meshInfo[0].uvs0;
+
+ // Calculate UV required to setup the 3 Quads for the Underline.
+ Vector2 uv0 = new Vector2(((float)m_cached_Underline_Character.glyph.glyphRect.x + m_cached_Underline_Character.glyph.glyphRect.width / 2) / m_fontAsset.atlasWidth, (m_cached_Underline_Character.glyph.glyphRect.y + (float)m_cached_Underline_Character.glyph.glyphRect.height / 2) / m_fontAsset.atlasHeight); // bottom left
+ //Vector2 uv1 = new Vector2(uv0.x, uv0.y); // top left
+ //Vector2 uv2 = new Vector2(uv0.x, uv0.y); // Top Right
+ //Vector2 uv3 = new Vector2(uv2.x, uv0.y); // Bottom Right
+
+ // Left Part of the Underline
+ uvs0[0 + index] = uv0; // BL
+ uvs0[1 + index] = uv0; // TL
+ uvs0[2 + index] = uv0; // TR
+ uvs0[3 + index] = uv0; // BR
+ #endregion
+
+ // UNDERLINE UV2
+ #region HANDLE UV2 - SDF SCALE
+ // UV1 contains Face / Border UV layout.
+ //float min_UvX = 0;
+ //float max_UvX = (vertices[index + 2].x - start.x) / (end.x - start.x);
+
+ ////Calculate the xScale or how much the UV's are getting stretched on the X axis for the middle section of the underline.
+ //float xScale = 0; // Mathf.Abs(sdfScale);
+
+ Vector2[] uvs2 = m_textInfo.meshInfo[0].uvs2;
+ Vector2 customUV = new Vector2(0, 1);
+ uvs2[0 + index] = customUV; // PackUV(-0.2f, -0.2f, xScale);
+ uvs2[1 + index] = customUV; // PackUV(-0.2f, -0.1f, xScale);
+ uvs2[2 + index] = customUV; // PackUV(-0.1f, -0.1f, xScale);
+ uvs2[3 + index] = customUV; // PackUV(-0.1f, -0.2f, xScale);
+ #endregion
+
+ // HIGHLIGHT VERTEX COLORS
+ #region
+ // Alpha is the lower of the vertex color or tag color alpha used.
+ highlightColor.a = m_fontColor32.a < highlightColor.a ? m_fontColor32.a : highlightColor.a;
+
+ Color32[] colors32 = m_textInfo.meshInfo[0].colors32;
+ colors32[0 + index] = highlightColor;
+ colors32[1 + index] = highlightColor;
+ colors32[2 + index] = highlightColor;
+ colors32[3 + index] = highlightColor;
+ #endregion
+
+ index += 4;
+ }
+
+
+ /// <summary>
+ /// Internal function used to load the default settings of text objects.
+ /// </summary>
+ protected void LoadDefaultSettings()
+ {
+ if (m_text == null || m_isWaitingOnResourceLoad)
+ {
+ if (TMP_Settings.autoSizeTextContainer)
+ autoSizeTextContainer = true;
+ else
+ {
+ m_rectTransform = this.rectTransform;
+
+ if (GetType() == typeof(TextMeshPro))
+ m_rectTransform.sizeDelta = TMP_Settings.defaultTextMeshProTextContainerSize;
+ else
+ m_rectTransform.sizeDelta = TMP_Settings.defaultTextMeshProUITextContainerSize;
+ }
+
+ m_enableWordWrapping = TMP_Settings.enableWordWrapping;
+ m_enableKerning = TMP_Settings.enableKerning;
+ m_enableExtraPadding = TMP_Settings.enableExtraPadding;
+ m_tintAllSprites = TMP_Settings.enableTintAllSprites;
+ m_parseCtrlCharacters = TMP_Settings.enableParseEscapeCharacters;
+ m_fontSize = m_fontSizeBase = TMP_Settings.defaultFontSize;
+ m_fontSizeMin = m_fontSize * TMP_Settings.defaultTextAutoSizingMinRatio;
+ m_fontSizeMax = m_fontSize * TMP_Settings.defaultTextAutoSizingMaxRatio;
+ m_isWaitingOnResourceLoad = false;
+ raycastTarget = TMP_Settings.enableRaycastTarget;
+ }
+ }
+
+
+ /// <summary>
+ /// Method used to find and cache references to the Underline and Ellipsis characters.
+ /// </summary>
+ /// <param name=""></param>
+ protected void GetSpecialCharacters(TMP_FontAsset fontAsset)
+ {
+ // Check & Assign Underline Character for use with the Underline tag.
+ if (!fontAsset.characterLookupTable.TryGetValue(95, out m_cached_Underline_Character))
+ {
+ m_cached_Underline_Character = TMP_FontAssetUtilities.GetCharacterFromFontAsset(95,fontAsset, false, m_FontStyleInternal, (FontWeight)m_FontWeightInternal, out bool isUsingAlternativeTypeface, out TMP_FontAsset tempFontAsset);
+
+ if (m_cached_Underline_Character == null)
+ {
+ if (!TMP_Settings.warningsDisabled)
+ Debug.LogWarning("The character used for Underline and Strikethrough is not available in font asset [" + fontAsset.name + "].", this);
+ }
+ }
+
+ // Check & Assign Underline Character for use with the Underline tag.
+ if (!fontAsset.characterLookupTable.TryGetValue(8230, out m_cached_Ellipsis_Character)) //95
+ {
+ m_cached_Ellipsis_Character = TMP_FontAssetUtilities.GetCharacterFromFontAsset(8230, fontAsset, false, m_FontStyleInternal, (FontWeight)m_FontWeightInternal, out bool isUsingAlternativeTypeface, out TMP_FontAsset tempFontAsset);
+
+ if (m_cached_Ellipsis_Character == null)
+ {
+ if (!TMP_Settings.warningsDisabled)
+ Debug.LogWarning("The character used for Ellipsis is not available in font asset [" + fontAsset.name + "].", this);
+ }
+ }
+ }
+
+
+ /// <summary>
+ /// Replace a given number of characters (tag) in the array with a new character and shift subsequent characters in the array.
+ /// </summary>
+ /// <param name="chars">Array which contains the text.</param>
+ /// <param name="insertionIndex">The index of where the new character will be inserted</param>
+ /// <param name="tagLength">Length of the tag being replaced.</param>
+ /// <param name="c">The replacement character.</param>
+ protected void ReplaceTagWithCharacter(int[] chars, int insertionIndex, int tagLength, char c)
+ {
+ chars[insertionIndex] = c;
+
+ for (int i = insertionIndex + tagLength; i < chars.Length; i++)
+ {
+ chars[i - 3] = chars[i];
+ }
+ }
+
+
+ /// <summary>
+ ///
+ /// </summary>
+ /// <returns></returns>
+ //protected int GetMaterialReferenceForFontWeight()
+ //{
+ // //bool isItalic = (m_style & FontStyles.Italic) == FontStyles.Italic || (m_fontStyle & FontStyles.Italic) == FontStyles.Italic;
+
+ // m_currentMaterialIndex = MaterialReference.AddMaterialReference(m_currentFontAsset.fontWeights[0].italicTypeface.material, m_currentFontAsset.fontWeights[0].italicTypeface, m_materialReferences, m_materialReferenceIndexLookup);
+
+ // return 0;
+ //}
+
+
+ /// <summary>
+ ///
+ /// </summary>
+ /// <returns></returns>
+ protected TMP_FontAsset GetFontAssetForWeight(int fontWeight)
+ {
+ bool isItalic = (m_FontStyleInternal & FontStyles.Italic) == FontStyles.Italic || (m_fontStyle & FontStyles.Italic) == FontStyles.Italic;
+
+ TMP_FontAsset fontAsset = null;
+
+ int weightIndex = fontWeight / 100;
+
+ if (isItalic)
+ fontAsset = m_currentFontAsset.fontWeightTable[weightIndex].italicTypeface;
+ else
+ fontAsset = m_currentFontAsset.fontWeightTable[weightIndex].regularTypeface;
+
+ return fontAsset;
+ }
+
+
+ /// <summary>
+ /// Method to Enable or Disable child SubMesh objects.
+ /// </summary>
+ /// <param name="state"></param>
+ protected virtual void SetActiveSubMeshes(bool state) { }
+
+
+ /// <summary>
+ /// Destroy Sub Mesh Objects.
+ /// </summary>
+ protected virtual void ClearSubMeshObjects() { }
+
+
+ /// <summary>
+ /// Function to clear the geometry of the Primary and Sub Text objects.
+ /// </summary>
+ public virtual void ClearMesh() { }
+
+
+ /// <summary>
+ /// Function to clear the geometry of the Primary and Sub Text objects.
+ /// </summary>
+ public virtual void ClearMesh(bool uploadGeometry) { }
+
+
+ /// <summary>
+ /// Function which returns the text after it has been parsed and rich text tags removed.
+ /// </summary>
+ /// <returns></returns>
+ public virtual string GetParsedText()
+ {
+ if (m_textInfo == null)
+ return string.Empty;
+
+ int characterCount = m_textInfo.characterCount;
+
+ // TODO - Could implement some static buffer pool shared by all instances of TMP objects.
+ char[] buffer = new char[characterCount];
+
+ for (int i = 0; i < characterCount && i < m_textInfo.characterInfo.Length; i++)
+ {
+ buffer[i] = m_textInfo.characterInfo[i].character;
+ }
+
+ return new string(buffer);
+ }
+
+
+ /// <summary>
+ /// Function to pack scale information in the UV2 Channel.
+ /// </summary>
+ /// <param name="x"></param>
+ /// <param name="y"></param>
+ /// <param name="scale"></param>
+ /// <returns></returns>
+ //protected Vector2 PackUV(float x, float y, float scale)
+ //{
+ // Vector2 output;
+
+ // output.x = Mathf.Floor(x * 4095);
+ // output.y = Mathf.Floor(y * 4095);
+
+ // output.x = (output.x * 4096) + output.y;
+ // output.y = scale;
+
+ // return output;
+ //}
+
+ /// <summary>
+ /// Function to pack scale information in the UV2 Channel.
+ /// </summary>
+ /// <param name="x"></param>
+ /// <param name="y"></param>
+ /// <param name="scale"></param>
+ /// <returns></returns>
+ protected Vector2 PackUV(float x, float y, float scale)
+ {
+ Vector2 output;
+
+ output.x = (int)(x * 511);
+ output.y = (int)(y * 511);
+
+ output.x = (output.x * 4096) + output.y;
+ output.y = scale;
+
+ return output;
+ }
+
+
+ /// <summary>
+ ///
+ /// </summary>
+ /// <param name="x"></param>
+ /// <param name="y"></param>
+ /// <returns></returns>
+ protected float PackUV(float x, float y)
+ {
+ double x0 = (int)(x * 511);
+ double y0 = (int)(y * 511);
+
+ return (float)((x0 * 4096) + y0);
+ }
+
+
+ /// <summary>
+ /// Function used as a replacement for LateUpdate()
+ /// </summary>
+ internal virtual void InternalUpdate() { }
+
+
+ /// <summary>
+ /// Function to pack scale information in the UV2 Channel.
+ /// </summary>
+ /// <param name="x"></param>
+ /// <param name="y"></param>
+ /// <param name="scale"></param>
+ /// <returns></returns>
+ //protected Vector2 PackUV(float x, float y, float scale)
+ //{
+ // Vector2 output;
+
+ // output.x = Mathf.Floor(x * 4095);
+ // output.y = Mathf.Floor(y * 4095);
+
+ // return new Vector2((output.x * 4096) + output.y, scale);
+ //}
+
+
+ /// <summary>
+ ///
+ /// </summary>
+ /// <param name="x"></param>
+ /// <param name="y"></param>
+ /// <returns></returns>
+ //protected float PackUV(float x, float y)
+ //{
+ // x = (x % 5) / 5;
+ // y = (y % 5) / 5;
+
+ // return Mathf.Round(x * 4096) + y;
+ //}
+
+
+ /// <summary>
+ /// Method to convert Hex to Int
+ /// </summary>
+ /// <param name="hex"></param>
+ /// <returns></returns>
+ protected int HexToInt(char hex)
+ {
+ switch (hex)
+ {
+ case '0': return 0;
+ case '1': return 1;
+ case '2': return 2;
+ case '3': return 3;
+ case '4': return 4;
+ case '5': return 5;
+ case '6': return 6;
+ case '7': return 7;
+ case '8': return 8;
+ case '9': return 9;
+ case 'A': return 10;
+ case 'B': return 11;
+ case 'C': return 12;
+ case 'D': return 13;
+ case 'E': return 14;
+ case 'F': return 15;
+ case 'a': return 10;
+ case 'b': return 11;
+ case 'c': return 12;
+ case 'd': return 13;
+ case 'e': return 14;
+ case 'f': return 15;
+ }
+ return 15;
+ }
+
+
+ /// <summary>
+ /// Convert UTF-16 Hex to Char
+ /// </summary>
+ /// <returns>The Unicode hex.</returns>
+ /// <param name="i">The index.</param>
+ protected int GetUTF16(string text, int i)
+ {
+ int unicode = 0;
+ unicode += HexToInt(text[i]) << 12;
+ unicode += HexToInt(text[i + 1]) << 8;
+ unicode += HexToInt(text[i + 2]) << 4;
+ unicode += HexToInt(text[i + 3]);
+ return unicode;
+ }
+
+ /// <summary>
+ /// Convert UTF-16 Hex to Char
+ /// </summary>
+ /// <returns>The Unicode hex.</returns>
+ /// <param name="i">The index.</param>
+ protected int GetUTF16(StringBuilder text, int i)
+ {
+ int unicode = 0;
+ unicode += HexToInt(text[i]) << 12;
+ unicode += HexToInt(text[i + 1]) << 8;
+ unicode += HexToInt(text[i + 2]) << 4;
+ unicode += HexToInt(text[i + 3]);
+ return unicode;
+ }
+
+
+ /// <summary>
+ /// Convert UTF-32 Hex to Char
+ /// </summary>
+ /// <returns>The Unicode hex.</returns>
+ /// <param name="i">The index.</param>
+ protected int GetUTF32(string text, int i)
+ {
+ int unicode = 0;
+ unicode += HexToInt(text[i]) << 30;
+ unicode += HexToInt(text[i + 1]) << 24;
+ unicode += HexToInt(text[i + 2]) << 20;
+ unicode += HexToInt(text[i + 3]) << 16;
+ unicode += HexToInt(text[i + 4]) << 12;
+ unicode += HexToInt(text[i + 5]) << 8;
+ unicode += HexToInt(text[i + 6]) << 4;
+ unicode += HexToInt(text[i + 7]);
+ return unicode;
+ }
+
+ /// <summary>
+ /// Convert UTF-32 Hex to Char
+ /// </summary>
+ /// <returns>The Unicode hex.</returns>
+ /// <param name="i">The index.</param>
+ protected int GetUTF32(StringBuilder text, int i)
+ {
+ int unicode = 0;
+ unicode += HexToInt(text[i]) << 30;
+ unicode += HexToInt(text[i + 1]) << 24;
+ unicode += HexToInt(text[i + 2]) << 20;
+ unicode += HexToInt(text[i + 3]) << 16;
+ unicode += HexToInt(text[i + 4]) << 12;
+ unicode += HexToInt(text[i + 5]) << 8;
+ unicode += HexToInt(text[i + 6]) << 4;
+ unicode += HexToInt(text[i + 7]);
+ return unicode;
+ }
+
+
+ /// <summary>
+ /// Method to convert Hex color values to Color32
+ /// </summary>
+ /// <param name="hexChars"></param>
+ /// <param name="tagCount"></param>
+ /// <returns></returns>
+ protected Color32 HexCharsToColor(char[] hexChars, int tagCount)
+ {
+ if (tagCount == 4)
+ {
+ byte r = (byte)(HexToInt(hexChars[1]) * 16 + HexToInt(hexChars[1]));
+ byte g = (byte)(HexToInt(hexChars[2]) * 16 + HexToInt(hexChars[2]));
+ byte b = (byte)(HexToInt(hexChars[3]) * 16 + HexToInt(hexChars[3]));
+
+ return new Color32(r, g, b, 255);
+ }
+ else if (tagCount == 5)
+ {
+ byte r = (byte)(HexToInt(hexChars[1]) * 16 + HexToInt(hexChars[1]));
+ byte g = (byte)(HexToInt(hexChars[2]) * 16 + HexToInt(hexChars[2]));
+ byte b = (byte)(HexToInt(hexChars[3]) * 16 + HexToInt(hexChars[3]));
+ byte a = (byte)(HexToInt(hexChars[4]) * 16 + HexToInt(hexChars[4]));
+
+ return new Color32(r, g, b, a);
+ }
+ else if (tagCount == 7)
+ {
+ byte r = (byte)(HexToInt(hexChars[1]) * 16 + HexToInt(hexChars[2]));
+ byte g = (byte)(HexToInt(hexChars[3]) * 16 + HexToInt(hexChars[4]));
+ byte b = (byte)(HexToInt(hexChars[5]) * 16 + HexToInt(hexChars[6]));
+
+ return new Color32(r, g, b, 255);
+ }
+ else if (tagCount == 9)
+ {
+ byte r = (byte)(HexToInt(hexChars[1]) * 16 + HexToInt(hexChars[2]));
+ byte g = (byte)(HexToInt(hexChars[3]) * 16 + HexToInt(hexChars[4]));
+ byte b = (byte)(HexToInt(hexChars[5]) * 16 + HexToInt(hexChars[6]));
+ byte a = (byte)(HexToInt(hexChars[7]) * 16 + HexToInt(hexChars[8]));
+
+ return new Color32(r, g, b, a);
+ }
+ else if (tagCount == 10)
+ {
+ byte r = (byte)(HexToInt(hexChars[7]) * 16 + HexToInt(hexChars[7]));
+ byte g = (byte)(HexToInt(hexChars[8]) * 16 + HexToInt(hexChars[8]));
+ byte b = (byte)(HexToInt(hexChars[9]) * 16 + HexToInt(hexChars[9]));
+
+ return new Color32(r, g, b, 255);
+ }
+ else if (tagCount == 11)
+ {
+ byte r = (byte)(HexToInt(hexChars[7]) * 16 + HexToInt(hexChars[7]));
+ byte g = (byte)(HexToInt(hexChars[8]) * 16 + HexToInt(hexChars[8]));
+ byte b = (byte)(HexToInt(hexChars[9]) * 16 + HexToInt(hexChars[9]));
+ byte a = (byte)(HexToInt(hexChars[10]) * 16 + HexToInt(hexChars[10]));
+
+ return new Color32(r, g, b, a);
+ }
+ else if (tagCount == 13)
+ {
+ byte r = (byte)(HexToInt(hexChars[7]) * 16 + HexToInt(hexChars[8]));
+ byte g = (byte)(HexToInt(hexChars[9]) * 16 + HexToInt(hexChars[10]));
+ byte b = (byte)(HexToInt(hexChars[11]) * 16 + HexToInt(hexChars[12]));
+
+ return new Color32(r, g, b, 255);
+ }
+ else if (tagCount == 15)
+ {
+ byte r = (byte)(HexToInt(hexChars[7]) * 16 + HexToInt(hexChars[8]));
+ byte g = (byte)(HexToInt(hexChars[9]) * 16 + HexToInt(hexChars[10]));
+ byte b = (byte)(HexToInt(hexChars[11]) * 16 + HexToInt(hexChars[12]));
+ byte a = (byte)(HexToInt(hexChars[13]) * 16 + HexToInt(hexChars[14]));
+
+ return new Color32(r, g, b, a);
+ }
+
+ return new Color32(255, 255, 255, 255);
+ }
+
+
+ /// <summary>
+ /// Method to convert Hex Color values to Color32
+ /// </summary>
+ /// <param name="hexChars"></param>
+ /// <param name="startIndex"></param>
+ /// <param name="length"></param>
+ /// <returns></returns>
+ protected Color32 HexCharsToColor(char[] hexChars, int startIndex, int length)
+ {
+ if (length == 7)
+ {
+ byte r = (byte)(HexToInt(hexChars[startIndex + 1]) * 16 + HexToInt(hexChars[startIndex + 2]));
+ byte g = (byte)(HexToInt(hexChars[startIndex + 3]) * 16 + HexToInt(hexChars[startIndex + 4]));
+ byte b = (byte)(HexToInt(hexChars[startIndex + 5]) * 16 + HexToInt(hexChars[startIndex + 6]));
+
+ return new Color32(r, g, b, 255);
+ }
+ else if (length == 9)
+ {
+ byte r = (byte)(HexToInt(hexChars[startIndex + 1]) * 16 + HexToInt(hexChars[startIndex + 2]));
+ byte g = (byte)(HexToInt(hexChars[startIndex + 3]) * 16 + HexToInt(hexChars[startIndex + 4]));
+ byte b = (byte)(HexToInt(hexChars[startIndex + 5]) * 16 + HexToInt(hexChars[startIndex + 6]));
+ byte a = (byte)(HexToInt(hexChars[startIndex + 7]) * 16 + HexToInt(hexChars[startIndex + 8]));
+
+ return new Color32(r, g, b, a);
+ }
+
+ return s_colorWhite;
+ }
+
+
+ /// <summary>
+ /// Method which returns the number of parameters used in a tag attribute and populates an array with such values.
+ /// </summary>
+ /// <param name="chars">Char[] containing the tag attribute and data</param>
+ /// <param name="startIndex">The index of the first char of the data</param>
+ /// <param name="length">The length of the data</param>
+ /// <param name="parameters">The number of parameters contained in the Char[]</param>
+ /// <returns></returns>
+ int GetAttributeParameters(char[] chars, int startIndex, int length, ref float[] parameters)
+ {
+ int endIndex = startIndex;
+ int attributeCount = 0;
+
+ while (endIndex < startIndex + length)
+ {
+ parameters[attributeCount] = ConvertToFloat(chars, startIndex, length, out endIndex);
+
+ length -= (endIndex - startIndex) + 1;
+ startIndex = endIndex + 1;
+
+ attributeCount += 1;
+ }
+
+ return attributeCount;
+ }
+
+
+ /// <summary>
+ /// Extracts a float value from char[] assuming we know the position of the start, end and decimal point.
+ /// </summary>
+ /// <param name="chars"></param>
+ /// <param name="startIndex"></param>
+ /// <param name="length"></param>
+ /// <returns></returns>
+ protected float ConvertToFloat(char[] chars, int startIndex, int length)
+ {
+ return ConvertToFloat(chars, startIndex, length, out int lastIndex);
+ }
+
+
+ /// <summary>
+ /// Extracts a float value from char[] given a start index and length.
+ /// </summary>
+ /// <param name="chars"></param> The Char[] containing the numerical sequence.
+ /// <param name="startIndex"></param> The index of the start of the numerical sequence.
+ /// <param name="length"></param> The length of the numerical sequence.
+ /// <param name="lastIndex"></param> Index of the last character in the validated sequence.
+ /// <returns></returns>
+ protected float ConvertToFloat(char[] chars, int startIndex, int length, out int lastIndex)
+ {
+ if (startIndex == 0) { lastIndex = 0; return -9999; }
+ int endIndex = startIndex + length;
+
+ bool isIntegerValue = true;
+ float decimalPointMultiplier = 0;
+
+ // Set value multiplier checking the first character to determine if we are using '+' or '-'
+ int valueSignMultiplier = 1;
+ if (chars[startIndex] == '+')
+ {
+ valueSignMultiplier = 1;
+ startIndex += 1;
+ }
+ else if (chars[startIndex] == '-')
+ {
+ valueSignMultiplier = -1;
+ startIndex += 1;
+ }
+
+ float value = 0;
+
+ for (int i = startIndex; i < endIndex; i++)
+ {
+ uint c = chars[i];
+
+ if (c >= '0' && c <= '9' || c == '.')
+ {
+ if (c == '.')
+ {
+ isIntegerValue = false;
+ decimalPointMultiplier = 0.1f;
+ continue;
+ }
+
+ //Calculate integer and floating point value
+ if (isIntegerValue)
+ value = value * 10 + (c - 48) * valueSignMultiplier;
+ else
+ {
+ value = value + (c - 48) * decimalPointMultiplier * valueSignMultiplier;
+ decimalPointMultiplier *= 0.1f;
+ }
+
+ continue;
+ }
+ else if (c == ',')
+ {
+ if (i + 1 < endIndex && chars[i + 1] == ' ')
+ lastIndex = i + 1;
+ else
+ lastIndex = i;
+
+ return value;
+ }
+ }
+
+ lastIndex = endIndex;
+ return value;
+ }
+
+
+ /// <summary>
+ /// Function to identify and validate the rich tag. Returns the position of the > if the tag was valid.
+ /// </summary>
+ /// <param name="chars"></param>
+ /// <param name="startIndex"></param>
+ /// <param name="endIndex"></param>
+ /// <returns></returns>
+ protected bool ValidateHtmlTag(UnicodeChar[] chars, int startIndex, out int endIndex)
+ {
+ int tagCharCount = 0;
+ byte attributeFlag = 0;
+
+ int attributeIndex = 0;
+ m_xmlAttribute[attributeIndex].nameHashCode = 0;
+ m_xmlAttribute[attributeIndex].valueHashCode = 0;
+ m_xmlAttribute[attributeIndex].valueStartIndex = 0;
+ m_xmlAttribute[attributeIndex].valueLength = 0;
+ TagValueType tagValueType = m_xmlAttribute[attributeIndex].valueType = TagValueType.None;
+ TagUnitType tagUnitType = m_xmlAttribute[attributeIndex].unitType = TagUnitType.Pixels;
+
+ // Clear attribute name hash codes
+ m_xmlAttribute[1].nameHashCode = 0;
+ m_xmlAttribute[2].nameHashCode = 0;
+ m_xmlAttribute[3].nameHashCode = 0;
+ m_xmlAttribute[4].nameHashCode = 0;
+
+ endIndex = startIndex;
+ bool isTagSet = false;
+ bool isValidHtmlTag = false;
+
+ for (int i = startIndex; i < chars.Length && chars[i].unicode != 0 && tagCharCount < m_htmlTag.Length && chars[i].unicode != '<'; i++)
+ {
+ int unicode = chars[i].unicode;
+
+ if (unicode == '>') // ASCII Code of End HTML tag '>'
+ {
+ isValidHtmlTag = true;
+ endIndex = i;
+ m_htmlTag[tagCharCount] = (char)0;
+ break;
+ }
+
+ m_htmlTag[tagCharCount] = (char)unicode;
+ tagCharCount += 1;
+
+ if (attributeFlag == 1)
+ {
+ if (tagValueType == TagValueType.None)
+ {
+ // Check for attribute type
+ if (unicode == '+' || unicode == '-' || unicode == '.' || (unicode >= '0' && unicode <= '9'))
+ {
+ tagUnitType = TagUnitType.Pixels;
+ tagValueType = m_xmlAttribute[attributeIndex].valueType = TagValueType.NumericalValue;
+ m_xmlAttribute[attributeIndex].valueStartIndex = tagCharCount - 1;
+ m_xmlAttribute[attributeIndex].valueLength += 1;
+ }
+ else if (unicode == '#')
+ {
+ tagUnitType = TagUnitType.Pixels;
+ tagValueType = m_xmlAttribute[attributeIndex].valueType = TagValueType.ColorValue;
+ m_xmlAttribute[attributeIndex].valueStartIndex = tagCharCount - 1;
+ m_xmlAttribute[attributeIndex].valueLength += 1;
+ }
+ else if (unicode == '"')
+ {
+ tagUnitType = TagUnitType.Pixels;
+ tagValueType = m_xmlAttribute[attributeIndex].valueType = TagValueType.StringValue;
+ m_xmlAttribute[attributeIndex].valueStartIndex = tagCharCount;
+ }
+ else
+ {
+ tagUnitType = TagUnitType.Pixels;
+ tagValueType = m_xmlAttribute[attributeIndex].valueType = TagValueType.StringValue;
+ m_xmlAttribute[attributeIndex].valueStartIndex = tagCharCount - 1;
+ m_xmlAttribute[attributeIndex].valueHashCode = (m_xmlAttribute[attributeIndex].valueHashCode << 5) + m_xmlAttribute[attributeIndex].valueHashCode ^ unicode;
+ m_xmlAttribute[attributeIndex].valueLength += 1;
+ }
+ }
+ else
+ {
+ if (tagValueType == TagValueType.NumericalValue)
+ {
+ // Check for termination of numerical value.
+ if (unicode == 'p' || unicode == 'e' || unicode == '%' || unicode == ' ')
+ {
+ attributeFlag = 2;
+ tagValueType = TagValueType.None;
+
+ switch (unicode)
+ {
+ case 'e':
+ m_xmlAttribute[attributeIndex].unitType = tagUnitType = TagUnitType.FontUnits;
+ break;
+ case '%':
+ m_xmlAttribute[attributeIndex].unitType = tagUnitType = TagUnitType.Percentage;
+ break;
+ default:
+ m_xmlAttribute[attributeIndex].unitType = tagUnitType = TagUnitType.Pixels;
+ break;
+ }
+
+ attributeIndex += 1;
+ m_xmlAttribute[attributeIndex].nameHashCode = 0;
+ m_xmlAttribute[attributeIndex].valueHashCode = 0;
+ m_xmlAttribute[attributeIndex].valueType = TagValueType.None;
+ m_xmlAttribute[attributeIndex].unitType = TagUnitType.Pixels;
+ m_xmlAttribute[attributeIndex].valueStartIndex = 0;
+ m_xmlAttribute[attributeIndex].valueLength = 0;
+
+ }
+ else if (attributeFlag != 2)
+ {
+ m_xmlAttribute[attributeIndex].valueLength += 1;
+ }
+ }
+ else if (tagValueType == TagValueType.ColorValue)
+ {
+ if (unicode != ' ')
+ {
+ m_xmlAttribute[attributeIndex].valueLength += 1;
+ }
+ else
+ {
+ attributeFlag = 2;
+ tagValueType = TagValueType.None;
+ tagUnitType = TagUnitType.Pixels;
+ attributeIndex += 1;
+ m_xmlAttribute[attributeIndex].nameHashCode = 0;
+ m_xmlAttribute[attributeIndex].valueType = TagValueType.None;
+ m_xmlAttribute[attributeIndex].unitType = TagUnitType.Pixels;
+ m_xmlAttribute[attributeIndex].valueHashCode = 0;
+ m_xmlAttribute[attributeIndex].valueStartIndex = 0;
+ m_xmlAttribute[attributeIndex].valueLength = 0;
+ }
+ }
+ else if (tagValueType == TagValueType.StringValue)
+ {
+ // Compute HashCode value for the named tag.
+ if (unicode != '"')
+ {
+ m_xmlAttribute[attributeIndex].valueHashCode = (m_xmlAttribute[attributeIndex].valueHashCode << 5) + m_xmlAttribute[attributeIndex].valueHashCode ^ unicode;
+ m_xmlAttribute[attributeIndex].valueLength += 1;
+ }
+ else
+ {
+ attributeFlag = 2;
+ tagValueType = TagValueType.None;
+ tagUnitType = TagUnitType.Pixels;
+ attributeIndex += 1;
+ m_xmlAttribute[attributeIndex].nameHashCode = 0;
+ m_xmlAttribute[attributeIndex].valueType = TagValueType.None;
+ m_xmlAttribute[attributeIndex].unitType = TagUnitType.Pixels;
+ m_xmlAttribute[attributeIndex].valueHashCode = 0;
+ m_xmlAttribute[attributeIndex].valueStartIndex = 0;
+ m_xmlAttribute[attributeIndex].valueLength = 0;
+ }
+ }
+ }
+ }
+
+
+ if (unicode == '=') // '='
+ attributeFlag = 1;
+
+ // Compute HashCode for the name of the attribute
+ if (attributeFlag == 0 && unicode == ' ')
+ {
+ if (isTagSet) return false;
+
+ isTagSet = true;
+ attributeFlag = 2;
+
+ tagValueType = TagValueType.None;
+ tagUnitType = TagUnitType.Pixels;
+ attributeIndex += 1;
+ m_xmlAttribute[attributeIndex].nameHashCode = 0;
+ m_xmlAttribute[attributeIndex].valueType = TagValueType.None;
+ m_xmlAttribute[attributeIndex].unitType = TagUnitType.Pixels;
+ m_xmlAttribute[attributeIndex].valueHashCode = 0;
+ m_xmlAttribute[attributeIndex].valueStartIndex = 0;
+ m_xmlAttribute[attributeIndex].valueLength = 0;
+ }
+
+ if (attributeFlag == 0)
+ m_xmlAttribute[attributeIndex].nameHashCode = (m_xmlAttribute[attributeIndex].nameHashCode << 3) - m_xmlAttribute[attributeIndex].nameHashCode + unicode;
+
+ if (attributeFlag == 2 && unicode == ' ')
+ attributeFlag = 0;
+
+ }
+
+ if (!isValidHtmlTag)
+ {
+ return false;
+ }
+
+ //Debug.Log("Tag is [" + m_htmlTag.ArrayToString() + "]. Tag HashCode: " + m_xmlAttribute[0].nameHashCode + " Tag Value HashCode: " + m_xmlAttribute[0].valueHashCode + " Attribute 1 HashCode: " + m_xmlAttribute[1].nameHashCode + " Value HashCode: " + m_xmlAttribute[1].valueHashCode);
+ //for (int i = 0; i < attributeIndex; i++)
+ // Debug.Log("Tag [" + i + "] with HashCode: " + m_xmlAttribute[i].nameHashCode + " has value of [" + new string(m_htmlTag, m_xmlAttribute[i].valueStartIndex, m_xmlAttribute[i].valueLength) + "] Numerical Value: " + ConvertToFloat(m_htmlTag, m_xmlAttribute[i].valueStartIndex, m_xmlAttribute[i].valueLength));
+
+ #region Rich Text Tag Processing
+ #if !RICH_TEXT_ENABLED
+ // Special handling of the no parsing tag </noparse> </NOPARSE> tag
+ if (tag_NoParsing && (m_xmlAttribute[0].nameHashCode != 53822163 && m_xmlAttribute[0].nameHashCode != 49429939))
+ return false;
+ else if (m_xmlAttribute[0].nameHashCode == 53822163 || m_xmlAttribute[0].nameHashCode == 49429939)
+ {
+ tag_NoParsing = false;
+ return true;
+ }
+
+ // Color <#FFF> 3 Hex values (short form)
+ if (m_htmlTag[0] == 35 && tagCharCount == 4)
+ {
+ m_htmlColor = HexCharsToColor(m_htmlTag, tagCharCount);
+ m_colorStack.Add(m_htmlColor);
+ return true;
+ }
+ // Color <#FFF7> 4 Hex values with alpha (short form)
+ else if (m_htmlTag[0] == 35 && tagCharCount == 5)
+ {
+ m_htmlColor = HexCharsToColor(m_htmlTag, tagCharCount);
+ m_colorStack.Add(m_htmlColor);
+ return true;
+ }
+ // Color <#FF00FF>
+ else if (m_htmlTag[0] == 35 && tagCharCount == 7) // if Tag begins with # and contains 7 characters.
+ {
+ m_htmlColor = HexCharsToColor(m_htmlTag, tagCharCount);
+ m_colorStack.Add(m_htmlColor);
+ return true;
+ }
+ // Color <#FF00FF00> with alpha
+ else if (m_htmlTag[0] == 35 && tagCharCount == 9) // if Tag begins with # and contains 9 characters.
+ {
+ m_htmlColor = HexCharsToColor(m_htmlTag, tagCharCount);
+ m_colorStack.Add(m_htmlColor);
+ return true;
+ }
+ else
+ {
+ float value = 0;
+
+ switch (m_xmlAttribute[0].nameHashCode)
+ {
+ case 98: // <b>
+ case 66: // <B>
+ m_FontStyleInternal |= FontStyles.Bold;
+ m_fontStyleStack.Add(FontStyles.Bold);
+
+ m_FontWeightInternal = FontWeight.Bold;
+ return true;
+ case 427: // </b>
+ case 395: // </B>
+ if ((m_fontStyle & FontStyles.Bold) != FontStyles.Bold)
+ {
+ if (m_fontStyleStack.Remove(FontStyles.Bold) == 0)
+ {
+ m_FontStyleInternal &= ~FontStyles.Bold;
+ m_FontWeightInternal = m_FontWeightStack.Peek();
+ }
+ }
+ return true;
+ case 105: // <i>
+ case 73: // <I>
+ m_FontStyleInternal |= FontStyles.Italic;
+ m_fontStyleStack.Add(FontStyles.Italic);
+ return true;
+ case 434: // </i>
+ case 402: // </I>
+ if ((m_fontStyle & FontStyles.Italic) != FontStyles.Italic)
+ {
+ if (m_fontStyleStack.Remove(FontStyles.Italic) == 0)
+ m_FontStyleInternal &= ~FontStyles.Italic;
+ }
+ return true;
+ case 115: // <s>
+ case 83: // <S>
+ m_FontStyleInternal |= FontStyles.Strikethrough;
+ m_fontStyleStack.Add(FontStyles.Strikethrough);
+
+ if (m_xmlAttribute[1].nameHashCode == 281955 || m_xmlAttribute[1].nameHashCode == 192323)
+ {
+ m_strikethroughColor = HexCharsToColor(m_htmlTag, m_xmlAttribute[1].valueStartIndex, m_xmlAttribute[1].valueLength);
+ m_strikethroughColor.a = m_htmlColor.a < m_strikethroughColor.a ? (byte)(m_htmlColor.a) : (byte)(m_strikethroughColor .a);
+ }
+ else
+ m_strikethroughColor = m_htmlColor;
+
+ m_strikethroughColorStack.Add(m_strikethroughColor);
+
+ return true;
+ case 444: // </s>
+ case 412: // </S>
+ if ((m_fontStyle & FontStyles.Strikethrough) != FontStyles.Strikethrough)
+ {
+ if (m_fontStyleStack.Remove(FontStyles.Strikethrough) == 0)
+ m_FontStyleInternal &= ~FontStyles.Strikethrough;
+ }
+ return true;
+ case 117: // <u>
+ case 85: // <U>
+ m_FontStyleInternal |= FontStyles.Underline;
+ m_fontStyleStack.Add(FontStyles.Underline);
+
+ if (m_xmlAttribute[1].nameHashCode == 281955 || m_xmlAttribute[1].nameHashCode == 192323)
+ {
+ m_underlineColor = HexCharsToColor(m_htmlTag, m_xmlAttribute[1].valueStartIndex, m_xmlAttribute[1].valueLength);
+ m_underlineColor.a = m_htmlColor.a < m_underlineColor.a ? (byte)(m_htmlColor.a) : (byte)(m_underlineColor.a);
+ }
+ else
+ m_underlineColor = m_htmlColor;
+
+ m_underlineColorStack.Add(m_underlineColor);
+
+ return true;
+ case 446: // </u>
+ case 414: // </U>
+ if ((m_fontStyle & FontStyles.Underline) != FontStyles.Underline)
+ {
+ m_underlineColor = m_underlineColorStack.Remove();
+
+ if (m_fontStyleStack.Remove(FontStyles.Underline) == 0)
+ m_FontStyleInternal &= ~FontStyles.Underline;
+ }
+ return true;
+ case 43045: // <mark=#FF00FF80>
+ case 30245: // <MARK>
+ m_FontStyleInternal |= FontStyles.Highlight;
+ m_fontStyleStack.Add(FontStyles.Highlight);
+
+ m_highlightColor = HexCharsToColor(m_htmlTag, m_xmlAttribute[0].valueStartIndex, m_xmlAttribute[0].valueLength);
+ m_highlightColor.a = m_htmlColor.a < m_highlightColor.a ? (byte)(m_htmlColor.a) : (byte)(m_highlightColor.a);
+ m_highlightColorStack.Add(m_highlightColor);
+
+ // Handle Mark Tag Attributes
+ for (int i = 0; i < m_xmlAttribute.Length && m_xmlAttribute[i].nameHashCode != 0; i++)
+ {
+ int nameHashCode = m_xmlAttribute[i].nameHashCode;
+
+ switch (nameHashCode)
+ {
+ case 281955: // color
+ break;
+
+ case 15087385: // padding
+ int paramCount = GetAttributeParameters(m_htmlTag, m_xmlAttribute[i].valueStartIndex, m_xmlAttribute[i].valueLength, ref m_attributeParameterValues);
+ if (paramCount != 4) return false;
+
+ m_highlightPadding = new Vector4(m_attributeParameterValues[0], m_attributeParameterValues[1], m_attributeParameterValues[2], m_attributeParameterValues[3]);
+ break;
+ }
+ }
+
+ return true;
+ case 155892: // </mark>
+ case 143092: // </MARK>
+ if ((m_fontStyle & FontStyles.Highlight) != FontStyles.Highlight)
+ {
+ m_highlightColor = m_highlightColorStack.Remove();
+
+ if (m_fontStyleStack.Remove(FontStyles.Highlight) == 0)
+ m_FontStyleInternal &= ~FontStyles.Highlight;
+ }
+ return true;
+ case 6552: // <sub>
+ case 4728: // <SUB>
+ m_fontScaleMultiplier *= m_currentFontAsset.faceInfo.subscriptSize > 0 ? m_currentFontAsset.faceInfo.subscriptSize : 1;
+ m_baselineOffsetStack.Push(m_baselineOffset);
+ m_baselineOffset += m_currentFontAsset.faceInfo.subscriptOffset * m_fontScale * m_fontScaleMultiplier;
+
+ m_fontStyleStack.Add(FontStyles.Subscript);
+ m_FontStyleInternal |= FontStyles.Subscript;
+ return true;
+ case 22673: // </sub>
+ case 20849: // </SUB>
+ if ((m_FontStyleInternal & FontStyles.Subscript) == FontStyles.Subscript)
+ {
+ if (m_fontScaleMultiplier < 1)
+ {
+ //m_baselineOffset -= m_currentFontAsset.fontInfo.SubscriptOffset * m_fontScale * m_fontScaleMultiplier;
+ m_baselineOffset = m_baselineOffsetStack.Pop();
+ m_fontScaleMultiplier /= m_currentFontAsset.faceInfo.subscriptSize > 0 ? m_currentFontAsset.faceInfo.subscriptSize : 1;
+ }
+
+ if (m_fontStyleStack.Remove(FontStyles.Subscript) == 0)
+ m_FontStyleInternal &= ~FontStyles.Subscript;
+ }
+ return true;
+ case 6566: // <sup>
+ case 4742: // <SUP>
+ m_fontScaleMultiplier *= m_currentFontAsset.faceInfo.superscriptSize > 0 ? m_currentFontAsset.faceInfo.superscriptSize : 1;
+ m_baselineOffsetStack.Push(m_baselineOffset);
+ m_baselineOffset += m_currentFontAsset.faceInfo.superscriptOffset * m_fontScale * m_fontScaleMultiplier;
+
+ m_fontStyleStack.Add(FontStyles.Superscript);
+ m_FontStyleInternal |= FontStyles.Superscript;
+ return true;
+ case 22687: // </sup>
+ case 20863: // </SUP>
+ if ((m_FontStyleInternal & FontStyles.Superscript) == FontStyles.Superscript)
+ {
+ if (m_fontScaleMultiplier < 1)
+ {
+ //m_baselineOffset -= m_currentFontAsset.fontInfo.SuperscriptOffset * m_fontScale * m_fontScaleMultiplier;
+ m_baselineOffset = m_baselineOffsetStack.Pop();
+ m_fontScaleMultiplier /= m_currentFontAsset.faceInfo.superscriptSize > 0 ? m_currentFontAsset.faceInfo.superscriptSize : 1;
+ }
+
+ if (m_fontStyleStack.Remove(FontStyles.Superscript) == 0)
+ m_FontStyleInternal &= ~FontStyles.Superscript;
+ }
+ return true;
+ case -330774850: // <font-weight>
+ case 2012149182: // <FONT-WEIGHT>
+ value = ConvertToFloat(m_htmlTag, m_xmlAttribute[0].valueStartIndex, m_xmlAttribute[0].valueLength);
+
+ //if (value == -9999) return false;
+
+ //if ((m_fontStyle & FontStyles.Bold) == FontStyles.Bold)
+ //{
+ // // Nothing happens since Bold is forced on the text.
+ // //m_fontWeight = 700;
+ // return true;
+ //}
+
+
+ //// Remove bold style
+ //m_style &= ~FontStyles.Bold;
+
+ switch ((int)value)
+ {
+ case 100:
+ m_FontWeightInternal = FontWeight.Thin;
+ break;
+ case 200:
+ m_FontWeightInternal = FontWeight.ExtraLight;
+ break;
+ case 300:
+ m_FontWeightInternal = FontWeight.Light;
+ break;
+ case 400:
+ m_FontWeightInternal = FontWeight.Regular;
+ break;
+ case 500:
+ m_FontWeightInternal = FontWeight.Medium;
+ break;
+ case 600:
+ m_FontWeightInternal = FontWeight.SemiBold;
+ break;
+ case 700:
+ m_FontWeightInternal = FontWeight.Bold;
+ break;
+ case 800:
+ m_FontWeightInternal = FontWeight.Heavy;
+ break;
+ case 900:
+ m_FontWeightInternal = FontWeight.Black;
+ break;
+ }
+
+ m_FontWeightStack.Add(m_FontWeightInternal);
+
+ return true;
+ case -1885698441: // </font-weight>
+ case 457225591: // </FONT-WEIGHT>
+ m_FontWeightStack.Remove();
+
+ if (m_FontStyleInternal == FontStyles.Bold)
+ m_FontWeightInternal = FontWeight.Bold;
+ else
+ m_FontWeightInternal = m_FontWeightStack.Peek();
+
+ return true;
+ case 6380: // <pos=000.00px> <pos=0em> <pos=50%>
+ case 4556: // <POS>
+ value = ConvertToFloat(m_htmlTag, m_xmlAttribute[0].valueStartIndex, m_xmlAttribute[0].valueLength);
+ if (value == -9999) return false;
+
+ switch (tagUnitType)
+ {
+ case TagUnitType.Pixels:
+ m_xAdvance = value * (m_isOrthographic ? 1.0f : 0.1f);
+ //m_isIgnoringAlignment = true;
+ return true;
+ case TagUnitType.FontUnits:
+ m_xAdvance = value * m_currentFontSize * (m_isOrthographic ? 1.0f : 0.1f);
+ //m_isIgnoringAlignment = true;
+ return true;
+ case TagUnitType.Percentage:
+ m_xAdvance = m_marginWidth * value / 100;
+ //m_isIgnoringAlignment = true;
+ return true;
+ }
+ return false;
+ case 22501: // </pos>
+ case 20677: // </POS>
+ m_isIgnoringAlignment = false;
+ return true;
+ case 16034505: // <voffset>
+ case 11642281: // <VOFFSET>
+ value = ConvertToFloat(m_htmlTag, m_xmlAttribute[0].valueStartIndex, m_xmlAttribute[0].valueLength);
+ if (value == -9999) return false;
+
+ switch (tagUnitType)
+ {
+ case TagUnitType.Pixels:
+ m_baselineOffset = value * (m_isOrthographic ? 1 : 0.1f);
+ return true;
+ case TagUnitType.FontUnits:
+ m_baselineOffset = value * (m_isOrthographic ? 1 : 0.1f) * m_currentFontSize;
+ return true;
+ case TagUnitType.Percentage:
+ //m_baselineOffset = m_marginHeight * val / 100;
+ return false;
+ }
+ return false;
+ case 54741026: // </voffset>
+ case 50348802: // </VOFFSET>
+ m_baselineOffset = 0;
+ return true;
+ case 43991: // <page>
+ case 31191: // <PAGE>
+ // This tag only works when Overflow - Page mode is used.
+ if (m_overflowMode == TextOverflowModes.Page)
+ {
+ m_xAdvance = 0 + tag_LineIndent + tag_Indent;
+ m_lineOffset = 0;
+ m_pageNumber += 1;
+ m_isNewPage = true;
+ }
+ return true;
+ // <BR> tag is now handled inline where it is replaced by a linefeed or \n.
+ //case 544: // <BR>
+ //case 800: // <br>
+ // m_forceLineBreak = true;
+ // return true;
+ case 43969: // <nobr>
+ case 31169: // <NOBR>
+ m_isNonBreakingSpace = true;
+ return true;
+ case 156816: // </nobr>
+ case 144016: // </NOBR>
+ m_isNonBreakingSpace = false;
+ return true;
+ case 45545: // <size=>
+ case 32745: // <SIZE>
+ value = ConvertToFloat(m_htmlTag, m_xmlAttribute[0].valueStartIndex, m_xmlAttribute[0].valueLength);
+ if (value == -9999) return false;
+
+ switch (tagUnitType)
+ {
+ case TagUnitType.Pixels:
+ if (m_htmlTag[5] == 43) // <size=+00>
+ {
+ m_currentFontSize = m_fontSize + value;
+ m_sizeStack.Add(m_currentFontSize);
+ m_fontScale = (m_currentFontSize / m_currentFontAsset.faceInfo.pointSize * m_currentFontAsset.faceInfo.scale * (m_isOrthographic ? 1 : 0.1f));
+ return true;
+ }
+ else if (m_htmlTag[5] == 45) // <size=-00>
+ {
+ m_currentFontSize = m_fontSize + value;
+ m_sizeStack.Add(m_currentFontSize);
+ m_fontScale = (m_currentFontSize / m_currentFontAsset.faceInfo.pointSize * m_currentFontAsset.faceInfo.scale * (m_isOrthographic ? 1 : 0.1f));
+ return true;
+ }
+ else // <size=00.0>
+ {
+ m_currentFontSize = value;
+ m_sizeStack.Add(m_currentFontSize);
+ m_fontScale = (m_currentFontSize / m_currentFontAsset.faceInfo.pointSize * m_currentFontAsset.faceInfo.scale * (m_isOrthographic ? 1 : 0.1f));
+ return true;
+ }
+ case TagUnitType.FontUnits:
+ m_currentFontSize = m_fontSize * value;
+ m_sizeStack.Add(m_currentFontSize);
+ m_fontScale = (m_currentFontSize / m_currentFontAsset.faceInfo.pointSize * m_currentFontAsset.faceInfo.scale * (m_isOrthographic ? 1 : 0.1f));
+ return true;
+ case TagUnitType.Percentage:
+ m_currentFontSize = m_fontSize * value / 100;
+ m_sizeStack.Add(m_currentFontSize);
+ m_fontScale = (m_currentFontSize / m_currentFontAsset.faceInfo.pointSize * m_currentFontAsset.faceInfo.scale * (m_isOrthographic ? 1 : 0.1f));
+ return true;
+ }
+ return false;
+ case 158392: // </size>
+ case 145592: // </SIZE>
+ m_currentFontSize = m_sizeStack.Remove();
+ m_fontScale = (m_currentFontSize / m_currentFontAsset.faceInfo.pointSize * m_currentFontAsset.faceInfo.scale * (m_isOrthographic ? 1 : 0.1f));
+ return true;
+ case 41311: // <font=xx>
+ case 28511: // <FONT>
+ int fontHashCode = m_xmlAttribute[0].valueHashCode;
+ int materialAttributeHashCode = m_xmlAttribute[1].nameHashCode;
+ int materialHashCode = m_xmlAttribute[1].valueHashCode;
+
+ // Special handling for <font=default> or <font=Default>
+ if (fontHashCode == 764638571 || fontHashCode == 523367755)
+ {
+ m_currentFontAsset = m_materialReferences[0].fontAsset;
+ m_currentMaterial = m_materialReferences[0].material;
+ m_currentMaterialIndex = 0;
+ //Debug.Log("<font=Default> assigning Font Asset [" + m_currentFontAsset.name + "] with Material [" + m_currentMaterial.name + "].");
+
+ m_fontScale = (m_currentFontSize / m_currentFontAsset.faceInfo.pointSize * m_currentFontAsset.faceInfo.scale * (m_isOrthographic ? 1 : 0.1f));
+
+ m_materialReferenceStack.Add(m_materialReferences[0]);
+
+ return true;
+ }
+
+ TMP_FontAsset tempFont;
+ Material tempMaterial;
+
+ // HANDLE NEW FONT ASSET
+ if (MaterialReferenceManager.TryGetFontAsset(fontHashCode, out tempFont))
+ {
+ //if (tempFont != m_currentFontAsset)
+ //{
+ // //Debug.Log("Assigning Font Asset: " + tempFont.name);
+ // m_currentFontAsset = tempFont;
+ // m_fontScale = (m_currentFontSize / m_currentFontAsset.fontInfo.PointSize * m_currentFontAsset.fontInfo.Scale * (m_isOrthographic ? 1 : 0.1f));
+ //}
+ }
+ else
+ {
+ // Load Font Asset
+ tempFont = Resources.Load<TMP_FontAsset>(TMP_Settings.defaultFontAssetPath + new string(m_htmlTag, m_xmlAttribute[0].valueStartIndex, m_xmlAttribute[0].valueLength));
+
+ if (tempFont == null)
+ return false;
+
+ // Add new reference to the font asset as well as default material to the MaterialReferenceManager
+ MaterialReferenceManager.AddFontAsset(tempFont);
+ }
+
+
+ // HANDLE NEW MATERIAL
+ if (materialAttributeHashCode == 0 && materialHashCode == 0)
+ {
+ // No material specified then use default font asset material.
+ m_currentMaterial = tempFont.material;
+
+ m_currentMaterialIndex = MaterialReference.AddMaterialReference(m_currentMaterial, tempFont, m_materialReferences, m_materialReferenceIndexLookup);
+
+ m_materialReferenceStack.Add(m_materialReferences[m_currentMaterialIndex]);
+ }
+ else if (materialAttributeHashCode == 103415287 || materialAttributeHashCode == 72669687) // using material attribute
+ {
+ if (MaterialReferenceManager.TryGetMaterial(materialHashCode, out tempMaterial))
+ {
+ m_currentMaterial = tempMaterial;
+
+ m_currentMaterialIndex = MaterialReference.AddMaterialReference(m_currentMaterial, tempFont, m_materialReferences, m_materialReferenceIndexLookup);
+
+ m_materialReferenceStack.Add(m_materialReferences[m_currentMaterialIndex]);
+ }
+ else
+ {
+ // Load new material
+ tempMaterial = Resources.Load<Material>(TMP_Settings.defaultFontAssetPath + new string(m_htmlTag, m_xmlAttribute[1].valueStartIndex, m_xmlAttribute[1].valueLength));
+
+ if (tempMaterial == null)
+ return false;
+
+ // Add new reference to this material in the MaterialReferenceManager
+ MaterialReferenceManager.AddFontMaterial(materialHashCode, tempMaterial);
+
+ m_currentMaterial = tempMaterial;
+
+ m_currentMaterialIndex = MaterialReference.AddMaterialReference(m_currentMaterial, tempFont, m_materialReferences, m_materialReferenceIndexLookup);
+
+ m_materialReferenceStack.Add(m_materialReferences[m_currentMaterialIndex]);
+ }
+ }
+ else
+ return false;
+
+ m_currentFontAsset = tempFont;
+ m_fontScale = (m_currentFontSize / m_currentFontAsset.faceInfo.pointSize * m_currentFontAsset.faceInfo.scale * (m_isOrthographic ? 1 : 0.1f));
+
+ return true;
+ case 154158: // </font>
+ case 141358: // </FONT>
+ {
+ MaterialReference materialReference = m_materialReferenceStack.Remove();
+
+ m_currentFontAsset = materialReference.fontAsset;
+ m_currentMaterial = materialReference.material;
+ m_currentMaterialIndex = materialReference.index;
+
+ m_fontScale = (m_currentFontSize / m_currentFontAsset.faceInfo.pointSize * m_currentFontAsset.faceInfo.scale * (m_isOrthographic ? 1 : 0.1f));
+
+ return true;
+ }
+ case 103415287: // <material="material name">
+ case 72669687: // <MATERIAL>
+ materialHashCode = m_xmlAttribute[0].valueHashCode;
+
+ // Special handling for <material=default> or <material=Default>
+ if (materialHashCode == 764638571 || materialHashCode == 523367755)
+ {
+ // Check if material font atlas texture matches that of the current font asset.
+ //if (m_currentFontAsset.atlas.GetInstanceID() != m_currentMaterial.GetTexture(ShaderUtilities.ID_MainTex).GetInstanceID()) return false;
+
+ m_currentMaterial = m_materialReferences[0].material;
+ m_currentMaterialIndex = 0;
+
+ m_materialReferenceStack.Add(m_materialReferences[0]);
+
+ return true;
+ }
+
+
+ // Check if material
+ if (MaterialReferenceManager.TryGetMaterial(materialHashCode, out tempMaterial))
+ {
+ // Check if material font atlas texture matches that of the current font asset.
+ //if (m_currentFontAsset.atlas.GetInstanceID() != tempMaterial.GetTexture(ShaderUtilities.ID_MainTex).GetInstanceID()) return false;
+
+ m_currentMaterial = tempMaterial;
+
+ m_currentMaterialIndex = MaterialReference.AddMaterialReference(m_currentMaterial, m_currentFontAsset, m_materialReferences, m_materialReferenceIndexLookup);
+
+ m_materialReferenceStack.Add(m_materialReferences[m_currentMaterialIndex]);
+ }
+ else
+ {
+ // Load new material
+ tempMaterial = Resources.Load<Material>(TMP_Settings.defaultFontAssetPath + new string(m_htmlTag, m_xmlAttribute[0].valueStartIndex, m_xmlAttribute[0].valueLength));
+
+ if (tempMaterial == null)
+ return false;
+
+ // Check if material font atlas texture matches that of the current font asset.
+ //if (m_currentFontAsset.atlas.GetInstanceID() != tempMaterial.GetTexture(ShaderUtilities.ID_MainTex).GetInstanceID()) return false;
+
+ // Add new reference to this material in the MaterialReferenceManager
+ MaterialReferenceManager.AddFontMaterial(materialHashCode, tempMaterial);
+
+ m_currentMaterial = tempMaterial;
+
+ m_currentMaterialIndex = MaterialReference.AddMaterialReference(m_currentMaterial, m_currentFontAsset , m_materialReferences, m_materialReferenceIndexLookup);
+
+ m_materialReferenceStack.Add(m_materialReferences[m_currentMaterialIndex]);
+ }
+ return true;
+ case 374360934: // </material>
+ case 343615334: // </MATERIAL>
+ {
+ //if (m_currentMaterial.GetTexture(ShaderUtilities.ID_MainTex).GetInstanceID() != m_materialReferenceStack.PreviousItem().material.GetTexture(ShaderUtilities.ID_MainTex).GetInstanceID())
+ // return false;
+
+ MaterialReference materialReference = m_materialReferenceStack.Remove();
+
+ m_currentMaterial = materialReference.material;
+ m_currentMaterialIndex = materialReference.index;
+
+ return true;
+ }
+ case 320078: // <space=000.00>
+ case 230446: // <SPACE>
+ value = ConvertToFloat(m_htmlTag, m_xmlAttribute[0].valueStartIndex, m_xmlAttribute[0].valueLength);
+ if (value == -9999) return false;
+
+ switch (tagUnitType)
+ {
+ case TagUnitType.Pixels:
+ m_xAdvance += value * (m_isOrthographic ? 1 : 0.1f);
+ return true;
+ case TagUnitType.FontUnits:
+ m_xAdvance += value * (m_isOrthographic ? 1 : 0.1f) * m_currentFontSize;
+ return true;
+ case TagUnitType.Percentage:
+ // Not applicable
+ return false;
+ }
+ return false;
+ case 276254: // <alpha=#FF>
+ case 186622: // <ALPHA>
+ if (m_xmlAttribute[0].valueLength != 3) return false;
+
+ m_htmlColor.a = (byte)(HexToInt(m_htmlTag[7]) * 16 + HexToInt(m_htmlTag[8]));
+ return true;
+
+ case 1750458: // <a name=" ">
+ return false;
+ case 426: // </a>
+ return true;
+ case 43066: // <link="name">
+ case 30266: // <LINK>
+ if (m_isParsingText && !m_isCalculatingPreferredValues)
+ {
+ int index = m_textInfo.linkCount;
+
+ if (index + 1 > m_textInfo.linkInfo.Length)
+ TMP_TextInfo.Resize(ref m_textInfo.linkInfo, index + 1);
+
+ m_textInfo.linkInfo[index].textComponent = this;
+ m_textInfo.linkInfo[index].hashCode = m_xmlAttribute[0].valueHashCode;
+ m_textInfo.linkInfo[index].linkTextfirstCharacterIndex = m_characterCount;
+
+ m_textInfo.linkInfo[index].linkIdFirstCharacterIndex = startIndex + m_xmlAttribute[0].valueStartIndex;
+ m_textInfo.linkInfo[index].linkIdLength = m_xmlAttribute[0].valueLength;
+ m_textInfo.linkInfo[index].SetLinkID(m_htmlTag, m_xmlAttribute[0].valueStartIndex, m_xmlAttribute[0].valueLength);
+ }
+ return true;
+ case 155913: // </link>
+ case 143113: // </LINK>
+ if (m_isParsingText && !m_isCalculatingPreferredValues)
+ {
+ if (m_textInfo.linkCount < m_textInfo.linkInfo.Length)
+ {
+ m_textInfo.linkInfo[m_textInfo.linkCount].linkTextLength = m_characterCount - m_textInfo.linkInfo[m_textInfo.linkCount].linkTextfirstCharacterIndex;
+
+ m_textInfo.linkCount += 1;
+ }
+ }
+ return true;
+ case 275917: // <align=>
+ case 186285: // <ALIGN>
+ switch (m_xmlAttribute[0].valueHashCode)
+ {
+ case 3774683: // <align=left>
+ m_lineJustification = TextAlignmentOptions.Left;
+ m_lineJustificationStack.Add(m_lineJustification);
+ return true;
+ case 136703040: // <align=right>
+ m_lineJustification = TextAlignmentOptions.Right;
+ m_lineJustificationStack.Add(m_lineJustification);
+ return true;
+ case -458210101: // <align=center>
+ m_lineJustification = TextAlignmentOptions.Center;
+ m_lineJustificationStack.Add(m_lineJustification);
+ return true;
+ case -523808257: // <align=justified>
+ m_lineJustification = TextAlignmentOptions.Justified;
+ m_lineJustificationStack.Add(m_lineJustification);
+ return true;
+ case 122383428: // <align=flush>
+ m_lineJustification = TextAlignmentOptions.Flush;
+ m_lineJustificationStack.Add(m_lineJustification);
+ return true;
+ }
+ return false;
+ case 1065846: // </align>
+ case 976214: // </ALIGN>
+ m_lineJustification = m_lineJustificationStack.Remove();
+ return true;
+ case 327550: // <width=xx>
+ case 237918: // <WIDTH>
+ value = ConvertToFloat(m_htmlTag, m_xmlAttribute[0].valueStartIndex, m_xmlAttribute[0].valueLength);
+ if (value == -9999) return false;
+
+ switch (tagUnitType)
+ {
+ case TagUnitType.Pixels:
+ m_width = value * (m_isOrthographic ? 1 : 0.1f);
+ break;
+ case TagUnitType.FontUnits:
+ return false;
+ //break;
+ case TagUnitType.Percentage:
+ m_width = m_marginWidth * value / 100;
+ break;
+ }
+ return true;
+ case 1117479: // </width>
+ case 1027847: // </WIDTH>
+ m_width = -1;
+ return true;
+ // STYLE tag is now handled inline and replaced by its definition.
+ //case 322689: // <style="name">
+ //case 233057: // <STYLE>
+ // TMP_Style style = TMP_StyleSheet.GetStyle(m_xmlAttribute[0].valueHashCode);
+
+ // if (style == null) return false;
+
+ // m_styleStack.Add(style.hashCode);
+
+ // // Parse Style Macro
+ // for (int i = 0; i < style.styleOpeningTagArray.Length; i++)
+ // {
+ // if (style.styleOpeningTagArray[i] == 60)
+ // {
+ // if (ValidateHtmlTag(style.styleOpeningTagArray, i + 1, out i) == false) return false;
+ // }
+ // }
+ // return true;
+ //case 1112618: // </style>
+ //case 1022986: // </STYLE>
+ // style = TMP_StyleSheet.GetStyle(m_xmlAttribute[0].valueHashCode);
+
+ // if (style == null)
+ // {
+ // // Get style from the Style Stack
+ // int styleHashCode = m_styleStack.CurrentItem();
+ // style = TMP_StyleSheet.GetStyle(styleHashCode);
+
+ // m_styleStack.Remove();
+ // }
+
+ // if (style == null) return false;
+ // //// Parse Style Macro
+ // for (int i = 0; i < style.styleClosingTagArray.Length; i++)
+ // {
+ // if (style.styleClosingTagArray[i] == 60)
+ // ValidateHtmlTag(style.styleClosingTagArray, i + 1, out i);
+ // }
+ // return true;
+ case 281955: // <color> <color=#FF00FF> or <color=#FF00FF00>
+ case 192323: // <COLOR=#FF00FF>
+ // <color=#FFF> 3 Hex (short hand)
+ if (m_htmlTag[6] == 35 && tagCharCount == 10)
+ {
+ m_htmlColor = HexCharsToColor(m_htmlTag, tagCharCount);
+ m_colorStack.Add(m_htmlColor);
+ return true;
+ }
+ // <color=#FFF7> 4 Hex (short hand)
+ else if (m_htmlTag[6] == 35 && tagCharCount == 11)
+ {
+ m_htmlColor = HexCharsToColor(m_htmlTag, tagCharCount);
+ m_colorStack.Add(m_htmlColor);
+ return true;
+ }
+ // <color=#FF00FF> 3 Hex pairs
+ if (m_htmlTag[6] == 35 && tagCharCount == 13)
+ {
+ m_htmlColor = HexCharsToColor(m_htmlTag, tagCharCount);
+ m_colorStack.Add(m_htmlColor);
+ return true;
+ }
+ // <color=#FF00FF00> 4 Hex pairs
+ else if (m_htmlTag[6] == 35 && tagCharCount == 15)
+ {
+ m_htmlColor = HexCharsToColor(m_htmlTag, tagCharCount);
+ m_colorStack.Add(m_htmlColor);
+ return true;
+ }
+
+ // <color=name>
+ switch (m_xmlAttribute[0].valueHashCode)
+ {
+ case 125395: // <color=red>
+ m_htmlColor = Color.red;
+ m_colorStack.Add(m_htmlColor);
+ return true;
+ case 3573310: // <color=blue>
+ m_htmlColor = Color.blue;
+ m_colorStack.Add(m_htmlColor);
+ return true;
+ case 117905991: // <color=black>
+ m_htmlColor = Color.black;
+ m_colorStack.Add(m_htmlColor);
+ return true;
+ case 121463835: // <color=green>
+ m_htmlColor = Color.green;
+ m_colorStack.Add(m_htmlColor);
+ return true;
+ case 140357351: // <color=white>
+ m_htmlColor = Color.white;
+ m_colorStack.Add(m_htmlColor);
+ return true;
+ case 26556144: // <color=orange>
+ m_htmlColor = new Color32(255, 128, 0, 255);
+ m_colorStack.Add(m_htmlColor);
+ return true;
+ case -36881330: // <color=purple>
+ m_htmlColor = new Color32(160, 32, 240, 255);
+ m_colorStack.Add(m_htmlColor);
+ return true;
+ case 554054276: // <color=yellow>
+ m_htmlColor = Color.yellow;
+ m_colorStack.Add(m_htmlColor);
+ return true;
+ }
+ return false;
+
+ case 100149144: //<gradient>
+ case 69403544: // <GRADIENT>
+ int gradientPresetHashCode = m_xmlAttribute[0].valueHashCode;
+ TMP_ColorGradient tempColorGradientPreset;
+
+ // Check if Color Gradient Preset has already been loaded.
+ if (MaterialReferenceManager.TryGetColorGradientPreset(gradientPresetHashCode, out tempColorGradientPreset))
+ {
+ m_colorGradientPreset = tempColorGradientPreset;
+ }
+ else
+ {
+ // Load Color Gradient Preset
+ if (tempColorGradientPreset == null)
+ {
+ tempColorGradientPreset = Resources.Load<TMP_ColorGradient>(TMP_Settings.defaultColorGradientPresetsPath + new string(m_htmlTag, m_xmlAttribute[0].valueStartIndex, m_xmlAttribute[0].valueLength));
+ }
+
+ if (tempColorGradientPreset == null)
+ return false;
+
+ MaterialReferenceManager.AddColorGradientPreset(gradientPresetHashCode, tempColorGradientPreset);
+ m_colorGradientPreset = tempColorGradientPreset;
+ }
+
+ m_colorGradientStack.Add(m_colorGradientPreset);
+
+ // TODO : Add support for defining preset in the tag itself
+
+ return true;
+
+ case 371094791: // </gradient>
+ case 340349191: // </GRADIENT>
+ m_colorGradientPreset = m_colorGradientStack.Remove();
+ return true;
+
+ case 1983971: // <cspace=xx.x>
+ case 1356515: // <CSPACE>
+ value = ConvertToFloat(m_htmlTag, m_xmlAttribute[0].valueStartIndex, m_xmlAttribute[0].valueLength);
+ if (value == -9999) return false;
+
+ switch (tagUnitType)
+ {
+ case TagUnitType.Pixels:
+ m_cSpacing = value * (m_isOrthographic ? 1 : 0.1f);
+ break;
+ case TagUnitType.FontUnits:
+ m_cSpacing = value * (m_isOrthographic ? 1 : 0.1f) * m_currentFontSize;
+ break;
+ case TagUnitType.Percentage:
+ return false;
+ }
+ return true;
+ case 7513474: // </cspace>
+ case 6886018: // </CSPACE>
+ if (!m_isParsingText) return true;
+
+ // Adjust xAdvance to remove extra space from last character.
+ if (m_characterCount > 0)
+ {
+ m_xAdvance -= m_cSpacing;
+ m_textInfo.characterInfo[m_characterCount - 1].xAdvance = m_xAdvance;
+ }
+ m_cSpacing = 0;
+ return true;
+ case 2152041: // <mspace=xx.x>
+ case 1524585: // <MSPACE>
+ value = ConvertToFloat(m_htmlTag, m_xmlAttribute[0].valueStartIndex, m_xmlAttribute[0].valueLength);
+ if (value == -9999) return false;
+
+ switch (tagUnitType)
+ {
+ case TagUnitType.Pixels:
+ m_monoSpacing = value * (m_isOrthographic ? 1 : 0.1f);
+ break;
+ case TagUnitType.FontUnits:
+ m_monoSpacing = value * (m_isOrthographic ? 1 : 0.1f) * m_currentFontSize;
+ break;
+ case TagUnitType.Percentage:
+ return false;
+ }
+ return true;
+ case 7681544: // </mspace>
+ case 7054088: // </MSPACE>
+ m_monoSpacing = 0;
+ return true;
+ case 280416: // <class="name">
+ return false;
+ case 1071884: // </color>
+ case 982252: // </COLOR>
+ m_htmlColor = m_colorStack.Remove();
+ return true;
+ case 2068980: // <indent=10px> <indent=10em> <indent=50%>
+ case 1441524: // <INDENT>
+ value = ConvertToFloat(m_htmlTag, m_xmlAttribute[0].valueStartIndex, m_xmlAttribute[0].valueLength);
+ if (value == -9999) return false;
+
+ switch (tagUnitType)
+ {
+ case TagUnitType.Pixels:
+ tag_Indent = value * (m_isOrthographic ? 1 : 0.1f);
+ break;
+ case TagUnitType.FontUnits:
+ tag_Indent = value * (m_isOrthographic ? 1 : 0.1f) * m_currentFontSize;
+ break;
+ case TagUnitType.Percentage:
+ tag_Indent = m_marginWidth * value / 100;
+ break;
+ }
+ m_indentStack.Add(tag_Indent);
+
+ m_xAdvance = tag_Indent;
+ return true;
+ case 7598483: // </indent>
+ case 6971027: // </INDENT>
+ tag_Indent = m_indentStack.Remove();
+ //m_xAdvance = tag_Indent;
+ return true;
+ case 1109386397: // <line-indent>
+ case -842656867: // <LINE-INDENT>
+ value = ConvertToFloat(m_htmlTag, m_xmlAttribute[0].valueStartIndex, m_xmlAttribute[0].valueLength);
+ if (value == -9999) return false;
+
+ switch (tagUnitType)
+ {
+ case TagUnitType.Pixels:
+ tag_LineIndent = value * (m_isOrthographic ? 1 : 0.1f);
+ break;
+ case TagUnitType.FontUnits:
+ tag_LineIndent = value * (m_isOrthographic ? 1 : 0.1f) * m_currentFontSize;
+ break;
+ case TagUnitType.Percentage:
+ tag_LineIndent = m_marginWidth * value / 100;
+ break;
+ }
+
+ m_xAdvance += tag_LineIndent;
+ return true;
+ case -445537194: // </line-indent>
+ case 1897386838: // </LINE-INDENT>
+ tag_LineIndent = 0;
+ return true;
+ case 2246877: // <sprite=x>
+ case 1619421: // <SPRITE>
+ int spriteAssetHashCode = m_xmlAttribute[0].valueHashCode;
+ TMP_SpriteAsset tempSpriteAsset;
+ m_spriteIndex = -1;
+
+ // CHECK TAG FORMAT
+ if (m_xmlAttribute[0].valueType == TagValueType.None || m_xmlAttribute[0].valueType == TagValueType.NumericalValue)
+ {
+ // No Sprite Asset is assigned to the text object
+ if (m_spriteAsset != null)
+ {
+ m_currentSpriteAsset = m_spriteAsset;
+ }
+ else if (m_defaultSpriteAsset != null)
+ {
+ m_currentSpriteAsset = m_defaultSpriteAsset;
+ }
+ else if (m_defaultSpriteAsset == null)
+ {
+ if (TMP_Settings.defaultSpriteAsset != null)
+ m_defaultSpriteAsset = TMP_Settings.defaultSpriteAsset;
+ else
+ m_defaultSpriteAsset = Resources.Load<TMP_SpriteAsset>("Sprite Assets/Default Sprite Asset");
+
+ m_currentSpriteAsset = m_defaultSpriteAsset;
+ }
+
+ // No valid sprite asset available
+ if (m_currentSpriteAsset == null)
+ return false;
+ }
+ else
+ {
+ // A Sprite Asset has been specified
+ if (MaterialReferenceManager.TryGetSpriteAsset(spriteAssetHashCode, out tempSpriteAsset))
+ {
+ m_currentSpriteAsset = tempSpriteAsset;
+ }
+ else
+ {
+ // Load Sprite Asset
+ if (tempSpriteAsset == null)
+ {
+ tempSpriteAsset = Resources.Load<TMP_SpriteAsset>(TMP_Settings.defaultSpriteAssetPath + new string(m_htmlTag, m_xmlAttribute[0].valueStartIndex, m_xmlAttribute[0].valueLength));
+ }
+
+ if (tempSpriteAsset == null)
+ return false;
+
+ //Debug.Log("Loading & assigning new Sprite Asset: " + tempSpriteAsset.name);
+ MaterialReferenceManager.AddSpriteAsset(spriteAssetHashCode, tempSpriteAsset);
+ m_currentSpriteAsset = tempSpriteAsset;
+ }
+ }
+
+ // Handling of <sprite=index> legacy tag format.
+ if (m_xmlAttribute[0].valueType == TagValueType.NumericalValue) // <sprite=index>
+ {
+ int index = (int)ConvertToFloat(m_htmlTag, m_xmlAttribute[0].valueStartIndex, m_xmlAttribute[0].valueLength);
+ if (index == -9999) return false;
+
+ // Check to make sure sprite index is valid
+ if (index > m_currentSpriteAsset.spriteCharacterTable.Count - 1) return false;
+
+ m_spriteIndex = index;
+ }
+
+ m_spriteColor = s_colorWhite;
+ m_tintSprite = false;
+
+ // Handle Sprite Tag Attributes
+ for (int i = 0; i < m_xmlAttribute.Length && m_xmlAttribute[i].nameHashCode != 0; i++)
+ {
+ //Debug.Log("Attribute[" + i + "].nameHashCode=" + m_xmlAttribute[i].nameHashCode + " Value:" + ConvertToFloat(m_htmlTag, m_xmlAttribute[i].valueStartIndex, m_xmlAttribute[i].valueLength));
+ int nameHashCode = m_xmlAttribute[i].nameHashCode;
+ int index = 0;
+
+ switch (nameHashCode)
+ {
+ case 43347: // <sprite name="">
+ case 30547: // <SPRITE NAME="">
+ m_currentSpriteAsset = TMP_SpriteAsset.SearchForSpriteByHashCode(m_currentSpriteAsset, m_xmlAttribute[i].valueHashCode, true, out index);
+ if (index == -1) return false;
+
+ m_spriteIndex = index;
+ break;
+ case 295562: // <sprite index=>
+ case 205930: // <SPRITE INDEX=>
+ index = (int)ConvertToFloat(m_htmlTag, m_xmlAttribute[1].valueStartIndex, m_xmlAttribute[1].valueLength);
+ if (index == -9999) return false;
+
+ // Check to make sure sprite index is valid
+ if (index > m_currentSpriteAsset.spriteCharacterTable.Count - 1) return false;
+
+ m_spriteIndex = index;
+ break;
+ case 45819: // tint
+ case 33019: // TINT
+ m_tintSprite = ConvertToFloat(m_htmlTag, m_xmlAttribute[i].valueStartIndex, m_xmlAttribute[i].valueLength) != 0;
+ break;
+ case 281955: // color=#FF00FF80
+ case 192323: // COLOR
+ m_spriteColor = HexCharsToColor(m_htmlTag, m_xmlAttribute[i].valueStartIndex, m_xmlAttribute[i].valueLength);
+ break;
+ case 39505: // anim="0,16,12" start, end, fps
+ case 26705: // ANIM
+ //Debug.Log("Start: " + m_xmlAttribute[i].valueStartIndex + " Length: " + m_xmlAttribute[i].valueLength);
+ int paramCount = GetAttributeParameters(m_htmlTag, m_xmlAttribute[i].valueStartIndex, m_xmlAttribute[i].valueLength, ref m_attributeParameterValues);
+ if (paramCount != 3) return false;
+
+ m_spriteIndex = (int)m_attributeParameterValues[0];
+
+ if (m_isParsingText)
+ {
+ // TODO : fix this!
+ //if (m_attributeParameterValues[0] > m_currentSpriteAsset.spriteInfoList.Count - 1 || m_attributeParameterValues[1] > m_currentSpriteAsset.spriteInfoList.Count - 1)
+ // return false;
+
+ spriteAnimator.DoSpriteAnimation(m_characterCount, m_currentSpriteAsset, m_spriteIndex, (int)m_attributeParameterValues[1], (int)m_attributeParameterValues[2]);
+ }
+
+ break;
+ //case 45545: // size
+ //case 32745: // SIZE
+
+ // break;
+ default:
+ if (nameHashCode != 2246877 && nameHashCode != 1619421)
+ return false;
+ break;
+ }
+ }
+
+ if (m_spriteIndex == -1) return false;
+
+ // Material HashCode for the Sprite Asset is the Sprite Asset Hash Code
+ m_currentMaterialIndex = MaterialReference.AddMaterialReference(m_currentSpriteAsset.material, m_currentSpriteAsset, m_materialReferences, m_materialReferenceIndexLookup);
+
+ m_textElementType = TMP_TextElementType.Sprite;
+ return true;
+ case 730022849: // <lowercase>
+ case 514803617: // <LOWERCASE>
+ m_FontStyleInternal |= FontStyles.LowerCase;
+ m_fontStyleStack.Add(FontStyles.LowerCase);
+ return true;
+ case -1668324918: // </lowercase>
+ case -1883544150: // </LOWERCASE>
+ if ((m_fontStyle & FontStyles.LowerCase) != FontStyles.LowerCase)
+ {
+ if (m_fontStyleStack.Remove(FontStyles.LowerCase) == 0)
+ m_FontStyleInternal &= ~FontStyles.LowerCase;
+ }
+ return true;
+ case 13526026: // <allcaps>
+ case 9133802: // <ALLCAPS>
+ case 781906058: // <uppercase>
+ case 566686826: // <UPPERCASE>
+ m_FontStyleInternal |= FontStyles.UpperCase;
+ m_fontStyleStack.Add(FontStyles.UpperCase);
+ return true;
+ case 52232547: // </allcaps>
+ case 47840323: // </ALLCAPS>
+ case -1616441709: // </uppercase>
+ case -1831660941: // </UPPERCASE>
+ if ((m_fontStyle & FontStyles.UpperCase) != FontStyles.UpperCase)
+ {
+ if (m_fontStyleStack.Remove(FontStyles.UpperCase) == 0)
+ m_FontStyleInternal &= ~FontStyles.UpperCase;
+ }
+ return true;
+ case 766244328: // <smallcaps>
+ case 551025096: // <SMALLCAPS>
+ m_FontStyleInternal |= FontStyles.SmallCaps;
+ m_fontStyleStack.Add(FontStyles.SmallCaps);
+ return true;
+ case -1632103439: // </smallcaps>
+ case -1847322671: // </SMALLCAPS>
+ if ((m_fontStyle & FontStyles.SmallCaps) != FontStyles.SmallCaps)
+ {
+ if (m_fontStyleStack.Remove(FontStyles.SmallCaps) == 0)
+ m_FontStyleInternal &= ~FontStyles.SmallCaps;
+ }
+ return true;
+ case 2109854: // <margin=00.0> <margin=00em> <margin=50%>
+ case 1482398: // <MARGIN>
+ // Check value type
+ switch (m_xmlAttribute[0].valueType)
+ {
+ case TagValueType.NumericalValue:
+ value = ConvertToFloat(m_htmlTag, m_xmlAttribute[0].valueStartIndex, m_xmlAttribute[0].valueLength); // px
+ if (value == -9999) return false;
+
+ // Determine tag unit type
+ switch (tagUnitType)
+ {
+ case TagUnitType.Pixels:
+ m_marginLeft = value * (m_isOrthographic ? 1 : 0.1f);
+ break;
+ case TagUnitType.FontUnits:
+ m_marginLeft = value * (m_isOrthographic ? 1 : 0.1f) * m_currentFontSize;
+ break;
+ case TagUnitType.Percentage:
+ m_marginLeft = (m_marginWidth - (m_width != -1 ? m_width : 0)) * value / 100;
+ break;
+ }
+ m_marginLeft = m_marginLeft >= 0 ? m_marginLeft : 0;
+ m_marginRight = m_marginLeft;
+ return true;
+
+ case TagValueType.None:
+ for (int i = 1; i < m_xmlAttribute.Length && m_xmlAttribute[i].nameHashCode != 0; i++)
+ {
+ // Get attribute name
+ int nameHashCode = m_xmlAttribute[i].nameHashCode;
+
+ switch (nameHashCode)
+ {
+ case 42823: // <margin left=value>
+ value = ConvertToFloat(m_htmlTag, m_xmlAttribute[i].valueStartIndex, m_xmlAttribute[i].valueLength); // px
+ if (value == -9999) return false;
+
+ switch (m_xmlAttribute[i].unitType)
+ {
+ case TagUnitType.Pixels:
+ m_marginLeft = value * (m_isOrthographic ? 1 : 0.1f);
+ break;
+ case TagUnitType.FontUnits:
+ m_marginLeft = value * (m_isOrthographic ? 1 : 0.1f) * m_currentFontSize;
+ break;
+ case TagUnitType.Percentage:
+ m_marginLeft = (m_marginWidth - (m_width != -1 ? m_width : 0)) * value / 100;
+ break;
+ }
+ m_marginLeft = m_marginLeft >= 0 ? m_marginLeft : 0;
+ break;
+
+ case 315620: // <margin right=value>
+ value = ConvertToFloat(m_htmlTag, m_xmlAttribute[i].valueStartIndex, m_xmlAttribute[i].valueLength); // px
+ if (value == -9999) return false;
+
+ switch (m_xmlAttribute[i].unitType)
+ {
+ case TagUnitType.Pixels:
+ m_marginRight = value * (m_isOrthographic ? 1 : 0.1f);
+ break;
+ case TagUnitType.FontUnits:
+ m_marginRight = value * (m_isOrthographic ? 1 : 0.1f) * m_currentFontSize;
+ break;
+ case TagUnitType.Percentage:
+ m_marginRight = (m_marginWidth - (m_width != -1 ? m_width : 0)) * value / 100;
+ break;
+ }
+ m_marginRight = m_marginRight >= 0 ? m_marginRight : 0;
+ break;
+ }
+ }
+ return true;
+ }
+
+ return false;
+ case 7639357: // </margin>
+ case 7011901: // </MARGIN>
+ m_marginLeft = 0;
+ m_marginRight = 0;
+ return true;
+ case 1100728678: // <margin-left=xx.x>
+ case -855002522: // <MARGIN-LEFT>
+ value = ConvertToFloat(m_htmlTag, m_xmlAttribute[0].valueStartIndex, m_xmlAttribute[0].valueLength); // px
+ if (value == -9999) return false;
+
+ switch (tagUnitType)
+ {
+ case TagUnitType.Pixels:
+ m_marginLeft = value * (m_isOrthographic ? 1 : 0.1f);
+ break;
+ case TagUnitType.FontUnits:
+ m_marginLeft = value * (m_isOrthographic ? 1 : 0.1f) * m_currentFontSize;
+ break;
+ case TagUnitType.Percentage:
+ m_marginLeft = (m_marginWidth - (m_width != -1 ? m_width : 0)) * value / 100;
+ break;
+ }
+ m_marginLeft = m_marginLeft >= 0 ? m_marginLeft : 0;
+ return true;
+ case -884817987: // <margin-right=xx.x>
+ case -1690034531: // <MARGIN-RIGHT>
+ value = ConvertToFloat(m_htmlTag, m_xmlAttribute[0].valueStartIndex, m_xmlAttribute[0].valueLength); // px
+ if (value == -9999) return false;
+
+ switch (tagUnitType)
+ {
+ case TagUnitType.Pixels:
+ m_marginRight = value * (m_isOrthographic ? 1 : 0.1f);
+ break;
+ case TagUnitType.FontUnits:
+ m_marginRight = value * (m_isOrthographic ? 1 : 0.1f) * m_currentFontSize;
+ break;
+ case TagUnitType.Percentage:
+ m_marginRight = (m_marginWidth - (m_width != -1 ? m_width : 0)) * value / 100;
+ break;
+ }
+ m_marginRight = m_marginRight >= 0 ? m_marginRight : 0;
+ return true;
+ case 1109349752: // <line-height=xx.x>
+ case -842693512: // <LINE-HEIGHT>
+ value = ConvertToFloat(m_htmlTag, m_xmlAttribute[0].valueStartIndex, m_xmlAttribute[0].valueLength);
+ if (value == -9999 || value == 0) return false;
+
+ switch (tagUnitType)
+ {
+ case TagUnitType.Pixels:
+ m_lineHeight = value * (m_isOrthographic ? 1 : 0.1f);
+ break;
+ case TagUnitType.FontUnits:
+ m_lineHeight = value * (m_isOrthographic ? 1 : 0.1f) * m_currentFontSize;
+ break;
+ case TagUnitType.Percentage:
+ m_lineHeight = m_fontAsset.faceInfo.lineHeight * value / 100 * m_fontScale;
+ break;
+ }
+ return true;
+ case -445573839: // </line-height>
+ case 1897350193: // </LINE-HEIGHT>
+ m_lineHeight = TMP_Math.FLOAT_UNSET;
+ return true;
+ case 15115642: // <noparse>
+ case 10723418: // <NOPARSE>
+ tag_NoParsing = true;
+ return true;
+ case 1913798: // <action>
+ case 1286342: // <ACTION>
+ int actionID = m_xmlAttribute[0].valueHashCode;
+
+ if (m_isParsingText)
+ {
+ m_actionStack.Add(actionID);
+
+ Debug.Log("Action ID: [" + actionID + "] First character index: " + m_characterCount);
+
+
+ }
+ //if (m_isParsingText)
+ //{
+ // TMP_Action action = TMP_Action.GetAction(m_xmlAttribute[0].valueHashCode);
+ //}
+ return true;
+ case 7443301: // </action>
+ case 6815845: // </ACTION>
+ if (m_isParsingText)
+ {
+ Debug.Log("Action ID: [" + m_actionStack.CurrentItem() + "] Last character index: " + (m_characterCount - 1));
+ }
+
+ m_actionStack.Remove();
+ return true;
+ case 315682: // <scale=xx.x>
+ case 226050: // <SCALE=xx.x>
+ value = ConvertToFloat(m_htmlTag, m_xmlAttribute[0].valueStartIndex, m_xmlAttribute[0].valueLength);
+ if (value == -9999) return false;
+
+ m_FXMatrix = Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(value, 1, 1));
+ m_isFXMatrixSet = true;
+
+ return true;
+ case 1105611: // </scale>
+ case 1015979: // </SCALE>
+ m_isFXMatrixSet = false;
+ return true;
+ case 2227963: // <rotate=xx.x>
+ case 1600507: // <ROTATE=xx.x>
+ // TODO: Add ability to use Random Rotation
+
+ value = ConvertToFloat(m_htmlTag, m_xmlAttribute[0].valueStartIndex, m_xmlAttribute[0].valueLength);
+ if (value == -9999) return false;
+
+ m_FXMatrix = Matrix4x4.TRS(Vector3.zero, Quaternion.Euler(0, 0, value), Vector3.one);
+ m_isFXMatrixSet = true;
+
+ return true;
+ case 7757466: // </rotate>
+ case 7130010: // </ROTATE>
+ m_isFXMatrixSet = false;
+ return true;
+ case 317446: // <table>
+ case 227814: // <TABLE>
+ switch (m_xmlAttribute[1].nameHashCode)
+ {
+ case 327550: // width
+ float tableWidth = ConvertToFloat(m_htmlTag, m_xmlAttribute[1].valueStartIndex, m_xmlAttribute[1].valueLength);
+
+ switch (tagUnitType)
+ {
+ case TagUnitType.Pixels:
+ Debug.Log("Table width = " + tableWidth + "px.");
+ break;
+ case TagUnitType.FontUnits:
+ Debug.Log("Table width = " + tableWidth + "em.");
+ break;
+ case TagUnitType.Percentage:
+ Debug.Log("Table width = " + tableWidth + "%.");
+ break;
+ }
+ break;
+ }
+ return true;
+ case 1107375: // </table>
+ case 1017743: // </TABLE>
+ return true;
+ case 926: // <tr>
+ case 670: // <TR>
+ return true;
+ case 3229: // </tr>
+ case 2973: // </TR>
+ return true;
+ case 916: // <th>
+ case 660: // <TH>
+ // Set style to bold and center alignment
+ return true;
+ case 3219: // </th>
+ case 2963: // </TH>
+ return true;
+ case 912: // <td>
+ case 656: // <TD>
+ // Style options
+ for (int i = 1; i < m_xmlAttribute.Length && m_xmlAttribute[i].nameHashCode != 0; i++)
+ {
+ switch (m_xmlAttribute[i].nameHashCode)
+ {
+ case 327550: // width
+ float tableWidth = ConvertToFloat(m_htmlTag, m_xmlAttribute[i].valueStartIndex, m_xmlAttribute[i].valueLength);
+
+ switch (tagUnitType)
+ {
+ case TagUnitType.Pixels:
+ Debug.Log("Table width = " + tableWidth + "px.");
+ break;
+ case TagUnitType.FontUnits:
+ Debug.Log("Table width = " + tableWidth + "em.");
+ break;
+ case TagUnitType.Percentage:
+ Debug.Log("Table width = " + tableWidth + "%.");
+ break;
+ }
+ break;
+ case 275917: // align
+ switch (m_xmlAttribute[i].valueHashCode)
+ {
+ case 3774683: // left
+ Debug.Log("TD align=\"left\".");
+ break;
+ case 136703040: // right
+ Debug.Log("TD align=\"right\".");
+ break;
+ case -458210101: // center
+ Debug.Log("TD align=\"center\".");
+ break;
+ case -523808257: // justified
+ Debug.Log("TD align=\"justified\".");
+ break;
+ }
+ break;
+ }
+ }
+
+ return true;
+ case 3215: // </td>
+ case 2959: // </TD>
+ return true;
+ }
+ }
+ #endif
+ #endregion
+
+ return false;
+ }
+ }
+}
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_Text.cs.meta b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_Text.cs.meta
new file mode 100644
index 0000000..580338a
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_Text.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 5143f58107604835ab1a5efa2d8818fd
+timeCreated: 1445841744
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_TextElement.cs b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_TextElement.cs
new file mode 100644
index 0000000..ff9032d
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_TextElement.cs
@@ -0,0 +1,62 @@
+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 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]
+ private uint m_Unicode;
+
+ private Glyph m_Glyph;
+
+ [SerializeField]
+ private uint m_GlyphIndex;
+
+ [SerializeField]
+ private float m_Scale;
+ }
+}
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_TextElement.cs.meta b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_TextElement.cs.meta
new file mode 100644
index 0000000..2124de4
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_TextElement.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 543674eec776b1442a192c932e6cd9b3
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_TextElement_Legacy.cs b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_TextElement_Legacy.cs
new file mode 100644
index 0000000..65d179b
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_TextElement_Legacy.cs
@@ -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;
+ }
+}
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_TextElement_Legacy.cs.meta b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_TextElement_Legacy.cs.meta
new file mode 100644
index 0000000..222ea85
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_TextElement_Legacy.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 87ab1bebe13f41f89d5427e7d2c34d58
+timeCreated: 1448407070
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_TextInfo.cs b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_TextInfo.cs
new file mode 100644
index 0000000..ce78220
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_TextInfo.cs
@@ -0,0 +1,256 @@
+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
+ {
+ private static Vector2 k_InfinityVectorPositive = new Vector2(32767, 32767);
+ private 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];
+ }
+
+
+ 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>
+ /// 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];
+
+ for (int i = 0; i < this.lineInfo.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].lineExtents.min = k_InfinityVectorPositive;
+ this.lineInfo[i].lineExtents.max = k_InfinityVectorNegative;
+
+ this.lineInfo[i].maxAdvance = 0;
+ //this.lineInfo[i].maxScale = 0;
+
+ }
+ }
+
+
+ /// <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 (size <= array.Length) return;
+
+ if (isBlockAllocated) size = size > 1024 ? size + 256 : Mathf.NextPowerOfTwo(size);
+
+ if (size == array.Length) return;
+
+ Array.Resize(ref array, size);
+ }
+
+ }
+}
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_TextInfo.cs.meta b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_TextInfo.cs.meta
new file mode 100644
index 0000000..faef713
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_TextInfo.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 4ae64f3f72004807a9f919f9c27af0db
+timeCreated: 1446589998
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_TextParsingUtilities.cs b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_TextParsingUtilities.cs
new file mode 100644
index 0000000..8501803
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_TextParsingUtilities.cs
@@ -0,0 +1,136 @@
+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 uint GetHashCode(string s)
+ {
+ uint 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;
+ }
+
+ }
+}
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_TextParsingUtilities.cs.meta b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_TextParsingUtilities.cs.meta
new file mode 100644
index 0000000..1ef488c
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_TextParsingUtilities.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 6ad632cbcc87f634d9b86006cdffdaf5
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_TextUtilities.cs b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_TextUtilities.cs
new file mode 100644
index 0000000..8cbaa48
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_TextUtilities.cs
@@ -0,0 +1,2279 @@
+using UnityEngine;
+using System.Collections;
+
+
+namespace TMPro
+{
+ public enum CaretPosition { None, Left, Right }
+
+ /// <summary>
+ /// Structure which contains the character index and position of caret relative to the character.
+ /// </summary>
+ public struct CaretInfo
+ {
+ public int index;
+ public CaretPosition position;
+
+ public CaretInfo(int index, CaretPosition position)
+ {
+ this.index = index;
+ this.position = position;
+ }
+ }
+
+ public static class TMP_TextUtilities
+ {
+ private static Vector3[] m_rectWorldCorners = new Vector3[4];
+
+
+ // TEXT INPUT COMPONENT RELATED FUNCTIONS
+
+ /// <summary>
+ ///
+ /// </summary>
+ /// <param name="textComponent">A reference to the text object.</param>
+ /// <param name="position">Position to check for intersection.</param>
+ /// <param name="camera">The scene camera which may be assigned to a Canvas using ScreenSpace Camera or WorldSpace render mode. Set to null is using ScreenSpace Overlay.</param>
+ /// <returns></returns>
+ //public static CaretInfo GetCursorInsertionIndex(TMP_Text textComponent, Vector3 position, Camera camera)
+ //{
+ // int index = TMP_TextUtilities.FindNearestCharacter(textComponent, position, camera, false);
+
+ // RectTransform rectTransform = textComponent.rectTransform;
+
+ // // Convert position into Worldspace coordinates
+ // ScreenPointToWorldPointInRectangle(rectTransform, position, camera, out position);
+
+ // TMP_CharacterInfo cInfo = textComponent.textInfo.characterInfo[index];
+
+ // // Get Bottom Left and Top Right position of the current character
+ // Vector3 bl = rectTransform.TransformPoint(cInfo.bottomLeft);
+ // //Vector3 tl = rectTransform.TransformPoint(new Vector3(cInfo.bottomLeft.x, cInfo.topRight.y, 0));
+ // Vector3 tr = rectTransform.TransformPoint(cInfo.topRight);
+ // //Vector3 br = rectTransform.TransformPoint(new Vector3(cInfo.topRight.x, cInfo.bottomLeft.y, 0));
+
+ // float insertPosition = (position.x - bl.x) / (tr.x - bl.x);
+
+ // if (insertPosition < 0.5f)
+ // return new CaretInfo(index, CaretPosition.Left);
+ // else
+ // return new CaretInfo(index, CaretPosition.Right);
+ //}
+
+
+ /// <summary>
+ /// Function returning the index of the character whose origin is closest to the cursor.
+ /// </summary>
+ /// <param name="textComponent">A reference to the text object.</param>
+ /// <param name="position">Position to check for intersection.</param>
+ /// <param name="camera">The scene camera which may be assigned to a Canvas using ScreenSpace Camera or WorldSpace render mode. Set to null is using ScreenSpace Overlay.</param>
+ /// <returns></returns>
+ public static int GetCursorIndexFromPosition(TMP_Text textComponent, Vector3 position, Camera camera)
+ {
+ int index = TMP_TextUtilities.FindNearestCharacter(textComponent, position, camera, false);
+
+ RectTransform rectTransform = textComponent.rectTransform;
+
+ // Convert position into Worldspace coordinates
+ ScreenPointToWorldPointInRectangle(rectTransform, position, camera, out position);
+
+ TMP_CharacterInfo cInfo = textComponent.textInfo.characterInfo[index];
+
+ // Get Bottom Left and Top Right position of the current character
+ Vector3 bl = rectTransform.TransformPoint(cInfo.bottomLeft);
+ Vector3 tr = rectTransform.TransformPoint(cInfo.topRight);
+
+ float insertPosition = (position.x - bl.x) / (tr.x - bl.x);
+
+ if (insertPosition < 0.5f)
+ return index;
+ else
+ return index + 1;
+
+ }
+
+
+ /// <summary>
+ /// Function returning the index of the character whose origin is closest to the cursor.
+ /// </summary>
+ /// <param name="textComponent">A reference to the text object.</param>
+ /// <param name="position">Position to check for intersection.</param>
+ /// <param name="camera">The scene camera which may be assigned to a Canvas using ScreenSpace Camera or WorldSpace render mode. Set to null is using ScreenSpace Overlay.</param>
+ /// <param name="cursor">The position of the cursor insertion position relative to the position.</param>
+ /// <returns></returns>
+ //public static int GetCursorIndexFromPosition(TMP_Text textComponent, Vector3 position, Camera camera, out CaretPosition cursor)
+ //{
+ // int index = TMP_TextUtilities.FindNearestCharacter(textComponent, position, camera, false);
+
+ // RectTransform rectTransform = textComponent.rectTransform;
+
+ // // Convert position into Worldspace coordinates
+ // ScreenPointToWorldPointInRectangle(rectTransform, position, camera, out position);
+
+ // TMP_CharacterInfo cInfo = textComponent.textInfo.characterInfo[index];
+
+ // // Get Bottom Left and Top Right position of the current character
+ // Vector3 bl = rectTransform.TransformPoint(cInfo.bottomLeft);
+ // Vector3 tr = rectTransform.TransformPoint(cInfo.topRight);
+
+ // float insertPosition = (position.x - bl.x) / (tr.x - bl.x);
+
+ // if (insertPosition < 0.5f)
+ // {
+ // cursor = CaretPosition.Left;
+ // return index;
+ // }
+ // else
+ // {
+ // cursor = CaretPosition.Right;
+ // return index;
+ // }
+ //}
+
+
+ /// <summary>
+ /// Function returning the index of the character whose origin is closest to the cursor.
+ /// </summary>
+ /// <param name="textComponent">A reference to the text object.</param>
+ /// <param name="position">Position to check for intersection.</param>
+ /// <param name="camera">The scene camera which may be assigned to a Canvas using ScreenSpace Camera or WorldSpace render mode. Set to null is using ScreenSpace Overlay.</param>
+ /// <param name="cursor">The position of the cursor insertion position relative to the position.</param>
+ /// <returns></returns>
+ public static int GetCursorIndexFromPosition(TMP_Text textComponent, Vector3 position, Camera camera, out CaretPosition cursor)
+ {
+ int line = TMP_TextUtilities.FindNearestLine(textComponent, position, camera);
+
+ int index = FindNearestCharacterOnLine(textComponent, position, line, camera, false);
+
+ // Special handling if line contains only one character.
+ if (textComponent.textInfo.lineInfo[line].characterCount == 1)
+ {
+ cursor = CaretPosition.Left;
+ return index;
+ }
+
+ RectTransform rectTransform = textComponent.rectTransform;
+
+ // Convert position into Worldspace coordinates
+ ScreenPointToWorldPointInRectangle(rectTransform, position, camera, out position);
+
+ TMP_CharacterInfo cInfo = textComponent.textInfo.characterInfo[index];
+
+ // Get Bottom Left and Top Right position of the current character
+ Vector3 bl = rectTransform.TransformPoint(cInfo.bottomLeft);
+ Vector3 tr = rectTransform.TransformPoint(cInfo.topRight);
+
+ float insertPosition = (position.x - bl.x) / (tr.x - bl.x);
+
+ if (insertPosition < 0.5f)
+ {
+ cursor = CaretPosition.Left;
+ return index;
+ }
+ else
+ {
+ cursor = CaretPosition.Right;
+ return index;
+ }
+ }
+
+
+ /// <summary>
+ /// Function returning the line nearest to the position.
+ /// </summary>
+ /// <param name="textComponent"></param>
+ /// <param name="position"></param>
+ /// <param name="camera"></param>
+ /// <returns></returns>
+ public static int FindNearestLine(TMP_Text text, Vector3 position, Camera camera)
+ {
+ RectTransform rectTransform = text.rectTransform;
+
+ float distance = Mathf.Infinity;
+ int closest = -1;
+
+ // Convert position into Worldspace coordinates
+ ScreenPointToWorldPointInRectangle(rectTransform, position, camera, out position);
+
+ for (int i = 0; i < text.textInfo.lineCount; i++)
+ {
+ TMP_LineInfo lineInfo = text.textInfo.lineInfo[i];
+
+ float ascender = rectTransform.TransformPoint(new Vector3(0, lineInfo.ascender, 0)).y;
+ float descender = rectTransform.TransformPoint(new Vector3(0, lineInfo.descender, 0)).y;
+
+ if (ascender > position.y && descender < position.y)
+ {
+ //Debug.Log("Position is on line " + i);
+ return i;
+ }
+
+ float d0 = Mathf.Abs(ascender - position.y);
+ float d1 = Mathf.Abs(descender - position.y);
+
+ float d = Mathf.Min(d0, d1);
+ if (d < distance)
+ {
+ distance = d;
+ closest = i;
+ }
+ }
+
+ //Debug.Log("Closest line to position is " + closest);
+ return closest;
+ }
+
+
+ /// <summary>
+ /// Function returning the nearest character to position on a given line.
+ /// </summary>
+ /// <param name="text"></param>
+ /// <param name="position"></param>
+ /// <param name="line"></param>
+ /// <param name="camera"></param>
+ /// <returns></returns>
+ public static int FindNearestCharacterOnLine(TMP_Text text, Vector3 position, int line, Camera camera, bool visibleOnly)
+ {
+ RectTransform rectTransform = text.rectTransform;
+
+ // Convert position into Worldspace coordinates
+ ScreenPointToWorldPointInRectangle(rectTransform, position, camera, out position);
+
+ int firstCharacter = text.textInfo.lineInfo[line].firstCharacterIndex;
+ int lastCharacter = text.textInfo.lineInfo[line].lastCharacterIndex;
+
+ float distanceSqr = Mathf.Infinity;
+ int closest = lastCharacter;
+
+ for (int i = firstCharacter; i < lastCharacter; i++)
+ {
+ // Get current character info.
+ TMP_CharacterInfo cInfo = text.textInfo.characterInfo[i];
+ if (visibleOnly && !cInfo.isVisible) continue;
+
+ // Get Bottom Left and Top Right position of the current character
+ Vector3 bl = rectTransform.TransformPoint(cInfo.bottomLeft);
+ Vector3 tl = rectTransform.TransformPoint(new Vector3(cInfo.bottomLeft.x, cInfo.topRight.y, 0));
+ Vector3 tr = rectTransform.TransformPoint(cInfo.topRight);
+ Vector3 br = rectTransform.TransformPoint(new Vector3(cInfo.topRight.x, cInfo.bottomLeft.y, 0));
+
+ if (PointIntersectRectangle(position, bl, tl, tr, br))
+ {
+ closest = i;
+ break;
+ }
+
+ // Find the closest corner to position.
+ float dbl = DistanceToLine(bl, tl, position);
+ float dtl = DistanceToLine(tl, tr, position);
+ float dtr = DistanceToLine(tr, br, position);
+ float dbr = DistanceToLine(br, bl, position);
+
+ float d = dbl < dtl ? dbl : dtl;
+ d = d < dtr ? d : dtr;
+ d = d < dbr ? d : dbr;
+
+ if (distanceSqr > d)
+ {
+ distanceSqr = d;
+ closest = i;
+ }
+ }
+ return closest;
+ }
+
+
+ /// <summary>
+ /// Function used to determine if the position intersects with the RectTransform.
+ /// </summary>
+ /// <param name="rectTransform">A reference to the RectTranform of the text object.</param>
+ /// <param name="position">Position to check for intersection.</param>
+ /// <param name="camera">The scene camera which may be assigned to a Canvas using ScreenSpace Camera or WorldSpace render mode. Set to null is using ScreenSpace Overlay.</param>
+ /// <returns></returns>
+ public static bool IsIntersectingRectTransform(RectTransform rectTransform, Vector3 position, Camera camera)
+ {
+ // Convert position into Worldspace coordinates
+ ScreenPointToWorldPointInRectangle(rectTransform, position, camera, out position);
+
+ rectTransform.GetWorldCorners(m_rectWorldCorners);
+
+ if (PointIntersectRectangle(position, m_rectWorldCorners[0], m_rectWorldCorners[1], m_rectWorldCorners[2], m_rectWorldCorners[3]))
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+
+ // CHARACTER HANDLING
+
+ /// <summary>
+ /// Function returning the index of the character at the given position (if any).
+ /// </summary>
+ /// <param name="text">A reference to the TextMeshPro component.</param>
+ /// <param name="position">Position to check for intersection.</param>
+ /// <param name="camera">The scene camera which is rendering the text or whichever one might be assigned to a Canvas using ScreenSpace Camera or WorldSpace render mode. Set to null is using ScreenSpace Overlay.</param>
+ /// <param name="visibleOnly">Only check for visible characters.</param>
+ /// <returns></returns>
+ public static int FindIntersectingCharacter(TMP_Text text, Vector3 position, Camera camera, bool visibleOnly)
+ {
+ RectTransform rectTransform = text.rectTransform;
+
+ // Convert position into Worldspace coordinates
+ ScreenPointToWorldPointInRectangle(rectTransform, position, camera, out position);
+
+ for (int i = 0; i < text.textInfo.characterCount; i++)
+ {
+ // Get current character info.
+ TMP_CharacterInfo cInfo = text.textInfo.characterInfo[i];
+ if (visibleOnly && !cInfo.isVisible) continue;
+
+ // Get Bottom Left and Top Right position of the current character
+ Vector3 bl = rectTransform.TransformPoint(cInfo.bottomLeft);
+ Vector3 tl = rectTransform.TransformPoint(new Vector3(cInfo.bottomLeft.x, cInfo.topRight.y, 0));
+ Vector3 tr = rectTransform.TransformPoint(cInfo.topRight);
+ Vector3 br = rectTransform.TransformPoint(new Vector3(cInfo.topRight.x, cInfo.bottomLeft.y, 0));
+
+ if (PointIntersectRectangle(position, bl, tl, tr, br))
+ return i;
+
+ }
+ return -1;
+ }
+
+
+ /// <summary>
+ /// Function returning the index of the character at the given position (if any).
+ /// </summary>
+ /// <param name="text">A reference to the TextMeshPro UGUI component.</param>
+ /// <param name="position">Position to check for intersection.</param>
+ /// <param name="camera">The camera which is rendering the text object.</param>
+ /// <param name="visibleOnly">Only check for visible characters.</param>
+ /// <returns></returns>
+ //public static int FindIntersectingCharacter(TextMeshPro text, Vector3 position, Camera camera, bool visibleOnly)
+ //{
+ // Transform textTransform = text.transform;
+
+ // // Convert position into Worldspace coordinates
+ // ScreenPointToWorldPointInRectangle(textTransform, position, camera, out position);
+
+ // for (int i = 0; i < text.textInfo.characterCount; i++)
+ // {
+ // // Get current character info.
+ // TMP_CharacterInfo cInfo = text.textInfo.characterInfo[i];
+ // if ((visibleOnly && !cInfo.isVisible) || (text.OverflowMode == TextOverflowModes.Page && cInfo.pageNumber + 1 != text.pageToDisplay))
+ // continue;
+
+ // // Get Bottom Left and Top Right position of the current character
+ // Vector3 bl = textTransform.TransformPoint(cInfo.bottomLeft);
+ // Vector3 tl = textTransform.TransformPoint(new Vector3(cInfo.bottomLeft.x, cInfo.topRight.y, 0));
+ // Vector3 tr = textTransform.TransformPoint(cInfo.topRight);
+ // Vector3 br = textTransform.TransformPoint(new Vector3(cInfo.topRight.x, cInfo.bottomLeft.y, 0));
+
+ // if (PointIntersectRectangle(position, bl, tl, tr, br))
+ // return i;
+
+ // }
+
+ // return -1;
+ //}
+
+
+ /// <summary>
+ /// Function to find the nearest character to position.
+ /// </summary>
+ /// <param name="text">A reference to the TMP Text component.</param>
+ /// <param name="position">Position to check for intersection.</param>
+ /// <param name="camera">The scene camera which may be assigned to a Canvas using ScreenSpace Camera or WorldSpace render mode. Set to null is using ScreenSpace Overlay.</param>
+ /// <param name="visibleOnly">Only check for visible characters.</param>
+ /// <returns></returns>
+ public static int FindNearestCharacter(TMP_Text text, Vector3 position, Camera camera, bool visibleOnly)
+ {
+ RectTransform rectTransform = text.rectTransform;
+
+ float distanceSqr = Mathf.Infinity;
+ int closest = 0;
+
+ // Convert position into Worldspace coordinates
+ ScreenPointToWorldPointInRectangle(rectTransform, position, camera, out position);
+
+ for (int i = 0; i < text.textInfo.characterCount; i++)
+ {
+ // Get current character info.
+ TMP_CharacterInfo cInfo = text.textInfo.characterInfo[i];
+ if (visibleOnly && !cInfo.isVisible) continue;
+
+ // Get Bottom Left and Top Right position of the current character
+ Vector3 bl = rectTransform.TransformPoint(cInfo.bottomLeft);
+ Vector3 tl = rectTransform.TransformPoint(new Vector3(cInfo.bottomLeft.x, cInfo.topRight.y, 0));
+ Vector3 tr = rectTransform.TransformPoint(cInfo.topRight);
+ Vector3 br = rectTransform.TransformPoint(new Vector3(cInfo.topRight.x, cInfo.bottomLeft.y, 0));
+
+ if (PointIntersectRectangle(position, bl, tl, tr, br))
+ return i;
+
+ // Find the closest corner to position.
+ float dbl = DistanceToLine(bl, tl, position);
+ float dtl = DistanceToLine(tl, tr, position);
+ float dtr = DistanceToLine(tr, br, position);
+ float dbr = DistanceToLine(br, bl, position);
+
+ float d = dbl < dtl ? dbl : dtl;
+ d = d < dtr ? d : dtr;
+ d = d < dbr ? d : dbr;
+
+ if (distanceSqr > d)
+ {
+ distanceSqr = d;
+ closest = i;
+ }
+ }
+
+ return closest;
+ }
+
+
+ /// <summary>
+ /// Function to find the nearest character to position.
+ /// </summary>
+ /// <param name="text">A reference to the TextMeshPro UGUI component.</param>
+ /// <param name="position">Position to check for intersection.</param>
+ /// <param name="camera">The scene camera which may be assigned to a Canvas using ScreenSpace Camera or WorldSpace render mode. Set to null is using ScreenSpace Overlay.</param>
+ /// <param name="visibleOnly">Only check for visible characters.</param>
+ /// <returns></returns>
+ //public static int FindNearestCharacter(TextMeshProUGUI text, Vector3 position, Camera camera, bool visibleOnly)
+ //{
+ // RectTransform rectTransform = text.rectTransform;
+
+ // float distanceSqr = Mathf.Infinity;
+ // int closest = 0;
+
+ // // Convert position into Worldspace coordinates
+ // ScreenPointToWorldPointInRectangle(rectTransform, position, camera, out position);
+
+ // for (int i = 0; i < text.textInfo.characterCount; i++)
+ // {
+ // // Get current character info.
+ // TMP_CharacterInfo cInfo = text.textInfo.characterInfo[i];
+ // if ((visibleOnly && !cInfo.isVisible) || (text.OverflowMode == TextOverflowModes.Page && cInfo.pageNumber + 1 != text.pageToDisplay))
+ // continue;
+
+ // // Get Bottom Left and Top Right position of the current character
+ // Vector3 bl = rectTransform.TransformPoint(cInfo.bottomLeft);
+ // Vector3 tl = rectTransform.TransformPoint(new Vector3(cInfo.bottomLeft.x, cInfo.topRight.y, 0));
+ // Vector3 tr = rectTransform.TransformPoint(cInfo.topRight);
+ // Vector3 br = rectTransform.TransformPoint(new Vector3(cInfo.topRight.x, cInfo.bottomLeft.y, 0));
+
+ // if (PointIntersectRectangle(position, bl, tl, tr, br))
+ // return i;
+
+ // // Find the closest corner to position.
+ // float dbl = DistanceToLine(bl, tl, position);
+ // float dtl = DistanceToLine(tl, tr, position);
+ // float dtr = DistanceToLine(tr, br, position);
+ // float dbr = DistanceToLine(br, bl, position);
+
+ // float d = dbl < dtl ? dbl : dtl;
+ // d = d < dtr ? d : dtr;
+ // d = d < dbr ? d : dbr;
+
+ // if (distanceSqr > d)
+ // {
+ // distanceSqr = d;
+ // closest = i;
+ // }
+ // }
+
+ // //Debug.Log("Returning nearest character at index: " + closest);
+
+ // return closest;
+ //}
+
+
+ /// <summary>
+ /// Function to find the nearest character to position.
+ /// </summary>
+ /// <param name="text">A reference to the TextMeshPro component.</param>
+ /// <param name="position">Position to check for intersection.</param>
+ /// <param name="camera">The camera which is rendering the text object.</param>
+ /// <param name="visibleOnly">Only check for visible characters.</param>
+ /// <returns></returns>
+ //public static int FindNearestCharacter(TextMeshPro text, Vector3 position, Camera camera, bool visibleOnly)
+ //{
+ // Transform textTransform = text.transform;
+
+ // float distanceSqr = Mathf.Infinity;
+ // int closest = 0;
+
+ // // Convert position into Worldspace coordinates
+ // ScreenPointToWorldPointInRectangle(textTransform, position, camera, out position);
+
+ // for (int i = 0; i < text.textInfo.characterCount; i++)
+ // {
+ // // Get current character info.
+ // TMP_CharacterInfo cInfo = text.textInfo.characterInfo[i];
+ // if ((visibleOnly && !cInfo.isVisible) || (text.OverflowMode == TextOverflowModes.Page && cInfo.pageNumber + 1 != text.pageToDisplay))
+ // continue;
+
+ // // Get Bottom Left and Top Right position of the current character
+ // Vector3 bl = textTransform.TransformPoint(cInfo.bottomLeft);
+ // Vector3 tl = textTransform.TransformPoint(new Vector3(cInfo.bottomLeft.x, cInfo.topRight.y, 0));
+ // Vector3 tr = textTransform.TransformPoint(cInfo.topRight);
+ // Vector3 br = textTransform.TransformPoint(new Vector3(cInfo.topRight.x, cInfo.bottomLeft.y, 0));
+
+ // if (PointIntersectRectangle(position, bl, tl, tr, br))
+ // return i;
+
+ // // Find the closest corner to position.
+ // float dbl = DistanceToLine(bl, tl, position); // (position - bl).sqrMagnitude;
+ // float dtl = DistanceToLine(tl, tr, position); // (position - tl).sqrMagnitude;
+ // float dtr = DistanceToLine(tr, br, position); // (position - tr).sqrMagnitude;
+ // float dbr = DistanceToLine(br, bl, position); // (position - br).sqrMagnitude;
+
+ // float d = dbl < dtl ? dbl : dtl;
+ // d = d < dtr ? d : dtr;
+ // d = d < dbr ? d : dbr;
+
+ // if (distanceSqr > d)
+ // {
+ // distanceSqr = d;
+ // closest = i;
+ // }
+ // }
+
+ // //Debug.Log("Returning nearest character at index: " + closest);
+
+ // return closest;
+ //}
+
+
+ // WORD HANDLING
+ /// <summary>
+ /// Function returning the index of the word at the given position (if any).
+ /// </summary>
+ /// <param name="text">A reference to the TMP_Text component.</param>
+ /// <param name="position">Position to check for intersection.</param>
+ /// <param name="camera">The scene camera which may be assigned to a Canvas using ScreenSpace Camera or WorldSpace render mode. Set to null is using ScreenSpace Overlay.</param>
+ /// <returns></returns>
+ public static int FindIntersectingWord(TMP_Text text, Vector3 position, Camera camera)
+ {
+ RectTransform rectTransform = text.rectTransform;
+
+ // Convert position into Worldspace coordinates
+ ScreenPointToWorldPointInRectangle(rectTransform, position, camera, out position);
+
+ for (int i = 0; i < text.textInfo.wordCount; i++)
+ {
+ TMP_WordInfo wInfo = text.textInfo.wordInfo[i];
+
+ bool isBeginRegion = false;
+
+ Vector3 bl = Vector3.zero;
+ Vector3 tl = Vector3.zero;
+ Vector3 br = Vector3.zero;
+ Vector3 tr = Vector3.zero;
+
+ float maxAscender = -Mathf.Infinity;
+ float minDescender = Mathf.Infinity;
+
+ // Iterate through each character of the word
+ for (int j = 0; j < wInfo.characterCount; j++)
+ {
+ int characterIndex = wInfo.firstCharacterIndex + j;
+ TMP_CharacterInfo currentCharInfo = text.textInfo.characterInfo[characterIndex];
+ int currentLine = currentCharInfo.lineNumber;
+
+ bool isCharacterVisible = currentCharInfo.isVisible;
+
+ // Track maximum Ascender and minimum Descender for each word.
+ maxAscender = Mathf.Max(maxAscender, currentCharInfo.ascender);
+ minDescender = Mathf.Min(minDescender, currentCharInfo.descender);
+
+ if (isBeginRegion == false && isCharacterVisible)
+ {
+ isBeginRegion = true;
+
+ bl = new Vector3(currentCharInfo.bottomLeft.x, currentCharInfo.descender, 0);
+ tl = new Vector3(currentCharInfo.bottomLeft.x, currentCharInfo.ascender, 0);
+
+ //Debug.Log("Start Word Region at [" + currentCharInfo.character + "]");
+
+ // If Word is one character
+ if (wInfo.characterCount == 1)
+ {
+ isBeginRegion = false;
+
+ br = new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0);
+ tr = new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0);
+
+ // Transform coordinates to be relative to transform and account min descender and max ascender.
+ bl = rectTransform.TransformPoint(new Vector3(bl.x, minDescender, 0));
+ tl = rectTransform.TransformPoint(new Vector3(tl.x, maxAscender, 0));
+ tr = rectTransform.TransformPoint(new Vector3(tr.x, maxAscender, 0));
+ br = rectTransform.TransformPoint(new Vector3(br.x, minDescender, 0));
+
+ // Check for Intersection
+ if (PointIntersectRectangle(position, bl, tl, tr, br))
+ return i;
+
+ //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
+ }
+ }
+
+ // Last Character of Word
+ if (isBeginRegion && j == wInfo.characterCount - 1)
+ {
+ isBeginRegion = false;
+
+ br = new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0);
+ tr = new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0);
+
+ // Transform coordinates to be relative to transform and account min descender and max ascender.
+ bl = rectTransform.TransformPoint(new Vector3(bl.x, minDescender, 0));
+ tl = rectTransform.TransformPoint(new Vector3(tl.x, maxAscender, 0));
+ tr = rectTransform.TransformPoint(new Vector3(tr.x, maxAscender, 0));
+ br = rectTransform.TransformPoint(new Vector3(br.x, minDescender, 0));
+
+ // Check for Intersection
+ if (PointIntersectRectangle(position, bl, tl, tr, br))
+ return i;
+
+ //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
+ }
+ // If Word is split on more than one line.
+ else if (isBeginRegion && currentLine != text.textInfo.characterInfo[characterIndex + 1].lineNumber)
+ {
+ isBeginRegion = false;
+
+ br = new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0);
+ tr = new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0);
+
+ // Transform coordinates to be relative to transform and account min descender and max ascender.
+ bl = rectTransform.TransformPoint(new Vector3(bl.x, minDescender, 0));
+ tl = rectTransform.TransformPoint(new Vector3(tl.x, maxAscender, 0));
+ tr = rectTransform.TransformPoint(new Vector3(tr.x, maxAscender, 0));
+ br = rectTransform.TransformPoint(new Vector3(br.x, minDescender, 0));
+
+ maxAscender = -Mathf.Infinity;
+ minDescender = Mathf.Infinity;
+
+ // Check for Intersection
+ if (PointIntersectRectangle(position, bl, tl, tr, br))
+ return i;
+
+ //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
+ }
+ }
+
+ //Debug.Log("Word at Index: " + i + " is located at (" + bl + ", " + tl + ", " + tr + ", " + br + ").");
+
+ }
+
+ return -1;
+ }
+
+
+ /// <summary>
+ /// Function returning the index of the word at the given position (if any).
+ /// </summary>
+ /// <param name="text">A reference to the TextMeshPro UGUI component.</param>
+ /// <param name="position">Position to check for intersection.</param>
+ /// <param name="camera">The scene camera which may be assigned to a Canvas using ScreenSpace Camera or WorldSpace render mode. Set to null is using ScreenSpace Overlay.</param>
+ /// <returns></returns>
+ //public static int FindIntersectingWord(TextMeshProUGUI text, Vector3 position, Camera camera)
+ //{
+ // RectTransform rectTransform = text.rectTransform;
+
+ // // Convert position into Worldspace coordinates
+ // ScreenPointToWorldPointInRectangle(rectTransform, position, camera, out position);
+
+ // for (int i = 0; i < text.textInfo.wordCount; i++)
+ // {
+ // TMP_WordInfo wInfo = text.textInfo.wordInfo[i];
+
+ // bool isBeginRegion = false;
+
+ // Vector3 bl = Vector3.zero;
+ // Vector3 tl = Vector3.zero;
+ // Vector3 br = Vector3.zero;
+ // Vector3 tr = Vector3.zero;
+
+ // float maxAscender = -Mathf.Infinity;
+ // float minDescender = Mathf.Infinity;
+
+ // // Iterate through each character of the word
+ // for (int j = 0; j < wInfo.characterCount; j++)
+ // {
+ // int characterIndex = wInfo.firstCharacterIndex + j;
+ // TMP_CharacterInfo currentCharInfo = text.textInfo.characterInfo[characterIndex];
+ // int currentLine = currentCharInfo.lineNumber;
+
+ // bool isCharacterVisible = characterIndex > text.maxVisibleCharacters ||
+ // currentCharInfo.lineNumber > text.maxVisibleLines ||
+ // (text.OverflowMode == TextOverflowModes.Page && currentCharInfo.pageNumber + 1 != text.pageToDisplay) ? false : true;
+
+ // // Track maximum Ascender and minimum Descender for each word.
+ // maxAscender = Mathf.Max(maxAscender, currentCharInfo.ascender);
+ // minDescender = Mathf.Min(minDescender, currentCharInfo.descender);
+
+ // if (isBeginRegion == false && isCharacterVisible)
+ // {
+ // isBeginRegion = true;
+
+ // bl = new Vector3(currentCharInfo.bottomLeft.x, currentCharInfo.descender, 0);
+ // tl = new Vector3(currentCharInfo.bottomLeft.x, currentCharInfo.ascender, 0);
+
+ // //Debug.Log("Start Word Region at [" + currentCharInfo.character + "]");
+
+ // // If Word is one character
+ // if (wInfo.characterCount == 1)
+ // {
+ // isBeginRegion = false;
+
+ // br = new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0);
+ // tr = new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0);
+
+ // // Transform coordinates to be relative to transform and account min descender and max ascender.
+ // bl = rectTransform.TransformPoint(new Vector3(bl.x, minDescender, 0));
+ // tl = rectTransform.TransformPoint(new Vector3(tl.x, maxAscender, 0));
+ // tr = rectTransform.TransformPoint(new Vector3(tr.x, maxAscender, 0));
+ // br = rectTransform.TransformPoint(new Vector3(br.x, minDescender, 0));
+
+ // // Check for Intersection
+ // if (PointIntersectRectangle(position, bl, tl, tr, br))
+ // return i;
+
+ // //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
+ // }
+ // }
+
+ // // Last Character of Word
+ // if (isBeginRegion && j == wInfo.characterCount - 1)
+ // {
+ // isBeginRegion = false;
+
+ // br = new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0);
+ // tr = new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0);
+
+ // // Transform coordinates to be relative to transform and account min descender and max ascender.
+ // bl = rectTransform.TransformPoint(new Vector3(bl.x, minDescender, 0));
+ // tl = rectTransform.TransformPoint(new Vector3(tl.x, maxAscender, 0));
+ // tr = rectTransform.TransformPoint(new Vector3(tr.x, maxAscender, 0));
+ // br = rectTransform.TransformPoint(new Vector3(br.x, minDescender, 0));
+
+ // // Check for Intersection
+ // if (PointIntersectRectangle(position, bl, tl, tr, br))
+ // return i;
+
+ // //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
+ // }
+ // // If Word is split on more than one line.
+ // else if (isBeginRegion && currentLine != text.textInfo.characterInfo[characterIndex + 1].lineNumber)
+ // {
+ // isBeginRegion = false;
+
+ // br = new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0);
+ // tr = new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0);
+
+ // // Transform coordinates to be relative to transform and account min descender and max ascender.
+ // bl = rectTransform.TransformPoint(new Vector3(bl.x, minDescender, 0));
+ // tl = rectTransform.TransformPoint(new Vector3(tl.x, maxAscender, 0));
+ // tr = rectTransform.TransformPoint(new Vector3(tr.x, maxAscender, 0));
+ // br = rectTransform.TransformPoint(new Vector3(br.x, minDescender, 0));
+
+ // maxAscender = -Mathf.Infinity;
+ // minDescender = Mathf.Infinity;
+
+ // // Check for Intersection
+ // if (PointIntersectRectangle(position, bl, tl, tr, br))
+ // return i;
+
+ // //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
+ // }
+ // }
+
+ // //Debug.Log("Word at Index: " + i + " is located at (" + bl + ", " + tl + ", " + tr + ", " + br + ").");
+
+ // }
+
+ // return -1;
+ //}
+
+
+ /// <summary>
+ /// Function returning the index of the word at the given position (if any).
+ /// </summary>
+ /// <param name="text">A reference to the TextMeshPro component.</param>
+ /// <param name="position">Position to check for intersection.</param>
+ /// <param name="camera">The camera which is rendering the text object.</param>
+ /// <returns></returns>
+ //public static int FindIntersectingWord(TextMeshPro text, Vector3 position, Camera camera)
+ //{
+ // Transform textTransform = text.transform;
+
+ // // Convert position into Worldspace coordinates
+ // ScreenPointToWorldPointInRectangle(textTransform, position, camera, out position);
+
+ // for (int i = 0; i < text.textInfo.wordCount; i++)
+ // {
+ // TMP_WordInfo wInfo = text.textInfo.wordInfo[i];
+
+ // bool isBeginRegion = false;
+
+ // Vector3 bl = Vector3.zero;
+ // Vector3 tl = Vector3.zero;
+ // Vector3 br = Vector3.zero;
+ // Vector3 tr = Vector3.zero;
+
+ // float maxAscender = -Mathf.Infinity;
+ // float minDescender = Mathf.Infinity;
+
+ // // Iterate through each character of the word
+ // for (int j = 0; j < wInfo.characterCount; j++)
+ // {
+ // int characterIndex = wInfo.firstCharacterIndex + j;
+ // TMP_CharacterInfo currentCharInfo = text.textInfo.characterInfo[characterIndex];
+ // int currentLine = currentCharInfo.lineNumber;
+
+ // bool isCharacterVisible = characterIndex > text.maxVisibleCharacters ||
+ // currentCharInfo.lineNumber > text.maxVisibleLines ||
+ // (text.OverflowMode == TextOverflowModes.Page && currentCharInfo.pageNumber + 1 != text.pageToDisplay) ? false : true;
+
+ // // Track maximum Ascender and minimum Descender for each word.
+ // maxAscender = Mathf.Max(maxAscender, currentCharInfo.ascender);
+ // minDescender = Mathf.Min(minDescender, currentCharInfo.descender);
+
+ // if (isBeginRegion == false && isCharacterVisible)
+ // {
+ // isBeginRegion = true;
+
+ // bl = new Vector3(currentCharInfo.bottomLeft.x, currentCharInfo.descender, 0);
+ // tl = new Vector3(currentCharInfo.bottomLeft.x, currentCharInfo.ascender, 0);
+
+ // //Debug.Log("Start Word Region at [" + currentCharInfo.character + "]");
+
+ // // If Word is one character
+ // if (wInfo.characterCount == 1)
+ // {
+ // isBeginRegion = false;
+
+ // br = new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0);
+ // tr = new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0);
+
+ // // Transform coordinates to be relative to transform and account min descender and max ascender.
+ // bl = textTransform.TransformPoint(new Vector3(bl.x, minDescender, 0));
+ // tl = textTransform.TransformPoint(new Vector3(tl.x, maxAscender, 0));
+ // tr = textTransform.TransformPoint(new Vector3(tr.x, maxAscender, 0));
+ // br = textTransform.TransformPoint(new Vector3(br.x, minDescender, 0));
+
+ // // Check for Intersection
+ // if (PointIntersectRectangle(position, bl, tl, tr, br))
+ // return i;
+
+ // //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
+ // }
+ // }
+
+ // // Last Character of Word
+ // if (isBeginRegion && j == wInfo.characterCount - 1)
+ // {
+ // isBeginRegion = false;
+
+ // br = new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0);
+ // tr = new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0);
+
+ // // Transform coordinates to be relative to transform and account min descender and max ascender.
+ // bl = textTransform.TransformPoint(new Vector3(bl.x, minDescender, 0));
+ // tl = textTransform.TransformPoint(new Vector3(tl.x, maxAscender, 0));
+ // tr = textTransform.TransformPoint(new Vector3(tr.x, maxAscender, 0));
+ // br = textTransform.TransformPoint(new Vector3(br.x, minDescender, 0));
+
+ // // Check for Intersection
+ // if (PointIntersectRectangle(position, bl, tl, tr, br))
+ // return i;
+
+ // //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
+ // }
+ // // If Word is split on more than one line.
+ // else if (isBeginRegion && currentLine != text.textInfo.characterInfo[characterIndex + 1].lineNumber)
+ // {
+ // isBeginRegion = false;
+
+ // br = new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0);
+ // tr = new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0);
+
+ // // Transform coordinates to be relative to transform and account min descender and max ascender.
+ // bl = textTransform.TransformPoint(new Vector3(bl.x, minDescender, 0));
+ // tl = textTransform.TransformPoint(new Vector3(tl.x, maxAscender, 0));
+ // tr = textTransform.TransformPoint(new Vector3(tr.x, maxAscender, 0));
+ // br = textTransform.TransformPoint(new Vector3(br.x, minDescender, 0));
+
+ // // Reset maxAscender and minDescender for next word segment.
+ // maxAscender = -Mathf.Infinity;
+ // minDescender = Mathf.Infinity;
+
+ // // Check for Intersection
+ // if (PointIntersectRectangle(position, bl, tl, tr, br))
+ // return i;
+
+ // //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
+ // }
+ // }
+ // }
+
+ // return -1;
+ //}
+
+
+ /// <summary>
+ /// Function returning the index of the word at the given position (if any).
+ /// </summary>
+ /// <param name="text">A reference to the TMP_Text component.</param>
+ /// <param name="position"></param>
+ /// <param name="camera">The scene camera which may be assigned to a Canvas using ScreenSpace Camera or WorldSpace render mode. Set to null is using ScreenSpace Overlay.</param>
+ /// <returns></returns>
+ public static int FindNearestWord(TMP_Text text, Vector3 position, Camera camera)
+ {
+ RectTransform rectTransform = text.rectTransform;
+
+ float distanceSqr = Mathf.Infinity;
+ int closest = 0;
+
+ // Convert position into Worldspace coordinates
+ ScreenPointToWorldPointInRectangle(rectTransform, position, camera, out position);
+
+ for (int i = 0; i < text.textInfo.wordCount; i++)
+ {
+ TMP_WordInfo wInfo = text.textInfo.wordInfo[i];
+
+ bool isBeginRegion = false;
+
+ Vector3 bl = Vector3.zero;
+ Vector3 tl = Vector3.zero;
+ Vector3 br = Vector3.zero;
+ Vector3 tr = Vector3.zero;
+
+ // Iterate through each character of the word
+ for (int j = 0; j < wInfo.characterCount; j++)
+ {
+ int characterIndex = wInfo.firstCharacterIndex + j;
+ TMP_CharacterInfo currentCharInfo = text.textInfo.characterInfo[characterIndex];
+ int currentLine = currentCharInfo.lineNumber;
+
+ bool isCharacterVisible = currentCharInfo.isVisible;
+
+ if (isBeginRegion == false && isCharacterVisible)
+ {
+ isBeginRegion = true;
+
+ bl = rectTransform.TransformPoint(new Vector3(currentCharInfo.bottomLeft.x, currentCharInfo.descender, 0));
+ tl = rectTransform.TransformPoint(new Vector3(currentCharInfo.bottomLeft.x, currentCharInfo.ascender, 0));
+
+ //Debug.Log("Start Word Region at [" + currentCharInfo.character + "]");
+
+ // If Word is one character
+ if (wInfo.characterCount == 1)
+ {
+ isBeginRegion = false;
+
+ br = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0));
+ tr = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0));
+
+ // Check for Intersection
+ if (PointIntersectRectangle(position, bl, tl, tr, br))
+ return i;
+
+ // Find the closest line segment to position.
+ float dbl = DistanceToLine(bl, tl, position);
+ float dtl = DistanceToLine(tl, tr, position);
+ float dtr = DistanceToLine(tr, br, position);
+ float dbr = DistanceToLine(br, bl, position);
+
+ float d = dbl < dtl ? dbl : dtl;
+ d = d < dtr ? d : dtr;
+ d = d < dbr ? d : dbr;
+
+ if (distanceSqr > d)
+ {
+ distanceSqr = d;
+ closest = i;
+ }
+ }
+ }
+
+ // Last Character of Word
+ if (isBeginRegion && j == wInfo.characterCount - 1)
+ {
+ isBeginRegion = false;
+
+ br = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0));
+ tr = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0));
+
+ // Check for Intersection
+ if (PointIntersectRectangle(position, bl, tl, tr, br))
+ return i;
+
+ // Find the closest line segment to position.
+ float dbl = DistanceToLine(bl, tl, position);
+ float dtl = DistanceToLine(tl, tr, position);
+ float dtr = DistanceToLine(tr, br, position);
+ float dbr = DistanceToLine(br, bl, position);
+
+ float d = dbl < dtl ? dbl : dtl;
+ d = d < dtr ? d : dtr;
+ d = d < dbr ? d : dbr;
+
+ if (distanceSqr > d)
+ {
+ distanceSqr = d;
+ closest = i;
+ }
+ }
+ // If Word is split on more than one line.
+ else if (isBeginRegion && currentLine != text.textInfo.characterInfo[characterIndex + 1].lineNumber)
+ {
+ isBeginRegion = false;
+
+ br = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0));
+ tr = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0));
+
+ // Check for Intersection
+ if (PointIntersectRectangle(position, bl, tl, tr, br))
+ return i;
+
+ // Find the closest line segment to position.
+ float dbl = DistanceToLine(bl, tl, position);
+ float dtl = DistanceToLine(tl, tr, position);
+ float dtr = DistanceToLine(tr, br, position);
+ float dbr = DistanceToLine(br, bl, position);
+
+ float d = dbl < dtl ? dbl : dtl;
+ d = d < dtr ? d : dtr;
+ d = d < dbr ? d : dbr;
+
+ if (distanceSqr > d)
+ {
+ distanceSqr = d;
+ closest = i;
+ }
+ }
+ }
+
+ //Debug.Log("Word at Index: " + i + " is located at (" + bl + ", " + tl + ", " + tr + ", " + br + ").");
+ }
+
+ return closest;
+ }
+
+ /// <summary>
+ /// Function returning the index of the word at the given position (if any).
+ /// </summary>
+ /// <param name="text">A reference to the TextMeshPro UGUI component.</param>
+ /// <param name="position"></param>
+ /// <param name="camera">The scene camera which may be assigned to a Canvas using ScreenSpace Camera or WorldSpace render mode. Set to null is using ScreenSpace Overlay.</param>
+ /// <returns></returns>
+ //public static int FindNearestWord(TextMeshProUGUI text, Vector3 position, Camera camera)
+ //{
+ // RectTransform rectTransform = text.rectTransform;
+
+ // float distanceSqr = Mathf.Infinity;
+ // int closest = 0;
+
+ // // Convert position into Worldspace coordinates
+ // ScreenPointToWorldPointInRectangle(rectTransform, position, camera, out position);
+
+ // for (int i = 0; i < text.textInfo.wordCount; i++)
+ // {
+ // TMP_WordInfo wInfo = text.textInfo.wordInfo[i];
+
+ // bool isBeginRegion = false;
+
+ // Vector3 bl = Vector3.zero;
+ // Vector3 tl = Vector3.zero;
+ // Vector3 br = Vector3.zero;
+ // Vector3 tr = Vector3.zero;
+
+ // // Iterate through each character of the word
+ // for (int j = 0; j < wInfo.characterCount; j++)
+ // {
+ // int characterIndex = wInfo.firstCharacterIndex + j;
+ // TMP_CharacterInfo currentCharInfo = text.textInfo.characterInfo[characterIndex];
+ // int currentLine = currentCharInfo.lineNumber;
+
+ // bool isCharacterVisible = characterIndex > text.maxVisibleCharacters ||
+ // currentCharInfo.lineNumber > text.maxVisibleLines ||
+ // (text.OverflowMode == TextOverflowModes.Page && currentCharInfo.pageNumber + 1 != text.pageToDisplay) ? false : true;
+
+ // if (isBeginRegion == false && isCharacterVisible)
+ // {
+ // isBeginRegion = true;
+
+ // bl = rectTransform.TransformPoint(new Vector3(currentCharInfo.bottomLeft.x, currentCharInfo.descender, 0));
+ // tl = rectTransform.TransformPoint(new Vector3(currentCharInfo.bottomLeft.x, currentCharInfo.ascender, 0));
+
+ // //Debug.Log("Start Word Region at [" + currentCharInfo.character + "]");
+
+ // // If Word is one character
+ // if (wInfo.characterCount == 1)
+ // {
+ // isBeginRegion = false;
+
+ // br = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0));
+ // tr = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0));
+
+ // // Check for Intersection
+ // if (PointIntersectRectangle(position, bl, tl, tr, br))
+ // return i;
+
+ // //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
+ // }
+ // }
+
+ // // Last Character of Word
+ // if (isBeginRegion && j == wInfo.characterCount - 1)
+ // {
+ // isBeginRegion = false;
+
+ // br = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0));
+ // tr = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0));
+
+ // // Check for Intersection
+ // if (PointIntersectRectangle(position, bl, tl, tr, br))
+ // return i;
+
+ // //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
+ // }
+ // // If Word is split on more than one line.
+ // else if (isBeginRegion && currentLine != text.textInfo.characterInfo[characterIndex + 1].lineNumber)
+ // {
+ // isBeginRegion = false;
+
+ // br = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0));
+ // tr = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0));
+
+ // // Check for Intersection
+ // if (PointIntersectRectangle(position, bl, tl, tr, br))
+ // return i;
+
+ // //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
+ // }
+ // }
+
+ // // Find the closest line segment to position.
+ // float dbl = DistanceToLine(bl, tl, position); // (position - bl).sqrMagnitude;
+ // float dtl = DistanceToLine(tl, tr, position); // (position - tl).sqrMagnitude;
+ // float dtr = DistanceToLine(tr, br, position); // (position - tr).sqrMagnitude;
+ // float dbr = DistanceToLine(br, bl, position); // (position - br).sqrMagnitude;
+
+ // float d = dbl < dtl ? dbl : dtl;
+ // d = d < dtr ? d : dtr;
+ // d = d < dbr ? d : dbr;
+
+ // if (distanceSqr > d)
+ // {
+ // distanceSqr = d;
+ // closest = i;
+ // }
+ // //Debug.Log("Word at Index: " + i + " is located at (" + bl + ", " + tl + ", " + tr + ", " + br + ").");
+
+ // }
+
+ // return closest;
+ //}
+
+
+ /// <summary>
+ /// Function returning the index of the word at the given position (if any).
+ /// </summary>
+ /// <param name="text">A reference to the TextMeshPro UGUI component.</param>
+ /// <param name="position">Position to check for intersection.</param>
+ /// <param name="camera">The camera which is rendering the text object.</param>
+ /// <returns></returns>
+ //public static int FindNearestWord(TextMeshPro text, Vector3 position, Camera camera)
+ //{
+ // Transform textTransform = text.transform;
+
+ // float distanceSqr = Mathf.Infinity;
+ // int closest = 0;
+
+ // // Convert position into Worldspace coordinates
+ // ScreenPointToWorldPointInRectangle(textTransform, position, camera, out position);
+
+ // for (int i = 0; i < text.textInfo.wordCount; i++)
+ // {
+ // TMP_WordInfo wInfo = text.textInfo.wordInfo[i];
+
+ // bool isBeginRegion = false;
+
+ // Vector3 bl = Vector3.zero;
+ // Vector3 tl = Vector3.zero;
+ // Vector3 br = Vector3.zero;
+ // Vector3 tr = Vector3.zero;
+
+ // // Iterate through each character of the word
+ // for (int j = 0; j < wInfo.characterCount; j++)
+ // {
+ // int characterIndex = wInfo.firstCharacterIndex + j;
+ // TMP_CharacterInfo currentCharInfo = text.textInfo.characterInfo[characterIndex];
+ // int currentLine = currentCharInfo.lineNumber;
+
+ // bool isCharacterVisible = characterIndex > text.maxVisibleCharacters ||
+ // currentCharInfo.lineNumber > text.maxVisibleLines ||
+ // (text.OverflowMode == TextOverflowModes.Page && currentCharInfo.pageNumber + 1 != text.pageToDisplay) ? false : true;
+
+ // if (isBeginRegion == false && isCharacterVisible)
+ // {
+ // isBeginRegion = true;
+
+ // bl = textTransform.TransformPoint(new Vector3(currentCharInfo.bottomLeft.x, currentCharInfo.descender, 0));
+ // tl = textTransform.TransformPoint(new Vector3(currentCharInfo.bottomLeft.x, currentCharInfo.ascender, 0));
+
+ // //Debug.Log("Start Word Region at [" + currentCharInfo.character + "]");
+
+ // // If Word is one character
+ // if (wInfo.characterCount == 1)
+ // {
+ // isBeginRegion = false;
+
+ // br = textTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0));
+ // tr = textTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0));
+
+ // // Check for Intersection
+ // if (PointIntersectRectangle(position, bl, tl, tr, br))
+ // return i;
+
+ // //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
+ // }
+ // }
+
+ // // Last Character of Word
+ // if (isBeginRegion && j == wInfo.characterCount - 1)
+ // {
+ // isBeginRegion = false;
+
+ // br = textTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0));
+ // tr = textTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0));
+
+ // // Check for Intersection
+ // if (PointIntersectRectangle(position, bl, tl, tr, br))
+ // return i;
+
+ // //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
+ // }
+ // // If Word is split on more than one line.
+ // else if (isBeginRegion && currentLine != text.textInfo.characterInfo[characterIndex + 1].lineNumber)
+ // {
+ // isBeginRegion = false;
+
+ // br = textTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0));
+ // tr = textTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0));
+
+ // // Check for Intersection
+ // if (PointIntersectRectangle(position, bl, tl, tr, br))
+ // return i;
+
+ // //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
+ // }
+ // }
+
+ // // Find the closest line segment to position.
+ // float dbl = DistanceToLine(bl, tl, position);
+ // float dtl = DistanceToLine(tl, tr, position);
+ // float dtr = DistanceToLine(tr, br, position);
+ // float dbr = DistanceToLine(br, bl, position);
+
+ // float d = dbl < dtl ? dbl : dtl;
+ // d = d < dtr ? d : dtr;
+ // d = d < dbr ? d : dbr;
+
+ // if (distanceSqr > d)
+ // {
+ // distanceSqr = d;
+ // closest = i;
+ // }
+ // //Debug.Log("Word at Index: " + i + " is located at (" + bl + ", " + tl + ", " + tr + ", " + br + ").");
+
+ // }
+
+ // return closest;
+
+ //}
+
+
+ /// <summary>
+ /// Function returning the line intersecting the position.
+ /// </summary>
+ /// <param name="textComponent"></param>
+ /// <param name="position"></param>
+ /// <param name="camera"></param>
+ /// <returns></returns>
+ public static int FindIntersectingLine(TMP_Text text, Vector3 position, Camera camera)
+ {
+ RectTransform rectTransform = text.rectTransform;
+
+ int closest = -1;
+
+ // Convert position into Worldspace coordinates
+ ScreenPointToWorldPointInRectangle(rectTransform, position, camera, out position);
+
+ for (int i = 0; i < text.textInfo.lineCount; i++)
+ {
+ TMP_LineInfo lineInfo = text.textInfo.lineInfo[i];
+
+ float ascender = rectTransform.TransformPoint(new Vector3(0, lineInfo.ascender, 0)).y;
+ float descender = rectTransform.TransformPoint(new Vector3(0, lineInfo.descender, 0)).y;
+
+ if (ascender > position.y && descender < position.y)
+ {
+ //Debug.Log("Position is on line " + i);
+ return i;
+ }
+ }
+
+ //Debug.Log("Closest line to position is " + closest);
+ return closest;
+ }
+
+
+ /// <summary>
+ /// Function returning the index of the Link at the given position (if any).
+ /// </summary>
+ /// <param name="text">A reference to the TMP_Text component.</param>
+ /// <param name="position">Position to check for intersection.</param>
+ /// <param name="camera">The scene camera which may be assigned to a Canvas using ScreenSpace Camera or WorldSpace render mode. Set to null is using ScreenSpace Overlay.</param>
+ /// <returns></returns>
+ public static int FindIntersectingLink(TMP_Text text, Vector3 position, Camera camera)
+ {
+ Transform rectTransform = text.transform;
+
+ // Convert position into Worldspace coordinates
+ ScreenPointToWorldPointInRectangle(rectTransform, position, camera, out position);
+
+ for (int i = 0; i < text.textInfo.linkCount; i++)
+ {
+ TMP_LinkInfo linkInfo = text.textInfo.linkInfo[i];
+
+ bool isBeginRegion = false;
+
+ Vector3 bl = Vector3.zero;
+ Vector3 tl = Vector3.zero;
+ Vector3 br = Vector3.zero;
+ Vector3 tr = Vector3.zero;
+
+ // Iterate through each character of the word
+ for (int j = 0; j < linkInfo.linkTextLength; j++)
+ {
+ int characterIndex = linkInfo.linkTextfirstCharacterIndex + j;
+ TMP_CharacterInfo currentCharInfo = text.textInfo.characterInfo[characterIndex];
+ int currentLine = currentCharInfo.lineNumber;
+
+ // Check if Link characters are on the current page
+ if (text.overflowMode == TextOverflowModes.Page && currentCharInfo.pageNumber + 1 != text.pageToDisplay) continue;
+
+ if (isBeginRegion == false)
+ {
+ isBeginRegion = true;
+
+ bl = rectTransform.TransformPoint(new Vector3(currentCharInfo.bottomLeft.x, currentCharInfo.descender, 0));
+ tl = rectTransform.TransformPoint(new Vector3(currentCharInfo.bottomLeft.x, currentCharInfo.ascender, 0));
+
+ //Debug.Log("Start Word Region at [" + currentCharInfo.character + "]");
+
+ // If Word is one character
+ if (linkInfo.linkTextLength == 1)
+ {
+ isBeginRegion = false;
+
+ br = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0));
+ tr = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0));
+
+ // Check for Intersection
+ if (PointIntersectRectangle(position, bl, tl, tr, br))
+ return i;
+
+ //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
+ }
+ }
+
+ // Last Character of Word
+ if (isBeginRegion && j == linkInfo.linkTextLength - 1)
+ {
+ isBeginRegion = false;
+
+ br = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0));
+ tr = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0));
+
+ // Check for Intersection
+ if (PointIntersectRectangle(position, bl, tl, tr, br))
+ return i;
+
+ //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
+ }
+ // If Word is split on more than one line.
+ else if (isBeginRegion && currentLine != text.textInfo.characterInfo[characterIndex + 1].lineNumber)
+ {
+ isBeginRegion = false;
+
+ br = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0));
+ tr = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0));
+
+ // Check for Intersection
+ if (PointIntersectRectangle(position, bl, tl, tr, br))
+ return i;
+
+ //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
+ }
+ }
+
+ //Debug.Log("Word at Index: " + i + " is located at (" + bl + ", " + tl + ", " + tr + ", " + br + ").");
+
+ }
+
+ return -1;
+ }
+
+ /// <summary>
+ /// Function returning the index of the Link at the given position (if any).
+ /// </summary>
+ /// <param name="text">A reference to the TextMeshPro UGUI component.</param>
+ /// <param name="position">Position to check for intersection.</param>
+ /// <param name="camera">The scene camera which may be assigned to a Canvas using ScreenSpace Camera or WorldSpace render mode. Set to null is using ScreenSpace Overlay.</param>
+ /// <returns></returns>
+ //public static int FindIntersectingLink(TextMeshProUGUI text, Vector3 position, Camera camera)
+ //{
+ // Transform rectTransform = text.transform;
+
+ // // Convert position into Worldspace coordinates
+ // ScreenPointToWorldPointInRectangle(rectTransform, position, camera, out position);
+
+ // for (int i = 0; i < text.textInfo.linkCount; i++)
+ // {
+ // TMP_LinkInfo linkInfo = text.textInfo.linkInfo[i];
+
+ // bool isBeginRegion = false;
+
+ // Vector3 bl = Vector3.zero;
+ // Vector3 tl = Vector3.zero;
+ // Vector3 br = Vector3.zero;
+ // Vector3 tr = Vector3.zero;
+
+ // // Iterate through each character of the word
+ // for (int j = 0; j < linkInfo.linkTextLength; j++)
+ // {
+ // int characterIndex = linkInfo.linkTextfirstCharacterIndex + j;
+ // TMP_CharacterInfo currentCharInfo = text.textInfo.characterInfo[characterIndex];
+ // int currentLine = currentCharInfo.lineNumber;
+
+ // // Check if Link characters are on the current page
+ // if (text.OverflowMode == TextOverflowModes.Page && currentCharInfo.pageNumber + 1 != text.pageToDisplay) continue;
+
+ // if (isBeginRegion == false)
+ // {
+ // isBeginRegion = true;
+
+ // bl = rectTransform.TransformPoint(new Vector3(currentCharInfo.bottomLeft.x, currentCharInfo.descender, 0));
+ // tl = rectTransform.TransformPoint(new Vector3(currentCharInfo.bottomLeft.x, currentCharInfo.ascender, 0));
+
+ // //Debug.Log("Start Word Region at [" + currentCharInfo.character + "]");
+
+ // // If Word is one character
+ // if (linkInfo.linkTextLength == 1)
+ // {
+ // isBeginRegion = false;
+
+ // br = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0));
+ // tr = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0));
+
+ // // Check for Intersection
+ // if (PointIntersectRectangle(position, bl, tl, tr, br))
+ // return i;
+
+ // //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
+ // }
+ // }
+
+ // // Last Character of Word
+ // if (isBeginRegion && j == linkInfo.linkTextLength - 1)
+ // {
+ // isBeginRegion = false;
+
+ // br = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0));
+ // tr = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0));
+
+ // // Check for Intersection
+ // if (PointIntersectRectangle(position, bl, tl, tr, br))
+ // return i;
+
+ // //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
+ // }
+ // // If Word is split on more than one line.
+ // else if (isBeginRegion && currentLine != text.textInfo.characterInfo[characterIndex + 1].lineNumber)
+ // {
+ // isBeginRegion = false;
+
+ // br = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0));
+ // tr = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0));
+
+ // // Check for Intersection
+ // if (PointIntersectRectangle(position, bl, tl, tr, br))
+ // return i;
+
+ // //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
+ // }
+ // }
+
+ // //Debug.Log("Word at Index: " + i + " is located at (" + bl + ", " + tl + ", " + tr + ", " + br + ").");
+
+ // }
+
+ // return -1;
+ //}
+
+
+ /// <summary>
+ /// Function returning the index of the Link at the given position (if any).
+ /// </summary>
+ /// <param name="text">A reference to the TextMeshPro component.</param>
+ /// <param name="position">Position to check for intersection.</param>
+ /// <param name="camera">The camera which is rendering the text object.</param>
+ /// <returns></returns>
+ //public static int FindIntersectingLink(TextMeshPro text, Vector3 position, Camera camera)
+ //{
+ // Transform textTransform = text.transform;
+
+ // // Convert position into Worldspace coordinates
+ // ScreenPointToWorldPointInRectangle(textTransform, position, camera, out position);
+
+ // for (int i = 0; i < text.textInfo.linkCount; i++)
+ // {
+ // TMP_LinkInfo linkInfo = text.textInfo.linkInfo[i];
+
+ // bool isBeginRegion = false;
+
+ // Vector3 bl = Vector3.zero;
+ // Vector3 tl = Vector3.zero;
+ // Vector3 br = Vector3.zero;
+ // Vector3 tr = Vector3.zero;
+
+ // // Iterate through each character of the word
+ // for (int j = 0; j < linkInfo.linkTextLength; j++)
+ // {
+ // int characterIndex = linkInfo.linkTextfirstCharacterIndex + j;
+ // TMP_CharacterInfo currentCharInfo = text.textInfo.characterInfo[characterIndex];
+ // int currentLine = currentCharInfo.lineNumber;
+
+ // // Check if Link characters are on the current page
+ // if (text.OverflowMode == TextOverflowModes.Page && currentCharInfo.pageNumber + 1 != text.pageToDisplay) continue;
+
+ // if (isBeginRegion == false)
+ // {
+ // isBeginRegion = true;
+
+ // bl = textTransform.TransformPoint(new Vector3(currentCharInfo.bottomLeft.x, currentCharInfo.descender, 0));
+ // tl = textTransform.TransformPoint(new Vector3(currentCharInfo.bottomLeft.x, currentCharInfo.ascender, 0));
+
+ // //Debug.Log("Start Word Region at [" + currentCharInfo.character + "]");
+
+ // // If Word is one character
+ // if (linkInfo.linkTextLength == 1)
+ // {
+ // isBeginRegion = false;
+
+ // br = textTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0));
+ // tr = textTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0));
+
+ // // Check for Intersection
+ // if (PointIntersectRectangle(position, bl, tl, tr, br))
+ // return i;
+
+ // //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
+ // }
+ // }
+
+ // // Last Character of Word
+ // if (isBeginRegion && j == linkInfo.linkTextLength - 1)
+ // {
+ // isBeginRegion = false;
+
+ // br = textTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0));
+ // tr = textTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0));
+
+ // // Check for Intersection
+ // if (PointIntersectRectangle(position, bl, tl, tr, br))
+ // return i;
+
+ // //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
+ // }
+ // // If Word is split on more than one line.
+ // else if (isBeginRegion && currentLine != text.textInfo.characterInfo[characterIndex + 1].lineNumber)
+ // {
+ // isBeginRegion = false;
+
+ // br = textTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0));
+ // tr = textTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0));
+
+ // // Check for Intersection
+ // if (PointIntersectRectangle(position, bl, tl, tr, br))
+ // return i;
+
+ // //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
+ // }
+ // }
+
+ // //Debug.Log("Word at Index: " + i + " is located at (" + bl + ", " + tl + ", " + tr + ", " + br + ").");
+
+ // }
+
+ // return -1;
+ //}
+
+
+ /// <summary>
+ /// Function returning the index of the word at the given position (if any).
+ /// </summary>
+ /// <param name="text">A reference to the TMP_Text component.</param>
+ /// <param name="position">Position to check for intersection.</param>
+ /// <param name="camera">The scene camera which may be assigned to a Canvas using ScreenSpace Camera or WorldSpace render mode. Set to null is using ScreenSpace Overlay.</param>
+ /// <returns></returns>
+ public static int FindNearestLink(TMP_Text text, Vector3 position, Camera camera)
+ {
+ RectTransform rectTransform = text.rectTransform;
+
+ // Convert position into Worldspace coordinates
+ ScreenPointToWorldPointInRectangle(rectTransform, position, camera, out position);
+
+ float distanceSqr = Mathf.Infinity;
+ int closest = 0;
+
+ for (int i = 0; i < text.textInfo.linkCount; i++)
+ {
+ TMP_LinkInfo linkInfo = text.textInfo.linkInfo[i];
+
+ bool isBeginRegion = false;
+
+ Vector3 bl = Vector3.zero;
+ Vector3 tl = Vector3.zero;
+ Vector3 br = Vector3.zero;
+ Vector3 tr = Vector3.zero;
+
+ // Iterate through each character of the link
+ for (int j = 0; j < linkInfo.linkTextLength; j++)
+ {
+ int characterIndex = linkInfo.linkTextfirstCharacterIndex + j;
+ TMP_CharacterInfo currentCharInfo = text.textInfo.characterInfo[characterIndex];
+ int currentLine = currentCharInfo.lineNumber;
+
+ // Check if Link characters are on the current page
+ if (text.overflowMode == TextOverflowModes.Page && currentCharInfo.pageNumber + 1 != text.pageToDisplay) continue;
+
+ if (isBeginRegion == false)
+ {
+ isBeginRegion = true;
+
+ //Debug.Log("Start Word Region at [" + currentCharInfo.character + "]");
+
+ bl = rectTransform.TransformPoint(new Vector3(currentCharInfo.bottomLeft.x, currentCharInfo.descender, 0));
+ tl = rectTransform.TransformPoint(new Vector3(currentCharInfo.bottomLeft.x, currentCharInfo.ascender, 0));
+
+ // If Link is one character
+ if (linkInfo.linkTextLength == 1)
+ {
+ isBeginRegion = false;
+
+ br = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0));
+ tr = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0));
+
+ // Check for Intersection
+ if (PointIntersectRectangle(position, bl, tl, tr, br))
+ return i;
+
+ // Find the closest line segment to position.
+ float dbl = DistanceToLine(bl, tl, position);
+ float dtl = DistanceToLine(tl, tr, position);
+ float dtr = DistanceToLine(tr, br, position);
+ float dbr = DistanceToLine(br, bl, position);
+
+ float d = dbl < dtl ? dbl : dtl;
+ d = d < dtr ? d : dtr;
+ d = d < dbr ? d : dbr;
+
+ if (distanceSqr > d)
+ {
+ distanceSqr = d;
+ closest = i;
+ }
+
+ }
+ }
+
+ // Last Character of Word
+ if (isBeginRegion && j == linkInfo.linkTextLength - 1)
+ {
+ isBeginRegion = false;
+
+ br = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0));
+ tr = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0));
+
+ // Check for Intersection
+ if (PointIntersectRectangle(position, bl, tl, tr, br))
+ return i;
+
+ // Find the closest line segment to position.
+ float dbl = DistanceToLine(bl, tl, position);
+ float dtl = DistanceToLine(tl, tr, position);
+ float dtr = DistanceToLine(tr, br, position);
+ float dbr = DistanceToLine(br, bl, position);
+
+ float d = dbl < dtl ? dbl : dtl;
+ d = d < dtr ? d : dtr;
+ d = d < dbr ? d : dbr;
+
+ if (distanceSqr > d)
+ {
+ distanceSqr = d;
+ closest = i;
+ }
+
+ }
+ // If Link is split on more than one line.
+ else if (isBeginRegion && currentLine != text.textInfo.characterInfo[characterIndex + 1].lineNumber)
+ {
+ isBeginRegion = false;
+
+ br = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0));
+ tr = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0));
+
+ // Check for Intersection
+ if (PointIntersectRectangle(position, bl, tl, tr, br))
+ return i;
+
+ // Find the closest line segment to position.
+ float dbl = DistanceToLine(bl, tl, position);
+ float dtl = DistanceToLine(tl, tr, position);
+ float dtr = DistanceToLine(tr, br, position);
+ float dbr = DistanceToLine(br, bl, position);
+
+ float d = dbl < dtl ? dbl : dtl;
+ d = d < dtr ? d : dtr;
+ d = d < dbr ? d : dbr;
+
+ if (distanceSqr > d)
+ {
+ distanceSqr = d;
+ closest = i;
+ }
+ }
+ }
+
+ //Debug.Log("Word at Index: " + i + " is located at (" + bl + ", " + tl + ", " + tr + ", " + br + ").");
+
+ }
+
+ return closest;
+ }
+
+
+ /// <summary>
+ /// Function returning the index of the word at the given position (if any).
+ /// </summary>
+ /// <param name="text">A reference to the TextMeshPro UGUI component.</param>
+ /// <param name="position">Position to check for intersection.</param>
+ /// <param name="camera">The scene camera which may be assigned to a Canvas using ScreenSpace Camera or WorldSpace render mode. Set to null is using ScreenSpace Overlay.</param>
+ /// <returns></returns>
+ //public static int FindNearestLink(TextMeshProUGUI text, Vector3 position, Camera camera)
+ //{
+ // RectTransform rectTransform = text.rectTransform;
+
+ // // Convert position into Worldspace coordinates
+ // ScreenPointToWorldPointInRectangle(rectTransform, position, camera, out position);
+
+ // float distanceSqr = Mathf.Infinity;
+ // int closest = 0;
+
+ // for (int i = 0; i < text.textInfo.linkCount; i++)
+ // {
+ // TMP_LinkInfo linkInfo = text.textInfo.linkInfo[i];
+
+ // bool isBeginRegion = false;
+
+ // Vector3 bl = Vector3.zero;
+ // Vector3 tl = Vector3.zero;
+ // Vector3 br = Vector3.zero;
+ // Vector3 tr = Vector3.zero;
+
+ // // Iterate through each character of the word
+ // for (int j = 0; j < linkInfo.linkTextLength; j++)
+ // {
+ // int characterIndex = linkInfo.linkTextfirstCharacterIndex + j;
+ // TMP_CharacterInfo currentCharInfo = text.textInfo.characterInfo[characterIndex];
+ // int currentLine = currentCharInfo.lineNumber;
+
+ // if (isBeginRegion == false)
+ // {
+ // isBeginRegion = true;
+
+ // bl = rectTransform.TransformPoint(new Vector3(currentCharInfo.bottomLeft.x, currentCharInfo.descender, 0));
+ // tl = rectTransform.TransformPoint(new Vector3(currentCharInfo.bottomLeft.x, currentCharInfo.ascender, 0));
+
+ // //Debug.Log("Start Word Region at [" + currentCharInfo.character + "]");
+
+ // // If Word is one character
+ // if (linkInfo.linkTextLength == 1)
+ // {
+ // isBeginRegion = false;
+
+ // br = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0));
+ // tr = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0));
+
+ // // Check for Intersection
+ // if (PointIntersectRectangle(position, bl, tl, tr, br))
+ // return i;
+
+ // //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
+ // }
+ // }
+
+ // // Last Character of Word
+ // if (isBeginRegion && j == linkInfo.linkTextLength - 1)
+ // {
+ // isBeginRegion = false;
+
+ // br = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0));
+ // tr = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0));
+
+ // // Check for Intersection
+ // if (PointIntersectRectangle(position, bl, tl, tr, br))
+ // return i;
+
+ // //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
+ // }
+ // // If Word is split on more than one line.
+ // else if (isBeginRegion && currentLine != text.textInfo.characterInfo[characterIndex + 1].lineNumber)
+ // {
+ // isBeginRegion = false;
+
+ // br = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0));
+ // tr = rectTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0));
+
+ // // Check for Intersection
+ // if (PointIntersectRectangle(position, bl, tl, tr, br))
+ // return i;
+
+ // //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
+ // }
+ // }
+
+ // // Find the closest line segment to position.
+ // float dbl = DistanceToLine(bl, tl, position); // (position - bl).sqrMagnitude;
+ // float dtl = DistanceToLine(tl, tr, position); // (position - tl).sqrMagnitude;
+ // float dtr = DistanceToLine(tr, br, position); // (position - tr).sqrMagnitude;
+ // float dbr = DistanceToLine(br, bl, position); // (position - br).sqrMagnitude;
+
+ // float d = dbl < dtl ? dbl : dtl;
+ // d = d < dtr ? d : dtr;
+ // d = d < dbr ? d : dbr;
+
+ // if (distanceSqr > d)
+ // {
+ // distanceSqr = d;
+ // closest = i;
+ // }
+ // //Debug.Log("Word at Index: " + i + " is located at (" + bl + ", " + tl + ", " + tr + ", " + br + ").");
+
+ // }
+
+ // return closest;
+ //}
+
+
+ /// <summary>
+ /// Function returning the index of the word at the given position (if any).
+ /// </summary>
+ /// <param name="text">A reference to the TextMeshPro component.</param>
+ /// <param name="position">Position to check for intersection.</param>
+ /// <param name="camera">The camera which is rendering the text object.</param>
+ /// <returns></returns>
+ //public static int FindNearestLink(TextMeshPro text, Vector3 position, Camera camera)
+ //{
+ // Transform textTransform = text.transform;
+
+ // // Convert position into Worldspace coordinates
+ // ScreenPointToWorldPointInRectangle(textTransform, position, camera, out position);
+
+ // float distanceSqr = Mathf.Infinity;
+ // int closest = 0;
+
+ // for (int i = 0; i < text.textInfo.linkCount; i++)
+ // {
+ // TMP_LinkInfo linkInfo = text.textInfo.linkInfo[i];
+
+ // bool isBeginRegion = false;
+
+ // Vector3 bl = Vector3.zero;
+ // Vector3 tl = Vector3.zero;
+ // Vector3 br = Vector3.zero;
+ // Vector3 tr = Vector3.zero;
+
+ // // Iterate through each character of the word
+ // for (int j = 0; j < linkInfo.linkTextLength; j++)
+ // {
+ // int characterIndex = linkInfo.linkTextfirstCharacterIndex + j;
+ // TMP_CharacterInfo currentCharInfo = text.textInfo.characterInfo[characterIndex];
+ // int currentLine = currentCharInfo.lineNumber;
+
+ // if (isBeginRegion == false)
+ // {
+ // isBeginRegion = true;
+
+ // bl = textTransform.TransformPoint(new Vector3(currentCharInfo.bottomLeft.x, currentCharInfo.descender, 0));
+ // tl = textTransform.TransformPoint(new Vector3(currentCharInfo.bottomLeft.x, currentCharInfo.ascender, 0));
+
+ // //Debug.Log("Start Word Region at [" + currentCharInfo.character + "]");
+
+ // // If Word is one character
+ // if (linkInfo.linkTextLength == 1)
+ // {
+ // isBeginRegion = false;
+
+ // br = textTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0));
+ // tr = textTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0));
+
+ // // Check for Intersection
+ // if (PointIntersectRectangle(position, bl, tl, tr, br))
+ // return i;
+
+ // //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
+ // }
+ // }
+
+ // // Last Character of Word
+ // if (isBeginRegion && j == linkInfo.linkTextLength - 1)
+ // {
+ // isBeginRegion = false;
+
+ // br = textTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0));
+ // tr = textTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0));
+
+ // // Check for Intersection
+ // if (PointIntersectRectangle(position, bl, tl, tr, br))
+ // return i;
+
+ // //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
+ // }
+ // // If Word is split on more than one line.
+ // else if (isBeginRegion && currentLine != text.textInfo.characterInfo[characterIndex + 1].lineNumber)
+ // {
+ // isBeginRegion = false;
+
+ // br = textTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.descender, 0));
+ // tr = textTransform.TransformPoint(new Vector3(currentCharInfo.topRight.x, currentCharInfo.ascender, 0));
+
+ // // Check for Intersection
+ // if (PointIntersectRectangle(position, bl, tl, tr, br))
+ // return i;
+
+ // //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
+ // }
+ // }
+
+ // // Find the closest line segment to position.
+ // float dbl = DistanceToLine(bl, tl, position);
+ // float dtl = DistanceToLine(tl, tr, position);
+ // float dtr = DistanceToLine(tr, br, position);
+ // float dbr = DistanceToLine(br, bl, position);
+
+ // float d = dbl < dtl ? dbl : dtl;
+ // d = d < dtr ? d : dtr;
+ // d = d < dbr ? d : dbr;
+
+ // if (distanceSqr > d)
+ // {
+ // distanceSqr = d;
+ // closest = i;
+ // }
+ // //Debug.Log("Word at Index: " + i + " is located at (" + bl + ", " + tl + ", " + tr + ", " + br + ").");
+
+ // }
+ // return closest;
+ //}
+
+
+
+ /// <summary>
+ /// Function to check if a Point is contained within a Rectangle.
+ /// </summary>
+ /// <param name="m"></param>
+ /// <param name="a"></param>
+ /// <param name="b"></param>
+ /// <param name="c"></param>
+ /// <param name="d"></param>
+ /// <returns></returns>
+ private static bool PointIntersectRectangle(Vector3 m, Vector3 a, Vector3 b, Vector3 c, Vector3 d)
+ {
+ Vector3 ab = b - a;
+ Vector3 am = m - a;
+ Vector3 bc = c - b;
+ Vector3 bm = m - b;
+
+ float abamDot = Vector3.Dot(ab, am);
+ float bcbmDot = Vector3.Dot(bc, bm);
+
+ return 0 <= abamDot && abamDot <= Vector3.Dot(ab, ab) && 0 <= bcbmDot && bcbmDot <= Vector3.Dot(bc, bc);
+ }
+
+
+ /// <summary>
+ /// Method to convert ScreenPoint to WorldPoint aligned with Rectangle
+ /// </summary>
+ /// <param name="transform"></param>
+ /// <param name="screenPoint"></param>
+ /// <param name="cam"></param>
+ /// <param name="worldPoint"></param>
+ /// <returns></returns>
+ public static bool ScreenPointToWorldPointInRectangle(Transform transform, Vector2 screenPoint, Camera cam, out Vector3 worldPoint)
+ {
+ worldPoint = (Vector3)Vector2.zero;
+ Ray ray = RectTransformUtility.ScreenPointToRay(cam, screenPoint);
+
+ if (!new Plane(transform.rotation * Vector3.back, transform.position).Raycast(ray, out float enter))
+ return false;
+
+ worldPoint = ray.GetPoint(enter);
+
+ return true;
+ }
+
+
+ private struct LineSegment
+ {
+ public Vector3 Point1;
+ public Vector3 Point2;
+
+ public LineSegment(Vector3 p1, Vector3 p2)
+ {
+ Point1 = p1;
+ Point2 = p2;
+ }
+ }
+
+
+ /// <summary>
+ /// Function returning the point of intersection between a line and a plane.
+ /// </summary>
+ /// <param name="line"></param>
+ /// <param name="point"></param>
+ /// <param name="normal"></param>
+ /// <param name="intersectingPoint"></param>
+ /// <returns></returns>
+ private static bool IntersectLinePlane(LineSegment line, Vector3 point, Vector3 normal, out Vector3 intersectingPoint)
+ {
+ intersectingPoint = Vector3.zero;
+ Vector3 u = line.Point2 - line.Point1;
+ Vector3 w = line.Point1 - point;
+
+ float D = Vector3.Dot(normal, u);
+ float N = -Vector3.Dot(normal, w);
+
+ if (Mathf.Abs(D) < Mathf.Epsilon) // if line is parallel & co-planar to plane
+ {
+ if (N == 0)
+ return true;
+ else
+ return false;
+ }
+
+ float sI = N / D;
+
+ if (sI < 0 || sI > 1) // Line parallel to plane
+ return false;
+
+ intersectingPoint = line.Point1 + sI * u;
+
+ return true;
+ }
+
+
+ /// <summary>
+ /// Function returning the Square Distance from a Point to a Line.
+ /// </summary>
+ /// <param name="a"></param>
+ /// <param name="b"></param>
+ /// <param name="point"></param>
+ /// <returns></returns>
+ public static float DistanceToLine(Vector3 a, Vector3 b, Vector3 point)
+ {
+ Vector3 n = b - a;
+ Vector3 pa = a - point;
+
+ float c = Vector3.Dot( n, pa );
+
+ // Closest point is a
+ if ( c > 0.0f )
+ return Vector3.Dot( pa, pa );
+
+ Vector3 bp = point - b;
+
+ // Closest point is b
+ if (Vector3.Dot( n, bp ) > 0.0f )
+ return Vector3.Dot( bp, bp );
+
+ // Closest point is between a and b
+ Vector3 e = pa - n * (c / Vector3.Dot( n, n ));
+
+ return Vector3.Dot( e, e );
+ }
+
+
+ /// <summary>
+ /// Function returning the Square Distance from a Point to a Line and Direction.
+ /// </summary>
+ /// <param name="a"></param>
+ /// <param name="b"></param>
+ /// <param name="point"></param>
+ /// <param name="direction">-1 left, 0 in between, 1 right</param>
+ /// <returns></returns>
+ //public static float DistanceToLineDirectional(Vector3 a, Vector3 b, Vector3 point, ref int direction)
+ //{
+ // Vector3 n = b - a;
+ // Vector3 pa = a - point;
+
+ // float c = Vector3.Dot(n, pa);
+ // direction = -1;
+
+ // // Closest point is a
+ // if (c > 0.0f)
+ // return Vector3.Dot(pa, pa);
+
+ // Vector3 bp = point - b;
+ // direction = 1;
+
+ // // Closest point is b
+ // if (Vector3.Dot(n, bp) > 0.0f)
+ // return Vector3.Dot(bp, bp);
+
+ // // Closest point is between a and b
+ // Vector3 e = pa - n * (c / Vector3.Dot(n, n));
+
+ // direction = 0;
+ // return Vector3.Dot(e, e);
+ //}
+
+
+ /// <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>
+ public static char ToLowerFast(char c)
+ {
+ if (c > k_lookupStringL.Length - 1)
+ return c;
+
+ return k_lookupStringL[c];
+ }
+
+ /// <summary>
+ /// Get uppercase version of this ASCII character.
+ /// </summary>
+ public static char ToUpperFast(char c)
+ {
+ if (c > k_lookupStringU.Length - 1)
+ return c;
+
+ return k_lookupStringU[c];
+ }
+
+ /// <summary>
+ /// Function which returns a simple hashcode from a string.
+ /// </summary>
+ /// <returns></returns>
+ public static int GetSimpleHashCode(string s)
+ {
+ int hashCode = 0; // 5381;
+
+ for (int i = 0; i < s.Length; i++)
+ hashCode = (hashCode << 5) + hashCode ^ s[i];
+
+ return hashCode;
+ }
+
+ /// <summary>
+ /// Function which returns a simple hashcode from a string converted to lowercase.
+ /// </summary>
+ /// <returns></returns>
+ public static uint GetSimpleHashCodeLowercase(string s)
+ {
+ uint hashCode = 5381;
+
+ for (int i = 0; i < s.Length; i++)
+ hashCode = (hashCode << 5) + hashCode ^ ToLowerFast(s[i]);
+
+ return hashCode;
+ }
+
+
+ /// <summary>
+ /// Function to convert Hex to Int
+ /// </summary>
+ /// <param name="hex"></param>
+ /// <returns></returns>
+ public static int HexToInt(char hex)
+ {
+ switch (hex)
+ {
+ case '0': return 0;
+ case '1': return 1;
+ case '2': return 2;
+ case '3': return 3;
+ case '4': return 4;
+ case '5': return 5;
+ case '6': return 6;
+ case '7': return 7;
+ case '8': return 8;
+ case '9': return 9;
+ case 'A': return 10;
+ case 'B': return 11;
+ case 'C': return 12;
+ case 'D': return 13;
+ case 'E': return 14;
+ case 'F': return 15;
+ case 'a': return 10;
+ case 'b': return 11;
+ case 'c': return 12;
+ case 'd': return 13;
+ case 'e': return 14;
+ case 'f': return 15;
+ }
+ return 15;
+ }
+
+
+ /// <summary>
+ /// Function to convert a properly formatted string which contains an hex value to its decimal value.
+ /// </summary>
+ /// <param name="s"></param>
+ /// <returns></returns>
+ public static int StringHexToInt(string s)
+ {
+ int value = 0;
+
+ for (int i = 0; i < s.Length; i++)
+ {
+ value += HexToInt(s[i]) * (int)Mathf.Pow(16, (s.Length - 1) - i);
+ }
+
+ return value;
+ }
+
+ }
+}
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_TextUtilities.cs.meta b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_TextUtilities.cs.meta
new file mode 100644
index 0000000..663b4ca
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_TextUtilities.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 4f0ca6874aa74540bb3d4fe5a0f86bcc
+timeCreated: 1432117579
+licenseType: Store
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_UpdateManager.cs b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_UpdateManager.cs
new file mode 100644
index 0000000..330e57b
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_UpdateManager.cs
@@ -0,0 +1,238 @@
+using UnityEngine;
+using UnityEngine.UI;
+using System.Collections.Generic;
+
+#if UNITY_2019_1_OR_NEWER
+using UnityEngine.Rendering;
+#elif UNITY_2018_1_OR_NEWER
+using UnityEngine.Experimental.Rendering;
+#endif
+
+
+namespace TMPro
+{
+
+ public class TMP_UpdateManager
+ {
+ private static TMP_UpdateManager s_Instance;
+
+ private readonly List<TMP_Text> m_LayoutRebuildQueue = new List<TMP_Text>();
+ private Dictionary<int, int> m_LayoutQueueLookup = new Dictionary<int, int>();
+
+ private readonly List<TMP_Text> m_GraphicRebuildQueue = new List<TMP_Text>();
+ private Dictionary<int, int> m_GraphicQueueLookup = new Dictionary<int, int>();
+
+ private readonly List<TMP_Text> m_InternalUpdateQueue = new List<TMP_Text>();
+ private Dictionary<int, int> m_InternalUpdateLookup = new Dictionary<int, int>();
+
+ //private bool m_PerformingGraphicRebuild;
+ //private bool m_PerformingLayoutRebuild;
+
+ /// <summary>
+ /// Get a singleton instance of the registry
+ /// </summary>
+ public static TMP_UpdateManager instance
+ {
+ get
+ {
+ if (TMP_UpdateManager.s_Instance == null)
+ TMP_UpdateManager.s_Instance = new TMP_UpdateManager();
+ return TMP_UpdateManager.s_Instance;
+ }
+ }
+
+
+ /// <summary>
+ /// Register to receive rendering callbacks.
+ /// </summary>
+ protected TMP_UpdateManager()
+ {
+ Camera.onPreCull += OnCameraPreCull;
+
+ #if UNITY_2019_1_OR_NEWER
+ RenderPipelineManager.beginFrameRendering += OnBeginFrameRendering;
+ #elif UNITY_2018_1_OR_NEWER
+ RenderPipeline.beginFrameRendering += OnBeginFrameRendering;
+ #endif
+ }
+
+
+ /// <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)
+ {
+ TMP_UpdateManager.instance.InternalRegisterTextObjectForUpdate(textObject);
+ }
+
+ private void InternalRegisterTextObjectForUpdate(TMP_Text textObject)
+ {
+ int id = textObject.GetInstanceID();
+
+ if (this.m_InternalUpdateLookup.ContainsKey(id))
+ return;
+
+ m_InternalUpdateLookup[id] = id;
+ this.m_InternalUpdateQueue.Add(textObject);
+
+ return;
+ }
+
+
+ /// <summary>
+ /// Function to register elements which require a layout rebuild.
+ /// </summary>
+ /// <param name="element"></param>
+ public static void RegisterTextElementForLayoutRebuild(TMP_Text element)
+ {
+ TMP_UpdateManager.instance.InternalRegisterTextElementForLayoutRebuild(element);
+ }
+
+ private bool InternalRegisterTextElementForLayoutRebuild(TMP_Text element)
+ {
+ int id = element.GetInstanceID();
+
+ if (this.m_LayoutQueueLookup.ContainsKey(id))
+ return false;
+
+ m_LayoutQueueLookup[id] = id;
+ this.m_LayoutRebuildQueue.Add(element);
+
+ return true;
+ }
+
+
+ /// <summary>
+ /// Function to register elements which require a layout rebuild.
+ /// </summary>
+ /// <param name="element"></param>
+ public static void RegisterTextElementForGraphicRebuild(TMP_Text element)
+ {
+ TMP_UpdateManager.instance.InternalRegisterTextElementForGraphicRebuild(element);
+ }
+
+ private bool InternalRegisterTextElementForGraphicRebuild(TMP_Text element)
+ {
+ int id = element.GetInstanceID();
+
+ if (this.m_GraphicQueueLookup.ContainsKey(id))
+ return false;
+
+ m_GraphicQueueLookup[id] = id;
+ this.m_GraphicRebuildQueue.Add(element);
+
+ return true;
+ }
+
+
+ /// <summary>
+ /// Callback which occurs just before the Scriptable Render Pipeline (SRP) begins rendering.
+ /// </summary>
+ /// <param name="cameras"></param>
+ #if UNITY_2019_1_OR_NEWER
+ void OnBeginFrameRendering(ScriptableRenderContext renderContext, Camera[] cameras)
+ #elif UNITY_2018_1_OR_NEWER
+ void OnBeginFrameRendering(Camera[] cameras)
+ #endif
+ {
+ // Exclude the PreRenderCamera
+ #if UNITY_EDITOR
+ if (cameras.Length == 1 && cameras[0].cameraType == CameraType.Preview)
+ return;
+ #endif
+ DoRebuilds();
+ }
+
+ /// <summary>
+ /// Callback which occurs just before the cam is rendered.
+ /// </summary>
+ /// <param name="cam"></param>
+ void OnCameraPreCull(Camera cam)
+ {
+ // Exclude the PreRenderCamera
+ #if UNITY_EDITOR
+ if (cam.cameraType == CameraType.Preview)
+ return;
+ #endif
+ 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();
+ }
+ }
+
+ internal static void UnRegisterTextObjectForUpdate(TMP_Text textObject)
+ {
+ TMP_UpdateManager.instance.InternalUnRegisterTextObjectForUpdate(textObject);
+ }
+
+ /// <summary>
+ /// Function to unregister elements which no longer require a rebuild.
+ /// </summary>
+ /// <param name="element"></param>
+ public static void UnRegisterTextElementForRebuild(TMP_Text element)
+ {
+ TMP_UpdateManager.instance.InternalUnRegisterTextElementForGraphicRebuild(element);
+ TMP_UpdateManager.instance.InternalUnRegisterTextElementForLayoutRebuild(element);
+ TMP_UpdateManager.instance.InternalUnRegisterTextObjectForUpdate(element);
+ }
+
+ private void InternalUnRegisterTextElementForGraphicRebuild(TMP_Text element)
+ {
+ int id = element.GetInstanceID();
+
+ TMP_UpdateManager.instance.m_GraphicRebuildQueue.Remove(element);
+ m_GraphicQueueLookup.Remove(id);
+ }
+
+ private void InternalUnRegisterTextElementForLayoutRebuild(TMP_Text element)
+ {
+ int id = element.GetInstanceID();
+
+ TMP_UpdateManager.instance.m_LayoutRebuildQueue.Remove(element);
+ m_LayoutQueueLookup.Remove(id);
+ }
+
+ private void InternalUnRegisterTextObjectForUpdate(TMP_Text textObject)
+ {
+ int id = textObject.GetInstanceID();
+
+ TMP_UpdateManager.instance.m_InternalUpdateQueue.Remove(textObject);
+ m_InternalUpdateLookup.Remove(id);
+ }
+ }
+} \ No newline at end of file
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_UpdateManager.cs.meta b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_UpdateManager.cs.meta
new file mode 100644
index 0000000..dcfd879
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_UpdateManager.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 691db8cb70c4426a8ae718465c21345f
+timeCreated: 1447406424
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_UpdateRegistery.cs b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_UpdateRegistery.cs
new file mode 100644
index 0000000..d507034
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_UpdateRegistery.cs
@@ -0,0 +1,178 @@
+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 Dictionary<int, int> m_LayoutQueueLookup = new Dictionary<int, int>();
+
+ private readonly List<ICanvasElement> m_GraphicRebuildQueue = new List<ICanvasElement>();
+ private Dictionary<int, int> m_GraphicQueueLookup = new Dictionary<int, 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 (this.m_LayoutQueueLookup.ContainsKey(id))
+ return false;
+
+ m_LayoutQueueLookup[id] = id;
+ this.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 (this.m_GraphicQueueLookup.ContainsKey(id))
+ return false;
+
+ m_GraphicQueueLookup[id] = id;
+ this.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);
+ }
+ }
+}
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_UpdateRegistery.cs.meta b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_UpdateRegistery.cs.meta
new file mode 100644
index 0000000..8b8993b
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMP_UpdateRegistery.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: a55fb7b4961a425381d1282fc424f966
+timeCreated: 1446775434
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMPro_EventManager.cs b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMPro_EventManager.cs
new file mode 100644
index 0000000..882aafb
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMPro_EventManager.cs
@@ -0,0 +1,150 @@
+using UnityEngine;
+using System.Collections.Generic;
+
+
+namespace TMPro
+{
+ public enum Compute_DistanceTransform_EventTypes { Processing, Completed };
+
+
+ public static class TMPro_EventManager
+ {
+ public static readonly FastAction<object, Compute_DT_EventArgs> COMPUTE_DT_EVENT = new FastAction<object, Compute_DT_EventArgs>();
+
+ // Event & Delegate used to notify TextMesh Pro objects that Material properties have been changed.
+ public static readonly FastAction<bool, Material> MATERIAL_PROPERTY_EVENT = new FastAction<bool, Material>();
+
+ public static readonly FastAction<bool, TMP_FontAsset> FONT_PROPERTY_EVENT = new FastAction<bool, TMP_FontAsset>();
+
+ public static readonly FastAction<bool, Object> SPRITE_ASSET_PROPERTY_EVENT = new FastAction<bool, Object>();
+
+ public static readonly FastAction<bool, TextMeshPro> TEXTMESHPRO_PROPERTY_EVENT = new FastAction<bool, TextMeshPro>();
+
+ public static readonly FastAction<GameObject, Material, Material> DRAG_AND_DROP_MATERIAL_EVENT = new FastAction<GameObject, Material, Material>();
+
+ public static readonly FastAction<bool> TEXT_STYLE_PROPERTY_EVENT = new FastAction<bool>();
+
+ public static readonly FastAction<TMP_ColorGradient> COLOR_GRADIENT_PROPERTY_EVENT = new FastAction<TMP_ColorGradient>();
+
+ public static readonly FastAction TMP_SETTINGS_PROPERTY_EVENT = new FastAction();
+
+ public static readonly FastAction RESOURCE_LOAD_EVENT = new FastAction();
+
+ public static readonly FastAction<bool, TextMeshProUGUI> TEXTMESHPRO_UGUI_PROPERTY_EVENT = new FastAction<bool, TextMeshProUGUI>();
+
+ public static readonly FastAction OnPreRenderObject_Event = new FastAction();
+
+ public static readonly FastAction<Object> TEXT_CHANGED_EVENT = new FastAction<Object>();
+
+ //public static readonly FastAction WILL_RENDER_CANVASES = new FastAction();
+
+
+
+ //static TMPro_EventManager()
+ //{
+ // // Register to the willRenderCanvases callback once
+ // // then the WILL_RENDER_CANVASES FastAction will handle the rest
+ // Canvas.willRenderCanvases += WILL_RENDER_CANVASES.Call;
+ //}
+
+ public static void ON_PRE_RENDER_OBJECT_CHANGED()
+ {
+ OnPreRenderObject_Event.Call();
+ }
+
+ public static void ON_MATERIAL_PROPERTY_CHANGED(bool isChanged, Material mat)
+ {
+ MATERIAL_PROPERTY_EVENT.Call(isChanged, mat);
+ }
+
+ public static void ON_FONT_PROPERTY_CHANGED(bool isChanged, TMP_FontAsset font)
+ {
+ FONT_PROPERTY_EVENT.Call(isChanged, font);
+ }
+
+ public static void ON_SPRITE_ASSET_PROPERTY_CHANGED(bool isChanged, Object obj)
+ {
+ SPRITE_ASSET_PROPERTY_EVENT.Call(isChanged, obj);
+ }
+
+ public static void ON_TEXTMESHPRO_PROPERTY_CHANGED(bool isChanged, TextMeshPro obj)
+ {
+ TEXTMESHPRO_PROPERTY_EVENT.Call(isChanged, obj);
+ }
+
+ public static void ON_DRAG_AND_DROP_MATERIAL_CHANGED(GameObject sender, Material currentMaterial, Material newMaterial)
+ {
+ DRAG_AND_DROP_MATERIAL_EVENT.Call(sender, currentMaterial, newMaterial);
+ }
+
+ public static void ON_TEXT_STYLE_PROPERTY_CHANGED(bool isChanged)
+ {
+ TEXT_STYLE_PROPERTY_EVENT.Call(isChanged);
+ }
+
+ public static void ON_COLOR_GRAIDENT_PROPERTY_CHANGED(TMP_ColorGradient gradient)
+ {
+ COLOR_GRADIENT_PROPERTY_EVENT.Call(gradient);
+ }
+
+
+ public static void ON_TEXT_CHANGED(Object obj)
+ {
+ TEXT_CHANGED_EVENT.Call(obj);
+ }
+
+ public static void ON_TMP_SETTINGS_CHANGED()
+ {
+ TMP_SETTINGS_PROPERTY_EVENT.Call();
+ }
+
+ public static void ON_RESOURCES_LOADED()
+ {
+ RESOURCE_LOAD_EVENT.Call();
+ }
+
+ public static void ON_TEXTMESHPRO_UGUI_PROPERTY_CHANGED(bool isChanged, TextMeshProUGUI obj)
+ {
+ TEXTMESHPRO_UGUI_PROPERTY_EVENT.Call(isChanged, obj);
+ }
+
+ //public static void ON_BASE_MATERIAL_CHANGED(Material mat)
+ //{
+ // BASE_MATERIAL_EVENT.Call(mat);
+ //}
+
+ //public static void ON_PROGRESSBAR_UPDATE(Progress_Bar_EventTypes event_type, Progress_Bar_EventArgs eventArgs)
+ //{
+ // if (PROGRESS_BAR_EVENT != null)
+ // PROGRESS_BAR_EVENT(event_type, eventArgs);
+ //}
+
+ public static void ON_COMPUTE_DT_EVENT(object Sender, Compute_DT_EventArgs e)
+ {
+ COMPUTE_DT_EVENT.Call(Sender, e);
+ }
+ }
+
+
+ public class Compute_DT_EventArgs
+ {
+ public Compute_DistanceTransform_EventTypes EventType;
+ public float ProgressPercentage;
+ public Color[] Colors;
+
+
+ public Compute_DT_EventArgs(Compute_DistanceTransform_EventTypes type, float progress)
+ {
+ EventType = type;
+ ProgressPercentage = progress;
+ }
+
+ public Compute_DT_EventArgs(Compute_DistanceTransform_EventTypes type, Color[] colors)
+ {
+ EventType = type;
+ Colors = colors;
+ }
+
+ }
+
+} \ No newline at end of file
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMPro_EventManager.cs.meta b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMPro_EventManager.cs.meta
new file mode 100644
index 0000000..9d8d8ac
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMPro_EventManager.cs.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: 96e9072453a441618754c478755b3028
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMPro_ExtensionMethods.cs b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMPro_ExtensionMethods.cs
new file mode 100644
index 0000000..72309f4
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMPro_ExtensionMethods.cs
@@ -0,0 +1,224 @@
+using UnityEngine;
+using System.Text;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace TMPro
+{
+ public static class TMPro_ExtensionMethods
+ {
+
+ public static string ArrayToString(this char[] chars)
+ {
+ string s = string.Empty;
+
+ for (int i = 0; i < chars.Length && chars[i] != 0; i++)
+ {
+ s += chars[i];
+ }
+
+ return s;
+ }
+
+ public static string IntToString(this int[] unicodes)
+ {
+ char[] chars = new char[unicodes.Length];
+
+ for (int i = 0; i < unicodes.Length; i++)
+ {
+ chars[i] = (char)unicodes[i];
+ }
+
+ return new string(chars);
+ }
+
+ public static string IntToString(this int[] unicodes, int start, int length)
+ {
+ if (start > unicodes.Length)
+ return string.Empty;
+
+ int end = Mathf.Min(start + length, unicodes.Length);
+
+ char[] chars = new char[end - start];
+
+ int writeIndex = 0;
+
+ for (int i = start; i < end; i++)
+ {
+ chars[writeIndex++] = (char)unicodes[i];
+ }
+
+ return new string(chars);
+ }
+
+
+ public static int FindInstanceID <T> (this List<T> list, T target) where T : Object
+ {
+ int targetID = target.GetInstanceID();
+
+ for (int i = 0; i < list.Count; i++)
+ {
+ if (list[i].GetInstanceID() == targetID)
+ return i;
+ }
+ return -1;
+ }
+
+
+ public static bool Compare(this Color32 a, Color32 b)
+ {
+ return a.r == b.r && a.g == b.g && a.b == b.b && a.a == b.a;
+ }
+
+ public static bool CompareRGB(this Color32 a, Color32 b)
+ {
+ return a.r == b.r && a.g == b.g && a.b == b.b;
+ }
+
+ public static bool Compare(this Color a, Color b)
+ {
+ return a.r == b.r && a.g == b.g && a.b == b.b && a.a == b.a;
+ }
+
+
+ public static bool CompareRGB(this Color a, Color b)
+ {
+ return a.r == b.r && a.g == b.g && a.b == b.b;
+ }
+
+
+ public static Color32 Multiply (this Color32 c1, Color32 c2)
+ {
+ byte r = (byte)((c1.r / 255f) * (c2.r / 255f) * 255);
+ byte g = (byte)((c1.g / 255f) * (c2.g / 255f) * 255);
+ byte b = (byte)((c1.b / 255f) * (c2.b / 255f) * 255);
+ byte a = (byte)((c1.a / 255f) * (c2.a / 255f) * 255);
+
+ return new Color32(r, g, b, a);
+ }
+
+
+ public static Color32 Tint (this Color32 c1, Color32 c2)
+ {
+ byte r = (byte)((c1.r / 255f) * (c2.r / 255f) * 255);
+ byte g = (byte)((c1.g / 255f) * (c2.g / 255f) * 255);
+ byte b = (byte)((c1.b / 255f) * (c2.b / 255f) * 255);
+ byte a = (byte)((c1.a / 255f) * (c2.a / 255f) * 255);
+
+ return new Color32(r, g, b, a);
+ }
+
+ public static Color32 Tint(this Color32 c1, float tint)
+ {
+ byte r = (byte)(Mathf.Clamp(c1.r / 255f * tint * 255, 0, 255));
+ byte g = (byte)(Mathf.Clamp(c1.g / 255f * tint * 255, 0, 255));
+ byte b = (byte)(Mathf.Clamp(c1.b / 255f * tint * 255, 0, 255));
+ byte a = (byte)(Mathf.Clamp(c1.a / 255f * tint * 255, 0, 255));
+
+ return new Color32(r, g, b, a);
+ }
+
+
+ public static bool Compare(this Vector3 v1, Vector3 v2, int accuracy)
+ {
+ bool x = (int)(v1.x * accuracy) == (int)(v2.x * accuracy);
+ bool y = (int)(v1.y * accuracy) == (int)(v2.y * accuracy);
+ bool z = (int)(v1.z * accuracy) == (int)(v2.z * accuracy);
+
+ return x && y && z;
+ }
+
+ public static bool Compare(this Quaternion q1, Quaternion q2, int accuracy)
+ {
+ bool x = (int)(q1.x * accuracy) == (int)(q2.x * accuracy);
+ bool y = (int)(q1.y * accuracy) == (int)(q2.y * accuracy);
+ bool z = (int)(q1.z * accuracy) == (int)(q2.z * accuracy);
+ bool w = (int)(q1.w * accuracy) == (int)(q2.w * accuracy);
+
+ return x && y && z && w;
+ }
+
+ //public static void AddElementAtIndex<T>(this T[] array, int writeIndex, T item)
+ //{
+ // if (writeIndex >= array.Length)
+ // System.Array.Resize(ref array, Mathf.NextPowerOfTwo(writeIndex + 1));
+
+ // array[writeIndex] = item;
+ //}
+
+ /// <summary>
+ /// Insert item into array at index.
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ /// <param name="array"></param>
+ /// <param name="index"></param>
+ /// <param name="item"></param>
+ //public static void Insert<T>(this T[] array, int index, T item)
+ //{
+ // if (index > array.Length - 1) return;
+
+ // T savedItem = item;
+
+ // for (int i = index; i < array.Length; i++)
+ // {
+ // savedItem = array[i];
+
+ // array[i] = item;
+
+ // item = savedItem;
+ // }
+ //}
+
+ /// <summary>
+ /// Insert item into array at index.
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ /// <param name="array"></param>
+ /// <param name="index"></param>
+ /// <param name="item"></param>
+ //public static void Insert<T>(this T[] array, int index, T[] items)
+ //{
+ // if (index > array.Length - 1) return;
+
+ // System.Array.Resize(ref array, array.Length + items.Length);
+
+ // int sourceIndex = 0;
+
+ // T savedItem = items[sourceIndex];
+
+ // for (int i = index; i < array.Length; i++)
+ // {
+ // savedItem = array[i];
+
+ // array[i] = items[sourceIndex];
+
+ // items[sourceIndex] = savedItem;
+
+ // if (sourceIndex < items.Length - 1)
+ // sourceIndex += 1;
+ // else
+ // sourceIndex = 0;
+ // }
+ //}
+
+ }
+
+ public static class TMP_Math
+ {
+ public const float FLOAT_MAX = 32767;
+ public const float FLOAT_MIN = -32767;
+ public const int INT_MAX = 2147483647;
+ public const int INT_MIN = -2147483647;
+
+ public const float FLOAT_UNSET = -32767;
+ public const int INT_UNSET = -32767;
+
+ public static Vector2 MAX_16BIT = new Vector2(FLOAT_MAX, FLOAT_MAX);
+ public static Vector2 MIN_16BIT = new Vector2(FLOAT_MIN, FLOAT_MIN);
+
+ public static bool Approximately(float a, float b)
+ {
+ return (b - 0.0001f) < a && a < (b + 0.0001f);
+ }
+ }
+}
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMPro_ExtensionMethods.cs.meta b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMPro_ExtensionMethods.cs.meta
new file mode 100644
index 0000000..c8ac197
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMPro_ExtensionMethods.cs.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: 77476292f9fa4905a787e6417853846b
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMPro_MeshUtilities.cs b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMPro_MeshUtilities.cs
new file mode 100644
index 0000000..988e200
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMPro_MeshUtilities.cs
@@ -0,0 +1,357 @@
+using UnityEngine;
+using UnityEngine.TextCore;
+using System;
+
+
+namespace TMPro
+{
+ /// <summary>
+ /// Flags to control what vertex data is pushed to the mesh and renderer.
+ /// </summary>
+ public enum TMP_VertexDataUpdateFlags
+ {
+ None = 0x0,
+ Vertices = 0x1,
+ Uv0 = 0x2,
+ Uv2 = 0x4,
+ Uv4 = 0x8,
+ Colors32 = 0x10,
+ All = 0xFF
+ };
+
+
+ /// <summary>
+ /// TMP custom data type to represent 32 bit characters.
+ /// </summary>
+ //public struct TMP_Char
+ //{
+ // private int m_value;
+
+ // private TMP_Char(int value)
+ // {
+ // this.m_value = value;
+ // }
+
+ // private TMP_Char(TMP_Char value)
+ // {
+ // this.m_value = (int)value;
+ // }
+
+ // public static implicit operator TMP_Char(int value)
+ // {
+ // return new TMP_Char(value);
+ // }
+
+ // public static implicit operator TMP_Char(char c)
+ // {
+ // return new TMP_Char(c);
+ // }
+
+ // public static explicit operator int(TMP_Char value)
+ // {
+ // return value.m_value;
+ // }
+
+ // public override string ToString()
+ // {
+ // return m_value.ToString();
+ // }
+ //}
+
+
+ //public struct TMP_VertexInfo
+ //{
+ // public TMP_Vertex topLeft;
+ // public TMP_Vertex bottomLeft;
+ // public TMP_Vertex topRight;
+ // public TMP_Vertex bottomRight;
+ //}
+
+
+ [Serializable]
+ public struct VertexGradient
+ {
+ public Color topLeft;
+ public Color topRight;
+ public Color bottomLeft;
+ public Color bottomRight;
+
+ public VertexGradient (Color color)
+ {
+ this.topLeft = color;
+ this.topRight = color;
+ this.bottomLeft = color;
+ this.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 VertexGradient(Color color0, Color color1, Color color2, Color color3)
+ {
+ this.topLeft = color0;
+ this.topRight = color1;
+ this.bottomLeft = color2;
+ this.bottomRight = color3;
+ }
+ }
+
+
+ public struct TMP_PageInfo
+ {
+ public int firstCharacterIndex;
+ public int lastCharacterIndex;
+ public float ascender;
+ public float baseLine;
+ public float descender;
+ // public float extents;
+ }
+
+
+ /// <summary>
+ /// Structure containing information about individual links contained in the text object.
+ /// </summary>
+ public struct TMP_LinkInfo
+ {
+ public TMP_Text textComponent;
+
+ public int hashCode;
+
+ public int linkIdFirstCharacterIndex;
+ public int linkIdLength;
+ public int linkTextfirstCharacterIndex;
+ public int linkTextLength;
+
+ internal char[] linkID;
+
+
+ internal void SetLinkID(char[] text, int startIndex, int length)
+ {
+ if (linkID == null || linkID.Length < length) linkID = new char[length];
+
+ for (int i = 0; i < length; i++)
+ linkID[i] = text[startIndex + i];
+ }
+
+ /// <summary>
+ /// Function which returns the text contained in a link.
+ /// </summary>
+ /// <param name="textInfo"></param>
+ /// <returns></returns>
+ public string GetLinkText()
+ {
+ string text = string.Empty;
+ TMP_TextInfo textInfo = textComponent.textInfo;
+
+ for (int i = linkTextfirstCharacterIndex; i < linkTextfirstCharacterIndex + linkTextLength; i++)
+ text += textInfo.characterInfo[i].character;
+
+ return text;
+ }
+
+
+ /// <summary>
+ /// Function which returns the link ID as a string.
+ /// </summary>
+ /// <param name="text">The source input text.</param>
+ /// <returns></returns>
+ public string GetLinkID()
+ {
+ if (textComponent == null)
+ return string.Empty;
+
+ return new string(linkID, 0, linkIdLength);
+ //return textComponent.text.Substring(linkIdFirstCharacterIndex, linkIdLength);
+
+ }
+ }
+
+
+ /// <summary>
+ /// Structure containing information about the individual words contained in the text object.
+ /// </summary>
+ public struct TMP_WordInfo
+ {
+ // NOTE: Structure could be simplified by only including the firstCharacterIndex and length.
+
+ public TMP_Text textComponent;
+
+ public int firstCharacterIndex;
+ public int lastCharacterIndex;
+ public int characterCount;
+ //public float length;
+
+ /// <summary>
+ /// Returns the word as a string.
+ /// </summary>
+ /// <returns></returns>
+ public string GetWord()
+ {
+ string word = string.Empty;
+ TMP_CharacterInfo[] charInfo = textComponent.textInfo.characterInfo;
+
+ for (int i = firstCharacterIndex; i < lastCharacterIndex + 1; i++)
+ {
+ word += charInfo[i].character;
+ }
+
+ return word;
+ }
+ }
+
+
+ public struct TMP_SpriteInfo
+ {
+ public int spriteIndex; // Index of the sprite in the sprite atlas.
+ public int characterIndex; // The characterInfo index which holds the key information about this sprite.
+ public int vertexIndex;
+ }
+
+
+ //public struct SpriteInfo
+ //{
+ //
+ //}
+
+
+ public struct Extents
+ {
+ public Vector2 min;
+ public Vector2 max;
+
+ public Extents(Vector2 min, Vector2 max)
+ {
+ this.min = min;
+ this.max = max;
+ }
+
+ public override string ToString()
+ {
+ string s = "Min (" + min.x.ToString("f2") + ", " + min.y.ToString("f2") + ") Max (" + max.x.ToString("f2") + ", " + max.y.ToString("f2") + ")";
+ return s;
+ }
+ }
+
+
+ [Serializable]
+ public struct Mesh_Extents
+ {
+ public Vector2 min;
+ public Vector2 max;
+
+
+ public Mesh_Extents(Vector2 min, Vector2 max)
+ {
+ this.min = min;
+ this.max = max;
+ }
+
+ public override string ToString()
+ {
+ string s = "Min (" + min.x.ToString("f2") + ", " + min.y.ToString("f2") + ") Max (" + max.x.ToString("f2") + ", " + max.y.ToString("f2") + ")";
+ //string s = "Center: (" + ")" + " Extents: (" + ((max.x - min.x) / 2).ToString("f2") + "," + ((max.y - min.y) / 2).ToString("f2") + ").";
+ return s;
+ }
+ }
+
+
+ // Structure used for Word Wrapping which tracks the state of execution when the last space or carriage return character was encountered.
+ public struct WordWrapState
+ {
+ public int previous_WordBreak;
+ public int total_CharacterCount;
+ public int visible_CharacterCount;
+ public int visible_SpriteCount;
+ public int visible_LinkCount;
+ public int firstCharacterIndex;
+ public int firstVisibleCharacterIndex;
+ public int lastCharacterIndex;
+ public int lastVisibleCharIndex;
+ public int lineNumber;
+
+ public float maxCapHeight;
+ public float maxAscender;
+ public float maxDescender;
+ public float maxLineAscender;
+ public float maxLineDescender;
+ public float previousLineAscender;
+
+ public float xAdvance;
+ public float preferredWidth;
+ public float preferredHeight;
+ //public float maxFontScale;
+ public float previousLineScale;
+
+ public int wordCount;
+ public FontStyles fontStyle;
+ public float fontScale;
+ public float fontScaleMultiplier;
+
+ public float currentFontSize;
+ public float baselineOffset;
+ public float lineOffset;
+
+ public TMP_TextInfo textInfo;
+ //public TMPro_CharacterInfo[] characterInfo;
+ public TMP_LineInfo lineInfo;
+
+ public Color32 vertexColor;
+ public Color32 underlineColor;
+ public Color32 strikethroughColor;
+ public Color32 highlightColor;
+ public TMP_FontStyleStack basicStyleStack;
+ public TMP_RichTextTagStack<Color32> colorStack;
+ public TMP_RichTextTagStack<Color32> underlineColorStack;
+ public TMP_RichTextTagStack<Color32> strikethroughColorStack;
+ public TMP_RichTextTagStack<Color32> highlightColorStack;
+ public TMP_RichTextTagStack<TMP_ColorGradient> colorGradientStack;
+ public TMP_RichTextTagStack<float> sizeStack;
+ public TMP_RichTextTagStack<float> indentStack;
+ public TMP_RichTextTagStack<FontWeight> fontWeightStack;
+ public TMP_RichTextTagStack<int> styleStack;
+ public TMP_RichTextTagStack<float> baselineStack;
+ public TMP_RichTextTagStack<int> actionStack;
+ public TMP_RichTextTagStack<MaterialReference> materialReferenceStack;
+ public TMP_RichTextTagStack<TextAlignmentOptions> lineJustificationStack;
+ //public TMP_XmlTagStack<int> spriteAnimationStack;
+ public int spriteAnimationID;
+
+ public TMP_FontAsset currentFontAsset;
+ public TMP_SpriteAsset currentSpriteAsset;
+ public Material currentMaterial;
+ public int currentMaterialIndex;
+
+ public Extents meshExtents;
+
+ public bool tagNoParsing;
+ public bool isNonBreakingSpace;
+ //public Mesh_Extents lineExtents;
+ }
+
+
+ /// <summary>
+ /// Structure used to store retrieve the name and hashcode of the font and material
+ /// </summary>
+ public struct TagAttribute
+ {
+ public int startIndex;
+ public int length;
+ public int hashCode;
+ }
+
+
+ public struct RichTextTagAttribute
+ {
+ public int nameHashCode;
+ public int valueHashCode;
+ public TagValueType valueType;
+ public int valueStartIndex;
+ public int valueLength;
+ public TagUnitType unitType;
+ }
+
+}
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMPro_MeshUtilities.cs.meta b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMPro_MeshUtilities.cs.meta
new file mode 100644
index 0000000..9c6456f
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMPro_MeshUtilities.cs.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: effb76e1937b45ff8adf45e51a4c08cf
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMPro_Private.cs b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMPro_Private.cs
new file mode 100644
index 0000000..90bca2b
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMPro_Private.cs
@@ -0,0 +1,4051 @@
+//#define TMP_PROFILE_ON
+//#define TMP_PROFILE_PHASES_ON
+
+using UnityEngine;
+using UnityEngine.TextCore;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+
+#pragma warning disable 0414 // Disabled a few warnings related to serialized variables not used in this script but used in the editor.
+
+namespace TMPro
+{
+
+ public partial class TextMeshPro
+ {
+ [SerializeField]
+ private bool m_hasFontAssetChanged = false; // Used to track when font properties have changed.
+
+ float m_previousLossyScaleY = -1; // Used for Tracking lossy scale changes in the transform;
+
+ [SerializeField]
+ private Renderer m_renderer;
+ private MeshFilter m_meshFilter;
+
+ private bool m_isFirstAllocation; // Flag to determine if this is the first allocation of the buffers.
+ private int m_max_characters = 8; // Determines the initial allocation and size of the character array / buffer.
+ private int m_max_numberOfLines = 4; // Determines the initial allocation and maximum number of lines of text.
+
+ [SerializeField]
+ protected TMP_SubMesh[] m_subTextObjects = new TMP_SubMesh[8];
+
+
+ // MASKING RELATED PROPERTIES
+
+ //MaterialPropertyBlock m_maskingPropertyBlock;
+ //[SerializeField]
+ private bool m_isMaskingEnabled;
+ private bool isMaskUpdateRequired;
+ //private bool m_isMaterialBlockSet;
+
+ [SerializeField]
+ private MaskingTypes m_maskType;
+
+ // Matrix used to animated Env Map
+ private Matrix4x4 m_EnvMapMatrix = new Matrix4x4();
+
+
+ // Text Container / RectTransform Component
+ private Vector3[] m_RectTransformCorners = new Vector3[4];
+
+ [NonSerialized]
+ private bool m_isRegisteredForEvents;
+
+
+ // DEBUG Variables
+ //private System.Diagnostics.Stopwatch m_StopWatch;
+ //private bool isDebugOutputDone;
+ //private int m_recursiveCount = 0;
+ private int loopCountA;
+ //private int loopCountB;
+ //private int loopCountC;
+ //private int loopCountD;
+ //private int loopCountE;
+
+
+ protected override void Awake()
+ {
+ //Debug.Log("***** Awake() called on object ID " + GetInstanceID() + ". *****");
+
+ #if UNITY_EDITOR
+ // Special handling for TMP Settings and importing Essential Resources
+ if (TMP_Settings.instance == null)
+ {
+ if (m_isWaitingOnResourceLoad == false)
+ TMPro_EventManager.RESOURCE_LOAD_EVENT.Add(ON_RESOURCES_LOADED);
+
+ m_isWaitingOnResourceLoad = true;
+ return;
+ }
+ #endif
+
+ // Cache Reference to the Mesh Renderer.
+ m_renderer = GetComponent<Renderer>();
+ if (m_renderer == null)
+ m_renderer = gameObject.AddComponent<Renderer>();
+
+ // Make sure we have a CanvasRenderer for compatibility reasons and hide it
+ if (this.canvasRenderer != null)
+ this.canvasRenderer.hideFlags = HideFlags.HideInInspector;
+ else
+ {
+ CanvasRenderer canvasRenderer = gameObject.AddComponent<CanvasRenderer>();
+ canvasRenderer.hideFlags = HideFlags.HideInInspector;
+ }
+
+ // Cache Reference to RectTransform
+ m_rectTransform = this.rectTransform;
+
+ // Cache Reference to the transform;
+ m_transform = this.transform;
+
+ // Cache a reference to the Mesh Filter.
+ m_meshFilter = GetComponent<MeshFilter>();
+ if (m_meshFilter == null)
+ m_meshFilter = gameObject.AddComponent<MeshFilter>();
+
+ // Create new Mesh if necessary and cache reference to it.
+ if (m_mesh == null)
+ {
+ m_mesh = new Mesh();
+ m_mesh.hideFlags = HideFlags.HideAndDontSave;
+
+ m_meshFilter.mesh = m_mesh;
+
+ // Create new TextInfo for the text object.
+ m_textInfo = new TMP_TextInfo(this);
+ }
+ m_meshFilter.hideFlags = HideFlags.HideInInspector;
+
+ // Load TMP Settings for new text object instances.
+ LoadDefaultSettings();
+
+ // Load the font asset and assign material to renderer.
+ LoadFontAsset();
+
+ // Load Default TMP StyleSheet
+ TMP_StyleSheet.LoadDefaultStyleSheet();
+
+ // Allocate our initial buffers.
+ if (m_TextParsingBuffer == null)
+ m_TextParsingBuffer = new UnicodeChar[m_max_characters];
+
+ m_cached_TextElement = new TMP_Character();
+ m_isFirstAllocation = true;
+
+ // Check if we have a font asset assigned. Return if we don't because no one likes to see purple squares on screen.
+ if (m_fontAsset == null)
+ {
+ Debug.LogWarning("Please assign a Font Asset to this " + transform.name + " gameobject.", this);
+ return;
+ }
+
+ // Check to make sure Sub Text Objects are tracked correctly in the event a Prefab is used.
+ TMP_SubMesh[] subTextObjects = GetComponentsInChildren<TMP_SubMesh>();
+ if (subTextObjects.Length > 0)
+ {
+ for (int i = 0; i < subTextObjects.Length; i++)
+ m_subTextObjects[i + 1] = subTextObjects[i];
+ }
+
+ // Set flags to ensure our text is parsed and redrawn.
+ m_isInputParsingRequired = true;
+ m_havePropertiesChanged = true;
+ m_isCalculateSizeRequired = true;
+
+ m_isAwake = true;
+ }
+
+
+ protected override void OnEnable()
+ {
+ //Debug.Log("***** OnEnable() called on object ID " + GetInstanceID() + ". *****");
+
+ // Return if Awake() has not been called on the text object.
+ if (m_isAwake == false)
+ return;
+
+ // 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.COLOR_GRADIENT_PROPERTY_EVENT.Add(ON_COLOR_GRADIENT_CHANGED);
+ TMPro_EventManager.TMP_SETTINGS_PROPERTY_EVENT.Add(ON_TMP_SETTINGS_CHANGED);
+ #endif
+ m_isRegisteredForEvents = true;
+ }
+
+ TMP_UpdateManager.RegisterTextObjectForUpdate(this);
+
+ meshFilter.sharedMesh = mesh;
+ SetActiveSubMeshes(true);
+
+ // Schedule potential text object update (if any of the properties have changed.
+ ComputeMarginSize();
+
+ m_isInputParsingRequired = true;
+ m_havePropertiesChanged = true;
+ m_verticesAlreadyDirty = false;
+ SetVerticesDirty();
+ }
+
+
+ protected override void OnDisable()
+ {
+ //Debug.Log("***** OnDisable() called on object ID " + GetInstanceID() + ". *****");
+
+ // Return if Awake() has not been called on the text object.
+ if (m_isAwake == false)
+ return;
+
+ TMP_UpdateManager.UnRegisterTextElementForRebuild(this);
+ TMP_UpdateManager.UnRegisterTextObjectForUpdate(this);
+
+ m_meshFilter.sharedMesh = null;
+ SetActiveSubMeshes(false);
+ }
+
+
+ protected override void OnDestroy()
+ {
+ //Debug.Log("***** OnDestroy() called on object ID " + GetInstanceID() + ". *****");
+
+ // Destroy the mesh if we have one.
+ if (m_mesh != null)
+ {
+ DestroyImmediate(m_mesh);
+ }
+
+ // Unregister the event this object was listening to
+ #if UNITY_EDITOR
+ 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.COLOR_GRADIENT_PROPERTY_EVENT.Remove(ON_COLOR_GRADIENT_CHANGED);
+ TMPro_EventManager.TMP_SETTINGS_PROPERTY_EVENT.Remove(ON_TMP_SETTINGS_CHANGED);
+ TMPro_EventManager.RESOURCE_LOAD_EVENT.Remove(ON_RESOURCES_LOADED);
+ #endif
+
+ m_isRegisteredForEvents = false;
+ TMP_UpdateManager.UnRegisterTextElementForRebuild(this);
+ TMP_UpdateManager.UnRegisterTextObjectForUpdate(this);
+ }
+
+
+ #if UNITY_EDITOR
+ protected override void Reset()
+ {
+ //Debug.Log("Reset() has been called." + m_subTextObjects);
+
+ // Return if Awake() has not been called on the text object.
+ if (m_isAwake == false)
+ return;
+
+ if (m_mesh != null)
+ DestroyImmediate(m_mesh);
+
+ Awake();
+ }
+
+
+ protected override void OnValidate()
+ {
+ //Debug.Log("*** TextMeshPro OnValidate() has been called on Object ID:" + gameObject.GetInstanceID());
+
+ // Return if Awake() has not been called on the text object.
+ if (m_isAwake == false)
+ return;
+
+ // Additional Properties could be added to sync up Serialized Properties & Properties.
+
+ // Handle Font Asset changes in the inspector
+ if (m_fontAsset == null || m_hasFontAssetChanged)
+ {
+ LoadFontAsset();
+ m_isCalculateSizeRequired = true;
+ m_hasFontAssetChanged = false;
+ }
+
+ m_padding = GetPaddingForMaterial();
+ ComputeMarginSize();
+
+ m_isInputParsingRequired = true;
+ m_inputSource = TextInputSources.Text;
+ m_havePropertiesChanged = true;
+ m_isCalculateSizeRequired = true;
+ m_isPreferredWidthDirty = true;
+ m_isPreferredHeightDirty = true;
+
+ SetAllDirty();
+ }
+
+
+ // Event received when TMP resources have been loaded.
+ void ON_RESOURCES_LOADED()
+ {
+ TMPro_EventManager.RESOURCE_LOAD_EVENT.Remove(ON_RESOURCES_LOADED);
+
+ if (this == null)
+ return;
+
+ Awake();
+ OnEnable();
+ }
+
+
+ // Event received when custom material editor properties are changed.
+ void ON_MATERIAL_PROPERTY_CHANGED(bool isChanged, Material mat)
+ {
+ //Debug.Log("ON_MATERIAL_PROPERTY_CHANGED event received. Targeted Material is: " + mat.name + " m_sharedMaterial: " + m_sharedMaterial.name + " m_renderer.sharedMaterial: " + m_renderer.sharedMaterial);
+
+ if (m_renderer.sharedMaterial == null)
+ {
+ if (m_fontAsset != null)
+ {
+ m_renderer.sharedMaterial = m_fontAsset.material;
+ Debug.LogWarning("No Material was assigned to " + name + ". " + m_fontAsset.material.name + " was assigned.", this);
+ }
+ else
+ Debug.LogWarning("No Font Asset assigned to " + name + ". Please assign a Font Asset.", this);
+ }
+
+ if (m_fontAsset.atlasTexture != null && m_fontAsset.atlasTexture.GetInstanceID() != m_renderer.sharedMaterial.GetTexture(ShaderUtilities.ID_MainTex).GetInstanceID())
+ {
+ m_renderer.sharedMaterial = m_sharedMaterial;
+ //m_renderer.sharedMaterial = m_fontAsset.material;
+ Debug.LogWarning("Font Asset Atlas doesn't match the Atlas in the newly assigned material. Select a matching material or a different font asset.", this);
+ }
+
+ if (m_renderer.sharedMaterial != m_sharedMaterial) // || m_renderer.sharedMaterials.Contains(mat))
+ {
+ //Debug.Log("ON_MATERIAL_PROPERTY_CHANGED Called on Target ID: " + GetInstanceID() + ". Previous Material:" + m_sharedMaterial + " New Material:" + m_renderer.sharedMaterial); // on Object ID:" + GetInstanceID() + ". m_sharedMaterial: " + m_sharedMaterial.name + " m_renderer.sharedMaterial: " + m_renderer.sharedMaterial.name);
+ m_sharedMaterial = m_renderer.sharedMaterial;
+ }
+
+ m_padding = GetPaddingForMaterial();
+ //m_sharedMaterialHashCode = TMP_TextUtilities.GetSimpleHashCode(m_sharedMaterial.name);
+
+ UpdateMask();
+ UpdateEnvMapMatrix();
+ m_havePropertiesChanged = true;
+ SetVerticesDirty();
+ }
+
+
+ // Event received when font asset properties are changed in Font Inspector
+ void ON_FONT_PROPERTY_CHANGED(bool isChanged, TMP_FontAsset font)
+ {
+ if (MaterialReference.Contains(m_materialReferences, font))
+ {
+ //Debug.Log("ON_FONT_PROPERTY_CHANGED event received.");
+ m_isInputParsingRequired = true;
+ m_havePropertiesChanged = true;
+
+ UpdateMeshPadding();
+
+ SetMaterialDirty();
+ SetVerticesDirty();
+ }
+ }
+
+
+ // Event received when UNDO / REDO Event alters the properties of the object.
+ void ON_TEXTMESHPRO_PROPERTY_CHANGED(bool isChanged, TextMeshPro obj)
+ {
+ if (obj == this)
+ {
+ //Debug.Log("Undo / Redo Event Received by Object ID:" + GetInstanceID());
+ m_havePropertiesChanged = true;
+ m_isInputParsingRequired = true;
+
+ m_padding = GetPaddingForMaterial();
+ ComputeMarginSize(); // Verify this change
+ SetVerticesDirty();
+ }
+ }
+
+
+ // Event to Track Material Changed resulting from Drag-n-drop.
+ void ON_DRAG_AND_DROP_MATERIAL(GameObject obj, Material currentMaterial, Material newMaterial)
+ {
+ //Debug.Log("Drag-n-Drop Event - Receiving Object ID " + GetInstanceID()); // + ". Target Object ID " + obj.GetInstanceID() + ". New Material is " + mat.name + " with ID " + mat.GetInstanceID() + ". Base Material is " + m_baseMaterial.name + " with ID " + m_baseMaterial.GetInstanceID());
+
+ // 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
+ {
+ UnityEditor.Undo.RecordObject(this, "Material Assignment");
+ UnityEditor.Undo.RecordObject(m_renderer, "Material Assignment");
+
+ m_sharedMaterial = newMaterial;
+
+ m_padding = GetPaddingForMaterial();
+ m_havePropertiesChanged = true;
+ SetVerticesDirty();
+ SetMaterialDirty();
+ }
+ }
+
+
+ // Event received when Text Styles are changed.
+ void ON_TEXT_STYLE_CHANGED(bool isChanged)
+ {
+ m_havePropertiesChanged = true;
+ m_isInputParsingRequired = true;
+ SetVerticesDirty();
+ }
+
+
+ /// <summary>
+ /// Event received when a Color Gradient Preset is modified.
+ /// </summary>
+ /// <param name="textObject"></param>
+ void ON_COLOR_GRADIENT_CHANGED(TMP_ColorGradient gradient)
+ {
+ if (m_fontColorGradientPreset != null && gradient.GetInstanceID() == m_fontColorGradientPreset.GetInstanceID())
+ {
+ m_havePropertiesChanged = true;
+ SetVerticesDirty();
+ }
+ }
+
+
+ /// <summary>
+ /// Event received when the TMP Settings are changed.
+ /// </summary>
+ void ON_TMP_SETTINGS_CHANGED()
+ {
+ m_defaultSpriteAsset = null;
+ m_havePropertiesChanged = true;
+ m_isInputParsingRequired = true;
+ SetAllDirty();
+ }
+#endif
+
+
+ // Function which loads either the default font or a newly assigned font asset. This function also assigned the appropriate material to the renderer.
+ protected override void LoadFontAsset()
+ {
+ //Debug.Log("TextMeshPro LoadFontAsset() has been called."); // Current Font Asset is " + (font != null ? font.name: "Null") );
+
+ ShaderUtilities.GetShaderPropertyIDs(); // Initialize & Get shader property IDs.
+
+ if (m_fontAsset == null)
+ {
+ if (TMP_Settings.defaultFontAsset != null)
+ m_fontAsset =TMP_Settings.defaultFontAsset;
+ else
+ m_fontAsset = Resources.Load<TMP_FontAsset>("Fonts & Materials/LiberationSans SDF");
+
+ if (m_fontAsset == null)
+ {
+ Debug.LogWarning("The LiberationSans SDF Font Asset was not found. There is no Font Asset assigned to " + gameObject.name + ".", this);
+ return;
+ }
+
+ if (m_fontAsset.characterLookupTable == null)
+ {
+ Debug.Log("Dictionary is Null!");
+ }
+
+ m_renderer.sharedMaterial = m_fontAsset.material;
+ m_sharedMaterial = m_fontAsset.material;
+ m_sharedMaterial.SetFloat("_CullMode", 0);
+ m_sharedMaterial.SetFloat(ShaderUtilities.ShaderTag_ZTestMode, 4);
+ m_renderer.receiveShadows = false;
+ m_renderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off; // true;
+ // Get a Reference to the Shader
+ }
+ else
+ {
+ if (m_fontAsset.characterLookupTable == null)
+ {
+ //Debug.Log("Reading Font Definition and Creating Character Dictionary.");
+ m_fontAsset.ReadFontAssetDefinition();
+ }
+
+ //Debug.Log("Font Asset name:" + font.material.name);
+
+ // If font atlas texture doesn't match the assigned material font atlas, switch back to default material specified in the Font Asset.
+ if (m_renderer.sharedMaterial == null || m_renderer.sharedMaterial.GetTexture(ShaderUtilities.ID_MainTex) == null || m_fontAsset.atlasTexture.GetInstanceID() != m_renderer.sharedMaterial.GetTexture(ShaderUtilities.ID_MainTex).GetInstanceID())
+ {
+ m_renderer.sharedMaterial = m_fontAsset.material;
+ m_sharedMaterial = m_fontAsset.material;
+ }
+ else
+ {
+ m_sharedMaterial = m_renderer.sharedMaterial;
+ }
+
+ //m_sharedMaterial.SetFloat("_CullMode", 0);
+ m_sharedMaterial.SetFloat(ShaderUtilities.ShaderTag_ZTestMode, 4);
+
+ // Check if we are using the SDF Surface Shader
+ if (m_sharedMaterial.passCount == 1)
+ {
+ m_renderer.receiveShadows = false;
+ m_renderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
+ }
+
+ }
+
+ m_padding = GetPaddingForMaterial();
+ //m_alignmentPadding = ShaderUtilities.GetFontExtent(m_sharedMaterial);
+ m_isMaskingEnabled = ShaderUtilities.IsMaskingEnabled(m_sharedMaterial);
+
+
+ // Find and cache Underline & Ellipsis characters.
+ GetSpecialCharacters(m_fontAsset);
+
+
+ //m_sharedMaterials.Add(m_sharedMaterial);
+ //m_sharedMaterialHashCode = TMP_TextUtilities.GetSimpleHashCode(m_sharedMaterial.name);
+ // Hide Material Editor Component
+ //m_renderer.sharedMaterial.hideFlags = HideFlags.None;
+ }
+
+
+ void UpdateEnvMapMatrix()
+ {
+ if (!m_sharedMaterial.HasProperty(ShaderUtilities.ID_EnvMap) || m_sharedMaterial.GetTexture(ShaderUtilities.ID_EnvMap) == null)
+ return;
+
+ //Debug.Log("Updating Env Matrix...");
+ Vector3 rotation = m_sharedMaterial.GetVector(ShaderUtilities.ID_EnvMatrixRotation);
+ m_EnvMapMatrix = Matrix4x4.TRS(Vector3.zero, Quaternion.Euler(rotation), Vector3.one);
+
+ m_sharedMaterial.SetMatrix(ShaderUtilities.ID_EnvMatrix, m_EnvMapMatrix);
+ }
+
+
+ //
+ void SetMask(MaskingTypes maskType)
+ {
+ switch(maskType)
+ {
+ case MaskingTypes.MaskOff:
+ m_sharedMaterial.DisableKeyword(ShaderUtilities.Keyword_MASK_SOFT);
+ m_sharedMaterial.DisableKeyword(ShaderUtilities.Keyword_MASK_HARD);
+ m_sharedMaterial.DisableKeyword(ShaderUtilities.Keyword_MASK_TEX);
+ break;
+ case MaskingTypes.MaskSoft:
+ m_sharedMaterial.EnableKeyword(ShaderUtilities.Keyword_MASK_SOFT);
+ m_sharedMaterial.DisableKeyword(ShaderUtilities.Keyword_MASK_HARD);
+ m_sharedMaterial.DisableKeyword(ShaderUtilities.Keyword_MASK_TEX);
+ break;
+ case MaskingTypes.MaskHard:
+ m_sharedMaterial.EnableKeyword(ShaderUtilities.Keyword_MASK_HARD);
+ m_sharedMaterial.DisableKeyword(ShaderUtilities.Keyword_MASK_SOFT);
+ m_sharedMaterial.DisableKeyword(ShaderUtilities.Keyword_MASK_TEX);
+ break;
+ //case MaskingTypes.MaskTex:
+ // m_sharedMaterial.EnableKeyword(ShaderUtilities.Keyword_MASK_TEX);
+ // m_sharedMaterial.DisableKeyword(ShaderUtilities.Keyword_MASK_HARD);
+ // m_sharedMaterial.DisableKeyword(ShaderUtilities.Keyword_MASK_SOFT);
+ // break;
+ }
+ }
+
+
+ // Method used to set the masking coordinates
+ void SetMaskCoordinates(Vector4 coords)
+ {
+ m_sharedMaterial.SetVector(ShaderUtilities.ID_ClipRect, coords);
+ }
+
+ // Method used to set the masking coordinates
+ void SetMaskCoordinates(Vector4 coords, float softX, float softY)
+ {
+ m_sharedMaterial.SetVector(ShaderUtilities.ID_ClipRect, coords);
+ m_sharedMaterial.SetFloat(ShaderUtilities.ID_MaskSoftnessX, softX);
+ m_sharedMaterial.SetFloat(ShaderUtilities.ID_MaskSoftnessY, softY);
+ }
+
+
+
+ // Enable Masking in the Shader
+ void EnableMasking()
+ {
+ if (m_sharedMaterial.HasProperty(ShaderUtilities.ID_ClipRect))
+ {
+ m_sharedMaterial.EnableKeyword(ShaderUtilities.Keyword_MASK_SOFT);
+ m_sharedMaterial.DisableKeyword(ShaderUtilities.Keyword_MASK_HARD);
+ m_sharedMaterial.DisableKeyword(ShaderUtilities.Keyword_MASK_TEX);
+
+ m_isMaskingEnabled = true;
+ UpdateMask();
+ }
+ }
+
+
+ // Enable Masking in the Shader
+ void DisableMasking()
+ {
+ if (m_sharedMaterial.HasProperty(ShaderUtilities.ID_ClipRect))
+ {
+ m_sharedMaterial.DisableKeyword(ShaderUtilities.Keyword_MASK_SOFT);
+ m_sharedMaterial.DisableKeyword(ShaderUtilities.Keyword_MASK_HARD);
+ m_sharedMaterial.DisableKeyword(ShaderUtilities.Keyword_MASK_TEX);
+
+ m_isMaskingEnabled = false;
+ UpdateMask();
+ }
+ }
+
+
+ void UpdateMask()
+ {
+ //Debug.Log("UpdateMask() called.");
+
+ if (!m_isMaskingEnabled)
+ {
+ // Release Masking Material
+
+ // Re-assign Base Material
+
+ return;
+ }
+
+ if (m_isMaskingEnabled && m_fontMaterial == null)
+ {
+ CreateMaterialInstance();
+ }
+
+
+ /*
+ if (!m_isMaskingEnabled)
+ {
+ //Debug.Log("Masking is not enabled.");
+ if (m_maskingPropertyBlock != null)
+ {
+ m_renderer.SetPropertyBlock(null);
+ //havePropertiesChanged = true;
+ }
+ return;
+ }
+ //else
+ // Debug.Log("Updating Masking...");
+ */
+
+ // Compute Masking Coordinates & Softness
+ //float softnessX = Mathf.Min(Mathf.Min(m_textContainer.margins.x, m_textContainer.margins.z), m_sharedMaterial.GetFloat(ShaderUtilities.ID_MaskSoftnessX));
+ //float softnessY = Mathf.Min(Mathf.Min(m_textContainer.margins.y, m_textContainer.margins.w), m_sharedMaterial.GetFloat(ShaderUtilities.ID_MaskSoftnessY));
+
+ //softnessX = softnessX > 0 ? softnessX : 0;
+ //softnessY = softnessY > 0 ? softnessY : 0;
+
+ //float width = (m_textContainer.width - Mathf.Max(m_textContainer.margins.x, 0) - Mathf.Max(m_textContainer.margins.z, 0)) / 2 + softnessX;
+ //float height = (m_textContainer.height - Mathf.Max(m_textContainer.margins.y, 0) - Mathf.Max(m_textContainer.margins.w, 0)) / 2 + softnessY;
+
+ //Vector2 center = new Vector2((0.5f - m_textContainer.pivot.x) * m_textContainer.width + (Mathf.Max(m_textContainer.margins.x, 0) - Mathf.Max(m_textContainer.margins.z, 0)) / 2, (0.5f - m_textContainer.pivot.y) * m_textContainer.height + (- Mathf.Max(m_textContainer.margins.y, 0) + Mathf.Max(m_textContainer.margins.w, 0)) / 2);
+ //Vector4 mask = new Vector4(center.x, center.y, width, height);
+
+
+ //m_fontMaterial.SetVector(ShaderUtilities.ID_ClipRect, mask);
+ //m_fontMaterial.SetFloat(ShaderUtilities.ID_MaskSoftnessX, softnessX);
+ //m_fontMaterial.SetFloat(ShaderUtilities.ID_MaskSoftnessY, softnessY);
+
+ /*
+ if(m_maskingPropertyBlock == null)
+ {
+ m_maskingPropertyBlock = new MaterialPropertyBlock();
+
+ //m_maskingPropertyBlock.AddFloat(ShaderUtilities.ID_VertexOffsetX, m_sharedMaterial.GetFloat(ShaderUtilities.ID_VertexOffsetX));
+ //m_maskingPropertyBlock.AddFloat(ShaderUtilities.ID_VertexOffsetY, m_sharedMaterial.GetFloat(ShaderUtilities.ID_VertexOffsetY));
+ //Debug.Log("Creating new MaterialPropertyBlock.");
+ }
+
+ //Debug.Log("Updating Material Property Block.");
+ //m_maskingPropertyBlock.Clear();
+ m_maskingPropertyBlock.AddFloat(ShaderUtilities.ID_MaskID, m_renderer.GetInstanceID());
+ m_maskingPropertyBlock.AddVector(ShaderUtilities.ID_MaskCoord, mask);
+ m_maskingPropertyBlock.AddFloat(ShaderUtilities.ID_MaskSoftnessX, softnessX);
+ m_maskingPropertyBlock.AddFloat(ShaderUtilities.ID_MaskSoftnessY, softnessY);
+
+ m_renderer.SetPropertyBlock(m_maskingPropertyBlock);
+ */
+ }
+
+
+ // Function called internally when a new material is assigned via the fontMaterial property.
+ protected override 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_fontMaterial == null || m_fontMaterial.GetInstanceID() != mat.GetInstanceID())
+ m_fontMaterial = CreateMaterialInstance(mat);
+
+ m_sharedMaterial = m_fontMaterial;
+
+ m_padding = GetPaddingForMaterial();
+
+ SetVerticesDirty();
+ SetMaterialDirty();
+
+ return m_sharedMaterial;
+ }
+
+
+ /// <summary>
+ /// Method returning instances of the materials used by the text object.
+ /// </summary>
+ /// <returns></returns>
+ protected override Material[] GetMaterials(Material[] mats)
+ {
+ int materialCount = m_textInfo.materialCount;
+
+ if (m_fontMaterials == null)
+ m_fontMaterials = new Material[materialCount];
+ else if (m_fontMaterials.Length != materialCount)
+ TMP_TextInfo.Resize(ref m_fontMaterials, materialCount, false);
+
+ // Get instances of the materials
+ for (int i = 0; i < materialCount; i++)
+ {
+ if (i == 0)
+ m_fontMaterials[i] = fontMaterial;
+ else
+ m_fontMaterials[i] = m_subTextObjects[i].material;
+ }
+
+ m_fontSharedMaterials = m_fontMaterials;
+
+ return m_fontMaterials;
+ }
+
+
+ // Function called internally when a new shared material is assigned via the fontSharedMaterial property.
+ protected override void SetSharedMaterial(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>();
+
+ m_sharedMaterial = mat;
+
+ m_padding = GetPaddingForMaterial();
+
+ SetMaterialDirty();
+ }
+
+
+ /// <summary>
+ /// Method returning an array containing the materials used by the text object.
+ /// </summary>
+ /// <returns></returns>
+ protected override Material[] GetSharedMaterials()
+ {
+ int materialCount = m_textInfo.materialCount;
+
+ if (m_fontSharedMaterials == null)
+ m_fontSharedMaterials = new Material[materialCount];
+ else if (m_fontSharedMaterials.Length != materialCount)
+ TMP_TextInfo.Resize(ref m_fontSharedMaterials, materialCount, false);
+
+ for (int i = 0; i < materialCount; i++)
+ {
+ if (i == 0)
+ m_fontSharedMaterials[i] = m_sharedMaterial;
+ else
+ m_fontSharedMaterials[i] = m_subTextObjects[i].sharedMaterial;
+ }
+
+ return m_fontSharedMaterials;
+ }
+
+
+ /// <summary>
+ /// Method used to assign new materials to the text and sub text objects.
+ /// </summary>
+ protected override void SetSharedMaterials(Material[] materials)
+ {
+ int materialCount = m_textInfo.materialCount;
+
+ // Check allocation of the fontSharedMaterials array.
+ if (m_fontSharedMaterials == null)
+ m_fontSharedMaterials = new Material[materialCount];
+ else if (m_fontSharedMaterials.Length != materialCount)
+ TMP_TextInfo.Resize(ref m_fontSharedMaterials, materialCount, false);
+
+ // Only assign as many materials as the text object contains.
+ for (int i = 0; i < materialCount; i++)
+ {
+ Texture mat_MainTex = materials[i].GetTexture(ShaderUtilities.ID_MainTex);
+
+ if (i == 0)
+ {
+ // Only assign new material if the font atlas textures match.
+ if ( mat_MainTex == null || mat_MainTex.GetInstanceID() != m_sharedMaterial.GetTexture(ShaderUtilities.ID_MainTex).GetInstanceID())
+ continue;
+
+ m_sharedMaterial = m_fontSharedMaterials[i] = materials[i];
+ m_padding = GetPaddingForMaterial(m_sharedMaterial);
+ }
+ else
+ {
+ // Only assign new material if the font atlas textures match.
+ if (mat_MainTex == null || mat_MainTex.GetInstanceID() != m_subTextObjects[i].sharedMaterial.GetTexture(ShaderUtilities.ID_MainTex).GetInstanceID())
+ continue;
+
+ // Only assign a new material if none were specified in the text input.
+ if (m_subTextObjects[i].isDefaultMaterial)
+ m_subTextObjects[i].sharedMaterial = m_fontSharedMaterials[i] = materials[i];
+ }
+ }
+ }
+
+
+ // This function will create an instance of the Font Material.
+ protected override void SetOutlineThickness(float thickness)
+ {
+ thickness = Mathf.Clamp01(thickness);
+ m_renderer.material.SetFloat(ShaderUtilities.ID_OutlineWidth, thickness);
+
+ if (m_fontMaterial == null)
+ m_fontMaterial = m_renderer.material;
+
+ m_fontMaterial = m_renderer.material;
+ m_sharedMaterial = m_fontMaterial;
+ m_padding = GetPaddingForMaterial();
+ }
+
+
+ // This function will create an instance of the Font Material.
+ protected override void SetFaceColor(Color32 color)
+ {
+ m_renderer.material.SetColor(ShaderUtilities.ID_FaceColor, color);
+
+ if (m_fontMaterial == null)
+ m_fontMaterial = m_renderer.material;
+
+ m_sharedMaterial = m_fontMaterial;
+ }
+
+
+ // This function will create an instance of the Font Material.
+ protected override void SetOutlineColor(Color32 color)
+ {
+ m_renderer.material.SetColor(ShaderUtilities.ID_OutlineColor, color);
+
+ if (m_fontMaterial == null)
+ m_fontMaterial = m_renderer.material;
+
+ //Debug.Log("Material ID:" + m_fontMaterial.GetInstanceID());
+ m_sharedMaterial = m_fontMaterial;
+ }
+
+
+ // Function used to create an instance of the material
+ void CreateMaterialInstance()
+ {
+ Material mat = new Material(m_sharedMaterial);
+ mat.shaderKeywords = m_sharedMaterial.shaderKeywords;
+
+ //mat.hideFlags = HideFlags.DontSave;
+ mat.name += " Instance";
+
+ m_fontMaterial = mat;
+ }
+
+
+ // Sets the Render Queue and Ztest mode
+ protected override void SetShaderDepth()
+ {
+ if (m_isOverlay)
+ {
+ // Changing these properties results in an instance of the material
+ m_sharedMaterial.SetFloat(ShaderUtilities.ShaderTag_ZTestMode, 0);
+ //m_renderer.material.SetFloat("_ZTestMode", 8);
+ m_renderer.material.renderQueue = 4000;
+
+ m_sharedMaterial = m_renderer.material;
+ //Debug.Log("Text set to Overlay mode.");
+ }
+ else
+ {
+ // Should this use an instanced material?
+ m_sharedMaterial.SetFloat(ShaderUtilities.ShaderTag_ZTestMode, 4);
+ m_renderer.material.renderQueue = -1;
+
+ m_sharedMaterial = m_renderer.material;
+ //Debug.Log("Text set to Normal mode.");
+ }
+ }
+
+
+ // Sets the Culling mode of the material
+ protected override void SetCulling()
+ {
+ if (m_isCullingEnabled)
+ {
+ m_renderer.material.SetFloat("_CullMode", 2);
+
+ for (int i = 1; i < m_subTextObjects.Length && m_subTextObjects[i] != null; i++)
+ {
+ Renderer renderer = m_subTextObjects[i].renderer;
+
+ if (renderer != null)
+ {
+ renderer.material.SetFloat(ShaderUtilities.ShaderTag_CullMode, 2);
+ }
+ }
+ }
+ else
+ {
+ m_renderer.material.SetFloat("_CullMode", 0);
+
+ for (int i = 1; i < m_subTextObjects.Length && m_subTextObjects[i] != null; i++)
+ {
+ Renderer renderer = m_subTextObjects[i].renderer;
+
+ if (renderer != null)
+ {
+ renderer.material.SetFloat(ShaderUtilities.ShaderTag_CullMode, 0);
+ }
+ }
+ }
+ }
+
+
+ // Set Perspective Correction Mode based on whether Camera is Orthographic or Perspective
+ void SetPerspectiveCorrection()
+ {
+ if (m_isOrthographic)
+ m_sharedMaterial.SetFloat(ShaderUtilities.ID_PerspectiveFilter, 0.0f);
+ else
+ m_sharedMaterial.SetFloat(ShaderUtilities.ID_PerspectiveFilter, 0.875f);
+ }
+
+
+ /// <summary>
+ /// Get the padding value for the currently assigned material.
+ /// </summary>
+ /// <returns></returns>
+ protected override float GetPaddingForMaterial(Material mat)
+ {
+ m_padding = ShaderUtilities.GetPadding(mat, m_enableExtraPadding, m_isUsingBold);
+ m_isMaskingEnabled = ShaderUtilities.IsMaskingEnabled(m_sharedMaterial);
+ m_isSDFShader = mat.HasProperty(ShaderUtilities.ID_WeightNormal);
+
+ return m_padding;
+ }
+
+
+ /// <summary>
+ /// Get the padding value for the currently assigned material.
+ /// </summary>
+ /// <returns></returns>
+ protected override float GetPaddingForMaterial()
+ {
+ ShaderUtilities.GetShaderPropertyIDs();
+
+ if (m_sharedMaterial == null) return 0;
+
+ m_padding = ShaderUtilities.GetPadding(m_sharedMaterial, m_enableExtraPadding, m_isUsingBold);
+ m_isMaskingEnabled = ShaderUtilities.IsMaskingEnabled(m_sharedMaterial);
+ m_isSDFShader = m_sharedMaterial.HasProperty(ShaderUtilities.ID_WeightNormal);
+
+ return m_padding;
+ }
+
+
+ // This function parses through the Char[] to determine how many characters will be visible. It then makes sure the arrays are large enough for all those characters.
+ protected override int SetArraySizes(UnicodeChar[] chars)
+ {
+ //Debug.Log("*** SetArraySizes() ***");
+
+ int spriteCount = 0;
+
+ m_totalCharacterCount = 0;
+ m_isUsingBold = false;
+ m_isParsingText = false;
+ tag_NoParsing = false;
+ m_FontStyleInternal = m_fontStyle;
+
+ m_FontWeightInternal = (m_FontStyleInternal & FontStyles.Bold) == FontStyles.Bold ? FontWeight.Bold : m_fontWeight;
+ m_FontWeightStack.SetDefault(m_FontWeightInternal);
+
+ m_currentFontAsset = m_fontAsset;
+ m_currentMaterial = m_sharedMaterial;
+ m_currentMaterialIndex = 0;
+
+ m_materialReferenceStack.SetDefault(new MaterialReference(m_currentMaterialIndex, m_currentFontAsset, null, m_currentMaterial, m_padding));
+
+ m_materialReferenceIndexLookup.Clear();
+ MaterialReference.AddMaterialReference(m_currentMaterial, m_currentFontAsset, m_materialReferences, m_materialReferenceIndexLookup);
+
+ if (m_textInfo == null) m_textInfo = new TMP_TextInfo();
+ m_textElementType = TMP_TextElementType.Character;
+
+ // Clear Linked Text object if we have one.
+ if (m_linkedTextComponent != null)
+ {
+ m_linkedTextComponent.text = string.Empty;
+ m_linkedTextComponent.ForceMeshUpdate();
+ }
+
+ // Parsing XML tags in the text
+ for (int i = 0; i < chars.Length && chars[i].unicode != 0; i++)
+ {
+ //Make sure the characterInfo array can hold the next text element.
+ if (m_textInfo.characterInfo == null || m_totalCharacterCount >= m_textInfo.characterInfo.Length)
+ TMP_TextInfo.Resize(ref m_textInfo.characterInfo, m_totalCharacterCount + 1, true);
+
+ int unicode = (int)chars[i].unicode;
+
+ // PARSE XML TAGS
+ #region PARSE XML TAGS
+ if (m_isRichText && unicode == 60) // if Char '<'
+ {
+ int prev_MaterialIndex = m_currentMaterialIndex;
+
+ // Check if Tag is Valid
+ if (ValidateHtmlTag(chars, i + 1, out int tagEnd))
+ {
+ int tagStartIndex = chars[i].stringIndex;
+ i = tagEnd;
+
+ if ((m_FontStyleInternal & FontStyles.Bold) == FontStyles.Bold) m_isUsingBold = true;
+
+ if (m_textElementType == TMP_TextElementType.Sprite)
+ {
+ m_materialReferences[m_currentMaterialIndex].referenceCount += 1;
+
+ m_textInfo.characterInfo[m_totalCharacterCount].character = (char)(57344 + m_spriteIndex);
+ m_textInfo.characterInfo[m_totalCharacterCount].spriteIndex = m_spriteIndex;
+ m_textInfo.characterInfo[m_totalCharacterCount].fontAsset = m_currentFontAsset;
+ m_textInfo.characterInfo[m_totalCharacterCount].spriteAsset = m_currentSpriteAsset;
+ m_textInfo.characterInfo[m_totalCharacterCount].materialReferenceIndex = m_currentMaterialIndex;
+ m_textInfo.characterInfo[m_totalCharacterCount].textElement = m_currentSpriteAsset.spriteCharacterTable[m_spriteIndex];
+ m_textInfo.characterInfo[m_totalCharacterCount].elementType = m_textElementType;
+ m_textInfo.characterInfo[m_totalCharacterCount].index = tagStartIndex;
+ m_textInfo.characterInfo[m_totalCharacterCount].stringLength = chars[i].stringIndex - tagStartIndex + 1;
+
+ // Restore element type and material index to previous values.
+ m_textElementType = TMP_TextElementType.Character;
+ m_currentMaterialIndex = prev_MaterialIndex;
+
+ spriteCount += 1;
+ m_totalCharacterCount += 1;
+ }
+
+ continue;
+ }
+ }
+ #endregion
+
+ bool isUsingAlternativeTypeface = false;
+ bool isUsingFallbackOrAlternativeTypeface = false;
+
+ TMP_Character character;
+ TMP_FontAsset tempFontAsset;
+ TMP_FontAsset prev_fontAsset = m_currentFontAsset;
+ Material prev_material = m_currentMaterial;
+ int prev_materialIndex = m_currentMaterialIndex;
+
+
+ // Handle Font Styles like LowerCase, UpperCase and SmallCaps.
+ #region Handling of LowerCase, UpperCase and SmallCaps Font Styles
+ if (m_textElementType == TMP_TextElementType.Character)
+ {
+ if ((m_FontStyleInternal & FontStyles.UpperCase) == FontStyles.UpperCase)
+ {
+ // If this character is lowercase, switch to uppercase.
+ if (char.IsLower((char)unicode))
+ unicode = char.ToUpper((char)unicode);
+
+ }
+ else if ((m_FontStyleInternal & FontStyles.LowerCase) == FontStyles.LowerCase)
+ {
+ // If this character is uppercase, switch to lowercase.
+ if (char.IsUpper((char)unicode))
+ unicode = char.ToLower((char)unicode);
+ }
+ else if ((m_FontStyleInternal & FontStyles.SmallCaps) == FontStyles.SmallCaps)
+ {
+ // Only convert lowercase characters to uppercase.
+ if (char.IsLower((char)unicode))
+ unicode = char.ToUpper((char)unicode);
+ }
+ }
+ #endregion
+
+
+ // Lookup the Glyph data for each character and cache it.
+ #region LOOKUP GLYPH
+ character = TMP_FontAssetUtilities.GetCharacterFromFontAsset((uint)unicode, m_currentFontAsset, false, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface, out tempFontAsset);
+
+ // Search for the glyph in the list of fallback assigned to the primary font asset.
+ if (character == null)
+ {
+ if (m_currentFontAsset.fallbackFontAssetTable != null && m_currentFontAsset.fallbackFontAssetTable.Count > 0)
+ character = TMP_FontAssetUtilities.GetCharacterFromFontAssets((uint)unicode, m_currentFontAsset.fallbackFontAssetTable, true, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface, out tempFontAsset);
+ }
+
+ // Search for the glyph in the Sprite Asset assigned to the text object.
+ if (character == null)
+ {
+ TMP_SpriteAsset spriteAsset = this.spriteAsset;
+
+ if (spriteAsset != null)
+ {
+ int spriteIndex = -1;
+
+ // Check Default Sprite Asset and its Fallbacks
+ spriteAsset = TMP_SpriteAsset.SearchForSpriteByUnicode(spriteAsset, (uint)unicode, true, out spriteIndex);
+
+ if (spriteIndex != -1)
+ {
+ m_textElementType = TMP_TextElementType.Sprite;
+ m_textInfo.characterInfo[m_totalCharacterCount].elementType = m_textElementType;
+
+ m_currentMaterialIndex = MaterialReference.AddMaterialReference(spriteAsset.material, spriteAsset, m_materialReferences, m_materialReferenceIndexLookup);
+ m_materialReferences[m_currentMaterialIndex].referenceCount += 1;
+
+ m_textInfo.characterInfo[m_totalCharacterCount].character = (char)unicode;
+ m_textInfo.characterInfo[m_totalCharacterCount].spriteIndex = spriteIndex;
+ m_textInfo.characterInfo[m_totalCharacterCount].fontAsset = m_currentFontAsset;
+ m_textInfo.characterInfo[m_totalCharacterCount].spriteAsset = spriteAsset;
+ m_textInfo.characterInfo[m_totalCharacterCount].textElement = spriteAsset.spriteCharacterTable[m_spriteIndex];
+ m_textInfo.characterInfo[m_totalCharacterCount].materialReferenceIndex = m_currentMaterialIndex;
+ m_textInfo.characterInfo[m_totalCharacterCount].index = chars[i].stringIndex;
+ m_textInfo.characterInfo[m_totalCharacterCount].stringLength = chars[i].length;
+
+ // Restore element type and material index to previous values.
+ m_textElementType = TMP_TextElementType.Character;
+ m_currentMaterialIndex = prev_materialIndex;
+
+ spriteCount += 1;
+ m_totalCharacterCount += 1;
+
+ continue;
+ }
+ }
+ }
+
+ // Search for the glyph in the list of fallback assigned in the TMP Settings (General Fallbacks).
+ if (character == null)
+ {
+ if (TMP_Settings.fallbackFontAssets != null && TMP_Settings.fallbackFontAssets.Count > 0)
+ character = TMP_FontAssetUtilities.GetCharacterFromFontAssets((uint)unicode, TMP_Settings.fallbackFontAssets, true, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface, out tempFontAsset);
+ }
+
+ // Search for the glyph in the Default Font Asset assigned in the TMP Settings file.
+ if (character == null)
+ {
+ if (TMP_Settings.defaultFontAsset != null)
+ character = TMP_FontAssetUtilities.GetCharacterFromFontAsset((uint)unicode, TMP_Settings.defaultFontAsset, true, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface, out tempFontAsset);
+ }
+
+ // TODO: Add support for using Sprite Assets like a special Emoji only Sprite Asset when UTF16 or UTF32 glyphs are requested.
+ // This would kind of mirror native Emoji support.
+ if (character == null)
+ {
+ TMP_SpriteAsset spriteAsset = TMP_Settings.defaultSpriteAsset;
+
+ if (spriteAsset != null)
+ {
+ int spriteIndex = -1;
+
+ // Check Default Sprite Asset and its Fallbacks
+ spriteAsset = TMP_SpriteAsset.SearchForSpriteByUnicode(spriteAsset, (uint)unicode, true, out spriteIndex);
+
+ if (spriteIndex != -1)
+ {
+ m_textElementType = TMP_TextElementType.Sprite;
+ m_textInfo.characterInfo[m_totalCharacterCount].elementType = m_textElementType;
+
+ m_currentMaterialIndex = MaterialReference.AddMaterialReference(spriteAsset.material, spriteAsset, m_materialReferences, m_materialReferenceIndexLookup);
+ m_materialReferences[m_currentMaterialIndex].referenceCount += 1;
+
+ m_textInfo.characterInfo[m_totalCharacterCount].character = (char)unicode;
+ m_textInfo.characterInfo[m_totalCharacterCount].spriteIndex = spriteIndex;
+ m_textInfo.characterInfo[m_totalCharacterCount].fontAsset = m_currentFontAsset;
+ m_textInfo.characterInfo[m_totalCharacterCount].spriteAsset = spriteAsset;
+ m_textInfo.characterInfo[m_totalCharacterCount].textElement = spriteAsset.spriteCharacterTable[m_spriteIndex];
+ m_textInfo.characterInfo[m_totalCharacterCount].materialReferenceIndex = m_currentMaterialIndex;
+ m_textInfo.characterInfo[m_totalCharacterCount].index = chars[i].stringIndex;
+ m_textInfo.characterInfo[m_totalCharacterCount].stringLength = chars[i].length;
+
+ // Restore element type and material index to previous values.
+ m_textElementType = TMP_TextElementType.Character;
+ m_currentMaterialIndex = prev_materialIndex;
+
+ spriteCount += 1;
+ m_totalCharacterCount += 1;
+
+ continue;
+ }
+
+ }
+ }
+
+ //Check if Lowercase or Uppercase variant of the character is available.
+ // Not sure this is necessary anyone as it is very unlikely with recursive search through fallback fonts.
+ //if (glyph == null)
+ //{
+ // if (char.IsLower((char)c))
+ // {
+ // if (m_currentFontAsset.characterDictionary.TryGetValue(char.ToUpper((char)c), out glyph))
+ // c = chars[i] = char.ToUpper((char)c);
+ // }
+ // else if (char.IsUpper((char)c))
+ // {
+ // if (m_currentFontAsset.characterDictionary.TryGetValue(char.ToLower((char)c), out glyph))
+ // c = chars[i] = char.ToLower((char)c);
+ // }
+ //}
+
+ // Replace missing glyph by the Square (9633) glyph or possibly the Space (32) glyph.
+ if (character == null)
+ {
+ // Save the original unicode character
+ int srcGlyph = unicode;
+
+ // Try replacing the missing glyph character by TMP Settings Missing Glyph or Square (9633) character.
+ unicode = chars[i].unicode = TMP_Settings.missingGlyphCharacter == 0 ? 9633 : TMP_Settings.missingGlyphCharacter;
+
+ // Check for the missing glyph character in the currently assigned font asset and its fallbacks
+ character = TMP_FontAssetUtilities.GetCharacterFromFontAsset((uint)unicode, m_currentFontAsset, true, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface, out tempFontAsset);
+
+ if (character == null)
+ {
+ // Search for the missing glyph character in the TMP Settings Fallback list.
+ if (TMP_Settings.fallbackFontAssets != null && TMP_Settings.fallbackFontAssets.Count > 0)
+ character = TMP_FontAssetUtilities.GetCharacterFromFontAssets((uint)unicode, TMP_Settings.fallbackFontAssets, true, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface, out tempFontAsset);
+ }
+
+ if (character == null)
+ {
+ // Search for the missing glyph in the TMP Settings Default Font Asset.
+ if (TMP_Settings.defaultFontAsset != null)
+ character = TMP_FontAssetUtilities.GetCharacterFromFontAsset((uint)unicode, TMP_Settings.defaultFontAsset, true, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface, out tempFontAsset);
+ }
+
+ if (character == null)
+ {
+ // Use Space (32) Glyph from the currently assigned font asset.
+ unicode = chars[i].unicode = 32;
+ character = TMP_FontAssetUtilities.GetCharacterFromFontAsset((uint)unicode, m_currentFontAsset, true, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface, out tempFontAsset);
+ if (!TMP_Settings.warningsDisabled)
+ Debug.LogWarning("Character with ASCII value of " + srcGlyph + " was not found in the Font Asset Glyph Table. It was replaced by a space.", this);
+ }
+ }
+
+ // Determine if the font asset is still the current font asset or a fallback.
+ if (tempFontAsset != null)
+ {
+ if (tempFontAsset.GetInstanceID() != m_currentFontAsset.GetInstanceID())
+ {
+ isUsingFallbackOrAlternativeTypeface = true;
+ m_currentFontAsset = tempFontAsset;
+ }
+ }
+ #endregion
+
+ m_textInfo.characterInfo[m_totalCharacterCount].elementType = TMP_TextElementType.Character;
+ m_textInfo.characterInfo[m_totalCharacterCount].textElement = character;
+ m_textInfo.characterInfo[m_totalCharacterCount].isUsingAlternateTypeface = isUsingAlternativeTypeface;
+ m_textInfo.characterInfo[m_totalCharacterCount].character = (char)unicode;
+ m_textInfo.characterInfo[m_totalCharacterCount].fontAsset = m_currentFontAsset;
+ m_textInfo.characterInfo[m_totalCharacterCount].index = chars[i].stringIndex;
+ m_textInfo.characterInfo[m_totalCharacterCount].stringLength = chars[i].length;
+
+ if (isUsingFallbackOrAlternativeTypeface)
+ {
+ // Create Fallback material instance matching current material preset if necessary
+ if (TMP_Settings.matchMaterialPreset)
+ m_currentMaterial = TMP_MaterialManager.GetFallbackMaterial(m_currentMaterial, m_currentFontAsset.material);
+ else
+ m_currentMaterial = m_currentFontAsset.material;
+
+ m_currentMaterialIndex = MaterialReference.AddMaterialReference(m_currentMaterial, m_currentFontAsset, m_materialReferences, m_materialReferenceIndexLookup);
+ }
+
+ if (!char.IsWhiteSpace((char)unicode) && unicode != 0x200B)
+ {
+ // Limit the mesh of the main text object to 65535 vertices and use sub objects for the overflow.
+ if (m_materialReferences[m_currentMaterialIndex].referenceCount < 16383)
+ m_materialReferences[m_currentMaterialIndex].referenceCount += 1;
+ else
+ {
+ m_currentMaterialIndex = MaterialReference.AddMaterialReference(new Material(m_currentMaterial), m_currentFontAsset, m_materialReferences, m_materialReferenceIndexLookup);
+ m_materialReferences[m_currentMaterialIndex].referenceCount += 1;
+ }
+ }
+
+ m_textInfo.characterInfo[m_totalCharacterCount].material = m_currentMaterial;
+ m_textInfo.characterInfo[m_totalCharacterCount].materialReferenceIndex = m_currentMaterialIndex;
+ m_materialReferences[m_currentMaterialIndex].isFallbackMaterial = isUsingFallbackOrAlternativeTypeface;
+
+ // Restore previous font asset and material if fallback font was used.
+ if (isUsingFallbackOrAlternativeTypeface)
+ {
+ m_materialReferences[m_currentMaterialIndex].fallbackMaterial = prev_material;
+ m_currentFontAsset = prev_fontAsset;
+ m_currentMaterial = prev_material;
+ m_currentMaterialIndex = prev_materialIndex;
+ }
+
+ m_totalCharacterCount += 1;
+ }
+
+ // Early return if we are calculating the preferred values.
+ if (m_isCalculatingPreferredValues)
+ {
+ m_isCalculatingPreferredValues = false;
+ m_isInputParsingRequired = true;
+ return m_totalCharacterCount;
+ }
+
+ // Save material and sprite count.
+ m_textInfo.spriteCount = spriteCount;
+ int materialCount = m_textInfo.materialCount = m_materialReferenceIndexLookup.Count;
+
+ // Check if we need to resize the MeshInfo array for handling different materials.
+ if (materialCount > m_textInfo.meshInfo.Length)
+ TMP_TextInfo.Resize(ref m_textInfo.meshInfo, materialCount, false);
+
+ // Resize SubTextObject array if necessary
+ if (materialCount > m_subTextObjects.Length)
+ TMP_TextInfo.Resize(ref m_subTextObjects, Mathf.NextPowerOfTwo(materialCount + 1));
+
+ // Resize CharacterInfo[] if allocations are excessive
+ if (m_textInfo.characterInfo.Length - m_totalCharacterCount > 256)
+ TMP_TextInfo.Resize(ref m_textInfo.characterInfo, Mathf.Max(m_totalCharacterCount + 1, 256), true);
+
+
+ // Iterate through the material references to set the mesh buffer allocations
+ for (int i = 0; i < materialCount; i++)
+ {
+ // Add new sub text object for each material reference
+ if (i > 0)
+ {
+ if (m_subTextObjects[i] == null)
+ {
+ m_subTextObjects[i] = TMP_SubMesh.AddSubTextObject(this, m_materialReferences[i]);
+
+ // Not sure this is necessary
+ m_textInfo.meshInfo[i].vertices = null;
+ }
+ //else if (m_subTextObjects[i].gameObject.activeInHierarchy == false)
+ // m_subTextObjects[i].gameObject.SetActive(true);
+
+ // Check if the material has changed.
+ if (m_subTextObjects[i].sharedMaterial == null || m_subTextObjects[i].sharedMaterial.GetInstanceID() != m_materialReferences[i].material.GetInstanceID())
+ {
+ bool isDefaultMaterial = m_materialReferences[i].isDefaultMaterial;
+
+ m_subTextObjects[i].isDefaultMaterial = isDefaultMaterial;
+
+ // Assign new material if we are not using the default material or if the font asset has changed.
+ if (!isDefaultMaterial || m_subTextObjects[i].sharedMaterial == null || m_subTextObjects[i].sharedMaterial.GetTexture(ShaderUtilities.ID_MainTex).GetInstanceID() != m_materialReferences[i].material.GetTexture(ShaderUtilities.ID_MainTex).GetInstanceID())
+ {
+ m_subTextObjects[i].sharedMaterial = m_materialReferences[i].material;
+ m_subTextObjects[i].fontAsset = m_materialReferences[i].fontAsset;
+ m_subTextObjects[i].spriteAsset = m_materialReferences[i].spriteAsset;
+ }
+ }
+
+ // Check if we need to use a Fallback Material
+ if (m_materialReferences[i].isFallbackMaterial)
+ {
+ m_subTextObjects[i].fallbackMaterial = m_materialReferences[i].material;
+ m_subTextObjects[i].fallbackSourceMaterial = m_materialReferences[i].fallbackMaterial;
+ }
+
+ }
+
+ int referenceCount = m_materialReferences[i].referenceCount;
+
+ // Check to make sure our buffers allocations can accommodate the required text elements.
+ if (m_textInfo.meshInfo[i].vertices == null || m_textInfo.meshInfo[i].vertices.Length < referenceCount * (!m_isVolumetricText ? 4 : 8))
+ {
+ if (m_textInfo.meshInfo[i].vertices == null)
+ {
+ if (i == 0)
+ m_textInfo.meshInfo[i] = new TMP_MeshInfo(m_mesh, referenceCount + 1, m_isVolumetricText);
+ else
+ m_textInfo.meshInfo[i] = new TMP_MeshInfo(m_subTextObjects[i].mesh, referenceCount + 1, m_isVolumetricText);
+ }
+ else
+ m_textInfo.meshInfo[i].ResizeMeshInfo(referenceCount > 1024 ? referenceCount + 256 : Mathf.NextPowerOfTwo(referenceCount + 1), m_isVolumetricText);
+ }
+ else if (m_VertexBufferAutoSizeReduction && referenceCount > 0 && m_textInfo.meshInfo[i].vertices.Length - referenceCount * (!m_isVolumetricText ? 4 : 8) > 1024)
+ {
+ // Resize vertex buffers if allocations are excessive.
+ //Debug.Log("Reducing the size of the vertex buffers.");
+ m_textInfo.meshInfo[i].ResizeMeshInfo(referenceCount > 1024 ? referenceCount + 256 : Mathf.NextPowerOfTwo(referenceCount + 1), m_isVolumetricText);
+ }
+ }
+
+ //TMP_MaterialManager.CleanupFallbackMaterials();
+
+ // Clean up unused SubMeshes
+ for (int i = materialCount; i < m_subTextObjects.Length && m_subTextObjects[i] != null; i++)
+ {
+ if (i < m_textInfo.meshInfo.Length)
+ m_textInfo.meshInfo[i].ClearUnusedVertices(0, true);
+
+ //m_subTextObjects[i].gameObject.SetActive(false);
+ }
+
+ return m_totalCharacterCount;
+ }
+
+
+ // Added to sort handle the potential issue with OnWillRenderObject() not getting called when objects are not visible by camera.
+ //void OnBecameInvisible()
+ //{
+ // if (m_mesh != null)
+ // m_mesh.bounds = new Bounds(transform.position, new Vector3(1000, 1000, 0));
+ //}
+
+
+ /// <summary>
+ /// Update the margin width and height
+ /// </summary>
+ public override void ComputeMarginSize()
+ {
+ if (this.rectTransform != null)
+ {
+ //Debug.Log("*** ComputeMarginSize() *** Current RectTransform's Width is " + m_rectTransform.rect.width + " and Height is " + m_rectTransform.rect.height); // + " and size delta is " + m_rectTransform.sizeDelta);
+
+ m_marginWidth = m_rectTransform.rect.width - m_margin.x - m_margin.z;
+ m_marginHeight = m_rectTransform.rect.height - m_margin.y - m_margin.w;
+
+ // Update the corners of the RectTransform
+ m_RectTransformCorners = GetTextContainerLocalCorners();
+ }
+ }
+
+
+ protected override void OnDidApplyAnimationProperties()
+ {
+ //Debug.Log("*** OnDidApplyAnimationProperties() ***");
+ m_havePropertiesChanged = true;
+ isMaskUpdateRequired = true;
+
+ SetVerticesDirty();
+ }
+
+
+ protected override void OnTransformParentChanged()
+ {
+ //Debug.Log("*** OnTransformParentChanged() ***");
+ //ComputeMarginSize();
+
+ SetVerticesDirty();
+ SetLayoutDirty();
+ }
+
+
+ protected override void OnRectTransformDimensionsChange()
+ {
+ //Debug.Log("*** OnRectTransformDimensionsChange() ***");
+ ComputeMarginSize();
+
+ SetVerticesDirty();
+ SetLayoutDirty();
+ }
+
+
+ /// <summary>
+ /// Function used as a replacement for LateUpdate to check if the transform or scale of the text object has changed.
+ /// </summary>
+ internal override void InternalUpdate()
+ {
+ // We need to update the SDF scale or possibly regenerate the text object if lossy scale has changed.
+ if (m_havePropertiesChanged == false)
+ {
+ float lossyScaleY = m_rectTransform.lossyScale.y;
+ if (lossyScaleY != m_previousLossyScaleY && m_text != string.Empty && m_text != null)
+ {
+ float scaleDelta = lossyScaleY / m_previousLossyScaleY;
+
+ UpdateSDFScale(scaleDelta);
+
+ m_previousLossyScaleY = lossyScaleY;
+ }
+ }
+
+ // Added to handle legacy animation mode.
+ if (m_isUsingLegacyAnimationComponent)
+ {
+ //if (m_havePropertiesChanged)
+ m_havePropertiesChanged = true;
+ OnPreRenderObject();
+ }
+ }
+
+
+ /// <summary>
+ /// Function called when the text needs to be updated.
+ /// </summary>
+ void OnPreRenderObject()
+ {
+ //Debug.Log("*** OnPreRenderObject() ***");
+
+ if (!m_isAwake || (this.IsActive() == false && m_ignoreActiveState == false)) return;
+
+ // Debug Variables
+ loopCountA = 0;
+ //loopCountB = 0;
+ //loopCountC = 0;
+ //loopCountD = 0;
+ //loopCountE = 0;
+
+ if (m_havePropertiesChanged || m_isLayoutDirty)
+ {
+ //Debug.Log("Properties have changed!"); // Assigned Material is:" + m_sharedMaterial); // New Text is: " + m_text + ".");
+
+ if (isMaskUpdateRequired)
+ {
+ UpdateMask();
+ isMaskUpdateRequired = false;
+ }
+
+ // Update mesh padding if necessary.
+ if (checkPaddingRequired)
+ UpdateMeshPadding();
+
+ // Reparse the text if the input has changed or text was truncated.
+ if (m_isInputParsingRequired || m_isTextTruncated)
+ ParseInputText();
+
+ // Reset Font min / max used with Auto-sizing
+ if (m_enableAutoSizing)
+ m_fontSize = Mathf.Clamp(m_fontSizeBase, m_fontSizeMin, m_fontSizeMax);
+
+ m_maxFontSize = m_fontSizeMax;
+ m_minFontSize = m_fontSizeMin;
+ m_lineSpacingDelta = 0;
+ m_charWidthAdjDelta = 0;
+ //m_recursiveCount = 0;
+
+ m_isCharacterWrappingEnabled = false;
+ m_isTextTruncated = false;
+
+ m_havePropertiesChanged = false;
+ m_isLayoutDirty = false;
+ m_ignoreActiveState = false;
+
+ GenerateTextMesh();
+ }
+ }
+
+
+ /// <summary>
+ /// This is the main function that is responsible for creating / displaying the text.
+ /// </summary>
+ protected override void GenerateTextMesh()
+ {
+ //Debug.Log("***** GenerateTextMesh() *****"); // ***** Frame: " + Time.frameCount); // + ". Point Size: " + m_fontSize + ". Margins are (W) " + m_marginWidth + " (H) " + m_marginHeight); // ". Iteration Count: " + loopCountA + ". Min: " + m_minFontSize + " Max: " + m_maxFontSize + " Delta: " + (m_maxFontSize - m_minFontSize) + " Font size is " + m_fontSize); //called for Object with ID " + GetInstanceID()); // Assigned Material is " + m_uiRenderer.GetMaterial().name); // IncludeForMasking " + this.m_IncludeForMasking); // and text is " + m_text);
+
+ // Early exit if no font asset was assigned. This should not be needed since LiberationSans SDF will be assigned by default.
+ if (m_fontAsset == null || m_fontAsset.characterLookupTable == null)
+ {
+ Debug.LogWarning("Can't Generate Mesh! No Font Asset has been assigned to Object ID: " + this.GetInstanceID());
+ return;
+ }
+
+ // Clear TextInfo
+ if (m_textInfo != null)
+ m_textInfo.Clear();
+
+ // Early exit if we don't have any Text to generate.
+ if (m_TextParsingBuffer == null || m_TextParsingBuffer.Length == 0 || m_TextParsingBuffer[0].unicode == (char)0)
+ {
+ // Clear mesh and upload changes to the mesh.
+ ClearMesh(true);
+
+ m_preferredWidth = 0;
+ m_preferredHeight = 0;
+
+ // Event indicating the text has been regenerated.
+ TMPro_EventManager.ON_TEXT_CHANGED(this);
+
+ return;
+ }
+
+ m_currentFontAsset = m_fontAsset;
+ m_currentMaterial = m_sharedMaterial;
+ m_currentMaterialIndex = 0;
+ m_materialReferenceStack.SetDefault(new MaterialReference(m_currentMaterialIndex, m_currentFontAsset, null, m_currentMaterial, m_padding));
+
+ m_currentSpriteAsset = m_spriteAsset;
+
+ // Stop all Sprite Animations
+ if (m_spriteAnimator != null)
+ m_spriteAnimator.StopAllAnimations();
+
+ // Total character count is computed when the text is parsed.
+ int totalCharacterCount = m_totalCharacterCount;
+
+ // Calculate the scale of the font based on selected font size and sampling point size.
+ // baseScale is calculated using the font asset assigned to the text object.
+ float baseScale = m_fontScale = (m_fontSize / m_fontAsset.faceInfo.pointSize * m_fontAsset.faceInfo.scale * (m_isOrthographic ? 1 : 0.1f));
+ float currentElementScale = baseScale;
+ m_fontScaleMultiplier = 1;
+
+ m_currentFontSize = m_fontSize;
+ m_sizeStack.SetDefault(m_currentFontSize);
+ float fontSizeDelta = 0;
+
+ int charCode = 0; // Holds the character code of the currently being processed character.
+
+ m_FontStyleInternal = m_fontStyle; // Set the default style.
+ m_FontWeightInternal = (m_FontStyleInternal & FontStyles.Bold) == FontStyles.Bold ? FontWeight.Bold : m_fontWeight;
+ m_FontWeightStack.SetDefault(m_FontWeightInternal);
+ m_fontStyleStack.Clear();
+
+ m_lineJustification = m_textAlignment; // Sets the line justification mode to match editor alignment.
+ m_lineJustificationStack.SetDefault(m_lineJustification);
+
+ float padding = 0;
+ float style_padding = 0; // Extra padding required to accommodate Bold style.
+ float bold_xAdvance_multiplier = 1; // Used to increase spacing between character when style is bold.
+
+ m_baselineOffset = 0; // Used by subscript characters.
+ m_baselineOffsetStack.Clear();
+
+ // Underline
+ bool beginUnderline = false;
+ Vector3 underline_start = Vector3.zero; // Used to track where underline starts & ends.
+ Vector3 underline_end = Vector3.zero;
+
+ // Strike-through
+ bool beginStrikethrough = false;
+ Vector3 strikethrough_start = Vector3.zero;
+ Vector3 strikethrough_end = Vector3.zero;
+
+ // Text Highlight
+ bool beginHighlight = false;
+ Vector3 highlight_start = Vector3.zero;
+ Vector3 highlight_end = Vector3.zero;
+
+ m_fontColor32 = m_fontColor;
+ Color32 vertexColor;
+ m_htmlColor = m_fontColor32;
+ m_underlineColor = m_htmlColor;
+ m_strikethroughColor = m_htmlColor;
+
+ m_colorStack.SetDefault(m_htmlColor);
+ m_underlineColorStack.SetDefault(m_htmlColor);
+ m_strikethroughColorStack.SetDefault(m_htmlColor);
+ m_highlightColorStack.SetDefault(m_htmlColor);
+
+ m_colorGradientPreset = null;
+ m_colorGradientStack.SetDefault(null);
+
+ // Clear the Style stack.
+ //m_styleStack.Clear();
+
+ // Clear the Action stack.
+ m_actionStack.Clear();
+
+ m_isFXMatrixSet = false;
+
+ m_lineOffset = 0; // Amount of space between lines (font line spacing + m_linespacing).
+ m_lineHeight = TMP_Math.FLOAT_UNSET;
+ float lineGap = m_currentFontAsset.faceInfo.lineHeight - (m_currentFontAsset.faceInfo.ascentLine - m_currentFontAsset.faceInfo.descentLine);
+
+ m_cSpacing = 0; // Amount of space added between characters as a result of the use of the <cspace> tag.
+ m_monoSpacing = 0;
+ float lineOffsetDelta = 0;
+ m_xAdvance = 0; // Used to track the position of each character.
+
+ tag_LineIndent = 0; // Used for indentation of text.
+ tag_Indent = 0;
+ m_indentStack.SetDefault(0);
+ tag_NoParsing = false;
+ //m_isIgnoringAlignment = false;
+
+ m_characterCount = 0; // Total characters in the char[]
+
+ // Tracking of line information
+ m_firstCharacterOfLine = 0;
+ m_lastCharacterOfLine = 0;
+ m_firstVisibleCharacterOfLine = 0;
+ m_lastVisibleCharacterOfLine = 0;
+ m_maxLineAscender = k_LargeNegativeFloat;
+ m_maxLineDescender = k_LargePositiveFloat;
+ m_lineNumber = 0;
+ m_lineVisibleCharacterCount = 0;
+ bool isStartOfNewLine = true;
+ m_firstOverflowCharacterIndex = -1;
+
+ m_pageNumber = 0;
+ int pageToDisplay = Mathf.Clamp(m_pageToDisplay - 1, 0, m_textInfo.pageInfo.Length - 1);
+ int previousPageOverflowChar = 0;
+
+ int ellipsisIndex = 0;
+
+ Vector4 margins = m_margin;
+ float marginWidth = m_marginWidth;
+ float marginHeight = m_marginHeight;
+ m_marginLeft = 0;
+ m_marginRight = 0;
+ m_width = -1;
+ float width = marginWidth + 0.0001f - m_marginLeft - m_marginRight;
+
+ // Need to initialize these Extents structures
+ m_meshExtents.min = k_LargePositiveVector2;
+ m_meshExtents.max = k_LargeNegativeVector2;
+
+ // Initialize lineInfo
+ m_textInfo.ClearLineInfo();
+
+ // Tracking of the highest Ascender
+ m_maxCapHeight = 0;
+ m_maxAscender = 0;
+ m_maxDescender = 0;
+ float pageAscender = 0;
+ float maxVisibleDescender = 0;
+ bool isMaxVisibleDescenderSet = false;
+ m_isNewPage = false;
+
+ // Initialize struct to track states of word wrapping
+ bool isFirstWord = true;
+ m_isNonBreakingSpace = false;
+ bool ignoreNonBreakingSpace = false;
+ bool isLastBreakingChar = false;
+ float linebreakingWidth = 0;
+ int wrappingIndex = 0;
+
+ // Save character and line state before we begin layout.
+ SaveWordWrappingState(ref m_SavedWordWrapState, -1, -1);
+ SaveWordWrappingState(ref m_SavedLineState, -1, -1);
+
+ loopCountA += 1;
+
+ // Parse through Character buffer to read HTML tags and begin creating mesh.
+ for (int i = 0; i < m_TextParsingBuffer.Length && m_TextParsingBuffer[i].unicode != 0; i++)
+ {
+ charCode = m_TextParsingBuffer[i].unicode;
+
+ // Parse Rich Text Tag
+ #region Parse Rich Text Tag
+ if (m_isRichText && charCode == 60) // '<'
+ {
+ m_isParsingText = true;
+ m_textElementType = TMP_TextElementType.Character;
+
+ // Check if Tag is valid. If valid, skip to the end of the validated tag.
+ if (ValidateHtmlTag(m_TextParsingBuffer, i + 1, out int endTagIndex))
+ {
+ i = endTagIndex;
+
+ // Continue to next character or handle the sprite element
+ if (m_textElementType == TMP_TextElementType.Character)
+ continue;
+ }
+ }
+ else
+ {
+ m_textElementType = m_textInfo.characterInfo[m_characterCount].elementType;
+ m_currentMaterialIndex = m_textInfo.characterInfo[m_characterCount].materialReferenceIndex;
+ m_currentFontAsset = m_textInfo.characterInfo[m_characterCount].fontAsset;
+ }
+ #endregion End Parse Rich Text Tag
+
+ int prev_MaterialIndex = m_currentMaterialIndex;
+ bool isUsingAltTypeface = m_textInfo.characterInfo[m_characterCount].isUsingAlternateTypeface;
+
+ m_isParsingText = false;
+
+ // When using Linked text, mark character as ignored and skip to next character.
+ if (m_characterCount < m_firstVisibleCharacter)
+ {
+ m_textInfo.characterInfo[m_characterCount].isVisible = false;
+ m_textInfo.characterInfo[m_characterCount].character = (char)0x200B;
+ m_characterCount += 1;
+ continue;
+ }
+
+ // Handle Font Styles like LowerCase, UpperCase and SmallCaps.
+ #region Handling of LowerCase, UpperCase and SmallCaps Font Styles
+
+ float smallCapsMultiplier = 1.0f;
+
+ if (m_textElementType == TMP_TextElementType.Character)
+ {
+ if ((m_FontStyleInternal & FontStyles.UpperCase) == FontStyles.UpperCase)
+ {
+ // If this character is lowercase, switch to uppercase.
+ if (char.IsLower((char)charCode))
+ charCode = char.ToUpper((char)charCode);
+
+ }
+ else if ((m_FontStyleInternal & FontStyles.LowerCase) == FontStyles.LowerCase)
+ {
+ // If this character is uppercase, switch to lowercase.
+ if (char.IsUpper((char)charCode))
+ charCode = char.ToLower((char)charCode);
+ }
+ else if ((m_FontStyleInternal & FontStyles.SmallCaps) == FontStyles.SmallCaps)
+ {
+ if (char.IsLower((char)charCode))
+ {
+ smallCapsMultiplier = 0.8f;
+ charCode = char.ToUpper((char)charCode);
+ }
+ }
+ }
+ #endregion
+
+
+ // Look up Character Data from Dictionary and cache it.
+ #region Look up Character Data
+ if (m_textElementType == TMP_TextElementType.Sprite)
+ {
+ // If a sprite is used as a fallback then get a reference to it and set the color to white.
+ m_currentSpriteAsset = m_textInfo.characterInfo[m_characterCount].spriteAsset;
+ m_spriteIndex = m_textInfo.characterInfo[m_characterCount].spriteIndex;
+
+ TMP_SpriteCharacter sprite = m_currentSpriteAsset.spriteCharacterTable[m_spriteIndex];
+ if (sprite == null) continue;
+
+ // Sprites are assigned in the E000 Private Area + sprite Index
+ if (charCode == 60)
+ charCode = 57344 + m_spriteIndex;
+ else
+ m_spriteColor = s_colorWhite;
+
+ // The sprite scale calculations are based on the font asset assigned to the text object.
+ float spriteScale = (m_currentFontSize / m_currentFontAsset.faceInfo.pointSize * m_currentFontAsset.faceInfo.scale * (m_isOrthographic ? 1 : 0.1f));
+ currentElementScale = m_currentFontAsset.faceInfo.ascentLine / sprite.glyph.metrics.height * sprite.scale * sprite.glyph.scale * spriteScale;
+
+ m_cached_TextElement = sprite;
+
+ m_textInfo.characterInfo[m_characterCount].elementType = TMP_TextElementType.Sprite;
+ m_textInfo.characterInfo[m_characterCount].scale = spriteScale;
+ m_textInfo.characterInfo[m_characterCount].spriteAsset = m_currentSpriteAsset;
+ m_textInfo.characterInfo[m_characterCount].fontAsset = m_currentFontAsset;
+ m_textInfo.characterInfo[m_characterCount].materialReferenceIndex = m_currentMaterialIndex;
+
+ m_currentMaterialIndex = prev_MaterialIndex;
+
+ padding = 0;
+ }
+ else if (m_textElementType == TMP_TextElementType.Character)
+ {
+ m_cached_TextElement = m_textInfo.characterInfo[m_characterCount].textElement;
+ if (m_cached_TextElement == null) continue;
+
+ m_currentFontAsset = m_textInfo.characterInfo[m_characterCount].fontAsset;
+ m_currentMaterial = m_textInfo.characterInfo[m_characterCount].material;
+ m_currentMaterialIndex = m_textInfo.characterInfo[m_characterCount].materialReferenceIndex;
+
+ // Re-calculate font scale as the font asset may have changed.
+ m_fontScale = m_currentFontSize * smallCapsMultiplier / m_currentFontAsset.faceInfo.pointSize * m_currentFontAsset.faceInfo.scale * (m_isOrthographic ? 1 : 0.1f);
+
+ currentElementScale = m_fontScale * m_fontScaleMultiplier * m_cached_TextElement.scale * m_cached_TextElement.glyph.scale;
+
+ m_textInfo.characterInfo[m_characterCount].elementType = TMP_TextElementType.Character;
+ m_textInfo.characterInfo[m_characterCount].scale = currentElementScale;
+
+ padding = m_currentMaterialIndex == 0 ? m_padding : m_subTextObjects[m_currentMaterialIndex].padding;
+ }
+ #endregion
+
+
+ // Handle Soft Hyphen
+ #region Handle Soft Hyphen
+ float old_scale = currentElementScale;
+ if (charCode == 0xAD)
+ {
+ currentElementScale = 0;
+ }
+ #endregion
+
+
+ // Store some of the text object's information
+ m_textInfo.characterInfo[m_characterCount].character = (char)charCode;
+ m_textInfo.characterInfo[m_characterCount].pointSize = m_currentFontSize;
+ m_textInfo.characterInfo[m_characterCount].color = m_htmlColor;
+ m_textInfo.characterInfo[m_characterCount].underlineColor = m_underlineColor;
+ m_textInfo.characterInfo[m_characterCount].strikethroughColor = m_strikethroughColor;
+ m_textInfo.characterInfo[m_characterCount].highlightColor = m_highlightColor;
+ m_textInfo.characterInfo[m_characterCount].style = m_FontStyleInternal;
+ //m_textInfo.characterInfo[m_characterCount].index = m_TextParsingBuffer[i].stringIndex;
+ //m_textInfo.characterInfo[m_characterCount].isIgnoringAlignment = m_isIgnoringAlignment;
+
+
+ // Handle Kerning if Enabled.
+ #region Handle Kerning
+ TMP_GlyphValueRecord glyphAdjustments = new TMP_GlyphValueRecord();
+ float characterSpacingAdjustment = m_characterSpacing;
+ if (m_enableKerning)
+ {
+ if (m_characterCount < totalCharacterCount - 1)
+ {
+ uint firstGlyphIndex = m_cached_TextElement.glyphIndex;
+ uint secondGlyphIndex = m_textInfo.characterInfo[m_characterCount + 1].textElement.glyphIndex;
+ long key = new GlyphPairKey(firstGlyphIndex, secondGlyphIndex).key;
+
+ if (m_currentFontAsset.fontFeatureTable.m_GlyphPairAdjustmentRecordLookupDictionary.TryGetValue(key, out TMP_GlyphPairAdjustmentRecord adjustmentPair))
+ {
+ glyphAdjustments = adjustmentPair.firstAdjustmentRecord.glyphValueRecord;
+ characterSpacingAdjustment = (adjustmentPair.featureLookupFlags & FontFeatureLookupFlags.IgnoreSpacingAdjustments) == FontFeatureLookupFlags.IgnoreSpacingAdjustments ? 0 : characterSpacingAdjustment;
+ }
+ }
+
+ if (m_characterCount >= 1)
+ {
+ uint firstGlyphIndex = m_textInfo.characterInfo[m_characterCount - 1].textElement.glyphIndex;
+ uint secondGlyphIndex = m_cached_TextElement.glyphIndex;
+ long key = new GlyphPairKey(firstGlyphIndex, secondGlyphIndex).key;
+
+ if (m_currentFontAsset.fontFeatureTable.m_GlyphPairAdjustmentRecordLookupDictionary.TryGetValue(key, out TMP_GlyphPairAdjustmentRecord adjustmentPair))
+ {
+ glyphAdjustments += adjustmentPair.secondAdjustmentRecord.glyphValueRecord;
+ characterSpacingAdjustment = (adjustmentPair.featureLookupFlags & FontFeatureLookupFlags.IgnoreSpacingAdjustments) == FontFeatureLookupFlags.IgnoreSpacingAdjustments ? 0 : characterSpacingAdjustment;
+ }
+ }
+ }
+ #endregion
+
+
+ // Initial Implementation for RTL support.
+ #region Handle Right-to-Left
+ if (m_isRightToLeft)
+ {
+ m_xAdvance -= ((m_cached_TextElement.glyph.metrics.horizontalAdvance * bold_xAdvance_multiplier + characterSpacingAdjustment + m_wordSpacing + m_currentFontAsset.normalSpacingOffset) * currentElementScale + m_cSpacing) * (1 - m_charWidthAdjDelta);
+
+ if (char.IsWhiteSpace((char)charCode) || charCode == 0x200B)
+ m_xAdvance -= m_wordSpacing * currentElementScale;
+ }
+ #endregion
+
+
+ // Handle Mono Spacing
+ #region Handle Mono Spacing
+ float monoAdvance = 0;
+ if (m_monoSpacing != 0)
+ {
+ monoAdvance = (m_monoSpacing / 2 - (m_cached_TextElement.glyph.metrics.width / 2 + m_cached_TextElement.glyph.metrics.horizontalBearingX) * currentElementScale) * (1 - m_charWidthAdjDelta);
+ m_xAdvance += monoAdvance;
+ }
+ #endregion
+
+
+ // Set Padding based on selected font style
+ #region Handle Style Padding
+ if (m_textElementType == TMP_TextElementType.Character && !isUsingAltTypeface && (/*(m_fontStyle & FontStyles.Bold) == FontStyles.Bold ||*/ (m_FontStyleInternal & FontStyles.Bold) == FontStyles.Bold)) // Checks for any combination of Bold Style.
+ {
+ if (m_currentMaterial.HasProperty(ShaderUtilities.ID_GradientScale))
+ {
+ float gradientScale = m_currentMaterial.GetFloat(ShaderUtilities.ID_GradientScale);
+ style_padding = m_currentFontAsset.boldStyle / 4.0f * gradientScale * m_currentMaterial.GetFloat(ShaderUtilities.ID_ScaleRatio_A);
+
+ // Clamp overall padding to Gradient Scale size.
+ if (style_padding + padding > gradientScale)
+ padding = gradientScale - style_padding;
+ }
+ else
+ style_padding = 0;
+
+ bold_xAdvance_multiplier = 1 + m_currentFontAsset.boldSpacing * 0.01f;
+ }
+ else
+ {
+ if (m_currentMaterial.HasProperty(ShaderUtilities.ID_GradientScale))
+ {
+ float gradientScale = m_currentMaterial.GetFloat(ShaderUtilities.ID_GradientScale);
+ style_padding = m_currentFontAsset.normalStyle / 4.0f * gradientScale * m_currentMaterial.GetFloat(ShaderUtilities.ID_ScaleRatio_A);
+
+ // Clamp overall padding to Gradient Scale size.
+ if (style_padding + padding > gradientScale)
+ padding = gradientScale - style_padding;
+ }
+ else
+ style_padding = 0;
+
+ bold_xAdvance_multiplier = 1.0f;
+ }
+ #endregion Handle Style Padding
+
+
+ // Determine the position of the vertices of the Character or Sprite.
+ #region Calculate Vertices Position
+ float fontBaseLineOffset = m_currentFontAsset.faceInfo.baseline * m_fontScale * m_fontScaleMultiplier * m_currentFontAsset.faceInfo.scale;
+ Vector3 top_left;
+ top_left.x = m_xAdvance + ((m_cached_TextElement.glyph.metrics.horizontalBearingX - padding - style_padding + glyphAdjustments.xPlacement) * currentElementScale * (1 - m_charWidthAdjDelta));
+ top_left.y = fontBaseLineOffset + (m_cached_TextElement.glyph.metrics.horizontalBearingY + padding + glyphAdjustments.yPlacement) * currentElementScale - m_lineOffset + m_baselineOffset;
+ top_left.z = 0;
+
+ Vector3 bottom_left;
+ bottom_left.x = top_left.x;
+ bottom_left.y = top_left.y - ((m_cached_TextElement.glyph.metrics.height + padding * 2) * currentElementScale);
+ bottom_left.z = 0;
+
+ Vector3 top_right;
+ top_right.x = bottom_left.x + ((m_cached_TextElement.glyph.metrics.width + padding * 2 + style_padding * 2) * currentElementScale * (1 - m_charWidthAdjDelta));
+ top_right.y = top_left.y;
+ top_right.z = 0;
+
+ Vector3 bottom_right;
+ bottom_right.x = top_right.x;
+ bottom_right.y = bottom_left.y;
+ bottom_right.z = 0;
+ #endregion
+
+
+ // Check if we need to Shear the rectangles for Italic styles
+ #region Handle Italic & Shearing
+ if (m_textElementType == TMP_TextElementType.Character && !isUsingAltTypeface && (/*(m_fontStyle & FontStyles.Italic) == FontStyles.Italic ||*/ (m_FontStyleInternal & FontStyles.Italic) == FontStyles.Italic))
+ {
+ // Shift Top vertices forward by half (Shear Value * height of character) and Bottom vertices back by same amount.
+ float shear_value = m_currentFontAsset.italicStyle * 0.01f;
+ Vector3 topShear = new Vector3(shear_value * ((m_cached_TextElement.glyph.metrics.horizontalBearingY + padding + style_padding) * currentElementScale), 0, 0);
+ Vector3 bottomShear = new Vector3(shear_value * (((m_cached_TextElement.glyph.metrics.horizontalBearingY - m_cached_TextElement.glyph.metrics.height - padding - style_padding)) * currentElementScale), 0, 0);
+
+ top_left = top_left + topShear;
+ bottom_left = bottom_left + bottomShear;
+ top_right = top_right + topShear;
+ bottom_right = bottom_right + bottomShear;
+ }
+ #endregion Handle Italics & Shearing
+
+
+ // Handle Character Rotation
+ #region Handle Character Rotation
+ if (m_isFXMatrixSet)
+ {
+ // Apply scale matrix when simulating Condensed text.
+ if (m_FXMatrix.lossyScale.x != 1)
+ {
+ //top_left = m_FXMatrix.MultiplyPoint3x4(top_left);
+ //bottom_left = m_FXMatrix.MultiplyPoint3x4(bottom_left);
+ //top_right = m_FXMatrix.MultiplyPoint3x4(top_right);
+ //bottom_right = m_FXMatrix.MultiplyPoint3x4(bottom_right);
+ }
+
+ Vector3 positionOffset = (top_right + bottom_left) / 2;
+
+ top_left = m_FXMatrix.MultiplyPoint3x4(top_left - positionOffset) + positionOffset;
+ bottom_left = m_FXMatrix.MultiplyPoint3x4(bottom_left - positionOffset) + positionOffset;
+ top_right = m_FXMatrix.MultiplyPoint3x4(top_right - positionOffset) + positionOffset;
+ bottom_right = m_FXMatrix.MultiplyPoint3x4(bottom_right - positionOffset) + positionOffset;
+ }
+ #endregion
+
+
+ // Store vertex information for the character or sprite.
+ m_textInfo.characterInfo[m_characterCount].bottomLeft = bottom_left;
+ m_textInfo.characterInfo[m_characterCount].topLeft = top_left;
+ m_textInfo.characterInfo[m_characterCount].topRight = top_right;
+ m_textInfo.characterInfo[m_characterCount].bottomRight = bottom_right;
+
+ m_textInfo.characterInfo[m_characterCount].origin = m_xAdvance;
+ m_textInfo.characterInfo[m_characterCount].baseLine = fontBaseLineOffset - m_lineOffset + m_baselineOffset;
+ m_textInfo.characterInfo[m_characterCount].aspectRatio = (top_right.x - bottom_left.x) / (top_left.y - bottom_left.y);
+
+
+ // Compute and save text element Ascender and maximum line Ascender.
+ float elementAscender = m_currentFontAsset.faceInfo.ascentLine * (m_textElementType == TMP_TextElementType.Character ? currentElementScale / smallCapsMultiplier : m_textInfo.characterInfo[m_characterCount].scale) + m_baselineOffset;
+ m_textInfo.characterInfo[m_characterCount].ascender = elementAscender - m_lineOffset;
+ m_maxLineAscender = elementAscender > m_maxLineAscender ? elementAscender : m_maxLineAscender;
+
+ // Compute and save text element Descender and maximum line Descender.
+ float elementDescender = m_currentFontAsset.faceInfo.descentLine * (m_textElementType == TMP_TextElementType.Character ? currentElementScale / smallCapsMultiplier : m_textInfo.characterInfo[m_characterCount].scale) + m_baselineOffset;
+ float elementDescenderII = m_textInfo.characterInfo[m_characterCount].descender = elementDescender - m_lineOffset;
+ m_maxLineDescender = elementDescender < m_maxLineDescender ? elementDescender : m_maxLineDescender;
+
+ // Adjust maxLineAscender and maxLineDescender if style is superscript or subscript
+ if ((m_FontStyleInternal & FontStyles.Subscript) == FontStyles.Subscript || (m_FontStyleInternal & FontStyles.Superscript) == FontStyles.Superscript)
+ {
+ float baseAscender = (elementAscender - m_baselineOffset) / m_currentFontAsset.faceInfo.subscriptSize;
+ elementAscender = m_maxLineAscender;
+ m_maxLineAscender = baseAscender > m_maxLineAscender ? baseAscender : m_maxLineAscender;
+
+ float baseDescender = (elementDescender - m_baselineOffset) / m_currentFontAsset.faceInfo.subscriptSize;
+ elementDescender = m_maxLineDescender;
+ m_maxLineDescender = baseDescender < m_maxLineDescender ? baseDescender : m_maxLineDescender;
+ }
+
+ if (m_lineNumber == 0 || m_isNewPage)
+ {
+ m_maxAscender = m_maxAscender > elementAscender ? m_maxAscender : elementAscender;
+ m_maxCapHeight = Mathf.Max(m_maxCapHeight, m_currentFontAsset.faceInfo.capLine * currentElementScale / smallCapsMultiplier);
+ }
+ if (m_lineOffset == 0) pageAscender = pageAscender > elementAscender ? pageAscender : elementAscender;
+
+
+ // Set Characters to not visible by default.
+ m_textInfo.characterInfo[m_characterCount].isVisible = false;
+
+ // Setup Mesh for visible text elements. ie. not a SPACE / LINEFEED / CARRIAGE RETURN.
+ #region Handle Visible Characters
+ if (charCode == 9 || charCode == 0xA0 || charCode == 0x2007 || (!char.IsWhiteSpace((char)charCode) && charCode != 0x200B) || m_textElementType == TMP_TextElementType.Sprite)
+ {
+ m_textInfo.characterInfo[m_characterCount].isVisible = true;
+
+ #region Experimental Margin Shaper
+ //Vector2 shapedMargins;
+ //if (marginShaper)
+ //{
+ // shapedMargins = m_marginShaper.GetShapedMargins(m_textInfo.characterInfo[m_characterCount].baseLine);
+ // if (shapedMargins.x < margins.x)
+ // {
+ // shapedMargins.x = m_marginLeft;
+ // }
+ // else
+ // {
+ // shapedMargins.x += m_marginLeft - margins.x;
+ // }
+ // if (shapedMargins.y < margins.z)
+ // {
+ // shapedMargins.y = m_marginRight;
+ // }
+ // else
+ // {
+ // shapedMargins.y += m_marginRight - margins.z;
+ // }
+ //}
+ //else
+ //{
+ // shapedMargins.x = m_marginLeft;
+ // shapedMargins.y = m_marginRight;
+ //}
+ //width = marginWidth + 0.0001f - shapedMargins.x - shapedMargins.y;
+ //if (m_width != -1 && m_width < width)
+ //{
+ // width = m_width;
+ //}
+ //m_textInfo.lineInfo[m_lineNumber].marginLeft = shapedMargins.x;
+ #endregion
+
+ width = m_width != -1 ? Mathf.Min(marginWidth + 0.0001f - m_marginLeft - m_marginRight, m_width) : marginWidth + 0.0001f - m_marginLeft - m_marginRight;
+ m_textInfo.lineInfo[m_lineNumber].marginLeft = m_marginLeft;
+
+ bool isJustifiedOrFlush = ((_HorizontalAlignmentOptions)m_lineJustification & _HorizontalAlignmentOptions.Flush) == _HorizontalAlignmentOptions.Flush || ((_HorizontalAlignmentOptions)m_lineJustification & _HorizontalAlignmentOptions.Justified) == _HorizontalAlignmentOptions.Justified;
+
+ // Calculate the line breaking width of the text.
+ linebreakingWidth = Mathf.Abs(m_xAdvance) + (!m_isRightToLeft ? m_cached_TextElement.glyph.metrics.horizontalAdvance : 0) * (1 - m_charWidthAdjDelta) * (charCode != 0xAD ? currentElementScale : old_scale);
+
+ // Check if Character exceeds the width of the Text Container
+ #region Handle Line Breaking, Text Auto-Sizing and Horizontal Overflow
+ if (linebreakingWidth > width * (isJustifiedOrFlush ? 1.05f : 1.0f))
+ {
+ ellipsisIndex = m_characterCount - 1; // Last safely rendered character
+
+ // Word Wrapping
+ #region Handle Word Wrapping
+ if (enableWordWrapping && m_characterCount != m_firstCharacterOfLine)
+ {
+ // Check if word wrapping is still possible
+ #region Line Breaking Check
+ if (wrappingIndex == m_SavedWordWrapState.previous_WordBreak || isFirstWord)
+ {
+ // Word wrapping is no longer possible. Shrink size of text if auto-sizing is enabled.
+ if (m_enableAutoSizing && m_fontSize > m_fontSizeMin)
+ {
+ // Handle Character Width Adjustments
+ #region Character Width Adjustments
+ if (m_charWidthAdjDelta < m_charWidthMaxAdj / 100)
+ {
+ loopCountA = 0;
+ m_charWidthAdjDelta += 0.01f;
+ GenerateTextMesh();
+ return;
+ }
+ #endregion
+
+ // Adjust Point Size
+ m_maxFontSize = m_fontSize;
+
+ m_fontSize -= Mathf.Max((m_fontSize - m_minFontSize) / 2, 0.05f);
+ m_fontSize = (int)(Mathf.Max(m_fontSize, m_fontSizeMin) * 20 + 0.5f) / 20f;
+
+ if (loopCountA > 20) return; // Added to debug
+ GenerateTextMesh();
+ return;
+ }
+
+ // Word wrapping is no longer possible, now breaking up individual words.
+ if (m_isCharacterWrappingEnabled == false)
+ {
+ if (ignoreNonBreakingSpace == false)
+ ignoreNonBreakingSpace = true;
+ else
+ m_isCharacterWrappingEnabled = true;
+ }
+ else
+ isLastBreakingChar = true;
+
+ //m_recursiveCount += 1;
+ //if (m_recursiveCount > 20)
+ //{
+ // Debug.Log("Recursive count exceeded!");
+ // continue;
+ //}
+ }
+ #endregion
+
+ // Restore to previously stored state of last valid (space character or linefeed)
+ i = RestoreWordWrappingState(ref m_SavedWordWrapState);
+ wrappingIndex = i; // Used to detect when line length can no longer be reduced.
+
+ // Handling for Soft Hyphen
+ if (m_TextParsingBuffer[i].unicode == 0xAD) // && !m_isCharacterWrappingEnabled) // && ellipsisIndex != i && !m_isCharacterWrappingEnabled)
+ {
+ m_isTextTruncated = true;
+ m_TextParsingBuffer[i].unicode = 0x2D;
+ GenerateTextMesh();
+ return;
+ }
+
+ //Debug.Log("Last Visible Character of line # " + m_lineNumber + " is [" + m_textInfo.characterInfo[m_lastVisibleCharacterOfLine].character + " Character Count: " + m_characterCount + " Last visible: " + m_lastVisibleCharacterOfLine);
+
+ // Check if Line Spacing of previous line needs to be adjusted.
+ if (m_lineNumber > 0 && !TMP_Math.Approximately(m_maxLineAscender, m_startOfLineAscender) && m_lineHeight == TMP_Math.FLOAT_UNSET && !m_isNewPage)
+ {
+ //Debug.Log("(Line Break - Adjusting Line Spacing on line #" + m_lineNumber);
+ float offsetDelta = m_maxLineAscender - m_startOfLineAscender;
+ AdjustLineOffset(m_firstCharacterOfLine, m_characterCount, offsetDelta);
+ m_lineOffset += offsetDelta;
+ m_SavedWordWrapState.lineOffset = m_lineOffset;
+ m_SavedWordWrapState.previousLineAscender = m_maxLineAscender;
+
+ // TODO - Add check for character exceeding vertical bounds
+ }
+ m_isNewPage = false;
+
+ // Calculate lineAscender & make sure if last character is superscript or subscript that we check that as well.
+ float lineAscender = m_maxLineAscender - m_lineOffset;
+ float lineDescender = m_maxLineDescender - m_lineOffset;
+
+
+ // Update maxDescender and maxVisibleDescender
+ m_maxDescender = m_maxDescender < lineDescender ? m_maxDescender : lineDescender;
+ if (!isMaxVisibleDescenderSet)
+ maxVisibleDescender = m_maxDescender;
+
+ if (m_useMaxVisibleDescender && (m_characterCount >= m_maxVisibleCharacters || m_lineNumber >= m_maxVisibleLines))
+ isMaxVisibleDescenderSet = true;
+
+ // Track & Store lineInfo for the new line
+ m_textInfo.lineInfo[m_lineNumber].firstCharacterIndex = m_firstCharacterOfLine;
+ m_textInfo.lineInfo[m_lineNumber].firstVisibleCharacterIndex = m_firstVisibleCharacterOfLine = m_firstCharacterOfLine > m_firstVisibleCharacterOfLine ? m_firstCharacterOfLine : m_firstVisibleCharacterOfLine;
+ m_textInfo.lineInfo[m_lineNumber].lastCharacterIndex = m_lastCharacterOfLine = m_characterCount - 1 > 0 ? m_characterCount - 1 : 0;
+ m_textInfo.lineInfo[m_lineNumber].lastVisibleCharacterIndex = m_lastVisibleCharacterOfLine = m_lastVisibleCharacterOfLine < m_firstVisibleCharacterOfLine ? m_firstVisibleCharacterOfLine : m_lastVisibleCharacterOfLine;
+
+ m_textInfo.lineInfo[m_lineNumber].characterCount = m_textInfo.lineInfo[m_lineNumber].lastCharacterIndex - m_textInfo.lineInfo[m_lineNumber].firstCharacterIndex + 1;
+ m_textInfo.lineInfo[m_lineNumber].visibleCharacterCount = m_lineVisibleCharacterCount;
+ m_textInfo.lineInfo[m_lineNumber].lineExtents.min = new Vector2(m_textInfo.characterInfo[m_firstVisibleCharacterOfLine].bottomLeft.x, lineDescender);
+ m_textInfo.lineInfo[m_lineNumber].lineExtents.max = new Vector2(m_textInfo.characterInfo[m_lastVisibleCharacterOfLine].topRight.x, lineAscender);
+ m_textInfo.lineInfo[m_lineNumber].length = m_textInfo.lineInfo[m_lineNumber].lineExtents.max.x;
+ m_textInfo.lineInfo[m_lineNumber].width = width;
+
+ //m_textInfo.lineInfo[m_lineNumber].alignment = m_lineJustification;
+
+ m_textInfo.lineInfo[m_lineNumber].maxAdvance = m_textInfo.characterInfo[m_lastVisibleCharacterOfLine].xAdvance - (characterSpacingAdjustment + m_currentFontAsset.normalSpacingOffset) * currentElementScale - m_cSpacing;
+
+ m_textInfo.lineInfo[m_lineNumber].baseline = 0 - m_lineOffset;
+ m_textInfo.lineInfo[m_lineNumber].ascender = lineAscender;
+ m_textInfo.lineInfo[m_lineNumber].descender = lineDescender;
+ m_textInfo.lineInfo[m_lineNumber].lineHeight = lineAscender - lineDescender + lineGap * baseScale;
+
+ m_firstCharacterOfLine = m_characterCount; // Store first character of the next line.
+ m_lineVisibleCharacterCount = 0;
+
+ // Store the state of the line before starting on the new line.
+ SaveWordWrappingState(ref m_SavedLineState, i, m_characterCount - 1);
+
+ m_lineNumber += 1;
+ isStartOfNewLine = true;
+ isFirstWord = true;
+
+ // Check to make sure Array is large enough to hold a new line.
+ if (m_lineNumber >= m_textInfo.lineInfo.Length)
+ ResizeLineExtents(m_lineNumber);
+
+ // Apply Line Spacing based on scale of the last character of the line.
+ if (m_lineHeight == TMP_Math.FLOAT_UNSET)
+ {
+ float ascender = m_textInfo.characterInfo[m_characterCount].ascender - m_textInfo.characterInfo[m_characterCount].baseLine;
+ lineOffsetDelta = 0 - m_maxLineDescender + ascender + (lineGap + m_lineSpacing + m_lineSpacingDelta) * baseScale;
+ m_lineOffset += lineOffsetDelta;
+
+ m_startOfLineAscender = ascender;
+ }
+ else
+ m_lineOffset += m_lineHeight + m_lineSpacing * baseScale;
+
+ m_maxLineAscender = k_LargeNegativeFloat;
+ m_maxLineDescender = k_LargePositiveFloat;
+
+ m_xAdvance = 0 + tag_Indent;
+
+ continue;
+ }
+ #endregion End Word Wrapping
+
+
+ // Text Auto-Sizing (text exceeding Width of container.
+ #region Handle Text Auto-Sizing
+ if (m_enableAutoSizing && m_fontSize > m_fontSizeMin)
+ {
+ // Handle Character Width Adjustments
+ #region Character Width Adjustments
+ if (m_charWidthAdjDelta < m_charWidthMaxAdj / 100)
+ {
+ loopCountA = 0;
+ m_charWidthAdjDelta += 0.01f;
+ GenerateTextMesh();
+ return;
+ }
+ #endregion
+
+ // Adjust Point Size
+ m_maxFontSize = m_fontSize;
+
+ m_fontSize -= Mathf.Max((m_fontSize - m_minFontSize) / 2, 0.05f);
+ m_fontSize = (int)(Mathf.Max(m_fontSize, m_fontSizeMin) * 20 + 0.5f) / 20f;
+
+ //m_recursiveCount = 0;
+ if (loopCountA > 20) return; // Added to debug
+ GenerateTextMesh();
+ return;
+ }
+ #endregion End Text Auto-Sizing
+
+
+ // Handle Text Overflow
+ #region Handle Text Overflow
+ switch (m_overflowMode)
+ {
+ case TextOverflowModes.Overflow:
+ if (m_isMaskingEnabled)
+ DisableMasking();
+
+ break;
+ case TextOverflowModes.Ellipsis:
+ if (m_isMaskingEnabled)
+ DisableMasking();
+
+ m_isTextTruncated = true;
+
+ if (m_characterCount < 1)
+ {
+ m_textInfo.characterInfo[m_characterCount].isVisible = false;
+ //m_visibleCharacterCount = 0;
+ break;
+ }
+
+ m_TextParsingBuffer[i - 1].unicode = 8230;
+ m_TextParsingBuffer[i].unicode = (char)0;
+
+ if (m_cached_Ellipsis_Character != null)
+ {
+ m_textInfo.characterInfo[ellipsisIndex].character = (char)8230;
+ m_textInfo.characterInfo[ellipsisIndex].textElement = m_cached_Ellipsis_Character;
+ m_textInfo.characterInfo[ellipsisIndex].fontAsset = m_materialReferences[0].fontAsset;
+ m_textInfo.characterInfo[ellipsisIndex].material = m_materialReferences[0].material;
+ m_textInfo.characterInfo[ellipsisIndex].materialReferenceIndex = 0;
+ }
+ else
+ {
+ Debug.LogWarning("Unable to use Ellipsis character since it wasn't found in the current Font Asset [" + m_fontAsset.name + "]. Consider regenerating this font asset to include the Ellipsis character (u+2026).\nNote: Warnings can be disabled in the TMP Settings file.", this);
+ }
+
+ m_totalCharacterCount = ellipsisIndex + 1;
+
+ GenerateTextMesh();
+ return;
+ //case TextOverflowModes.Masking:
+ // if (!m_isMaskingEnabled)
+ // EnableMasking();
+ // break;
+ //case TextOverflowModes.ScrollRect:
+ // if (!m_isMaskingEnabled)
+ // EnableMasking();
+ // break;
+ case TextOverflowModes.Truncate:
+ if (m_isMaskingEnabled)
+ DisableMasking();
+
+ m_textInfo.characterInfo[m_characterCount].isVisible = false;
+ break;
+ case TextOverflowModes.Linked:
+ //m_textInfo.characterInfo[m_characterCount].isVisible = false;
+
+ //if (m_linkedTextComponent != null)
+ //{
+ // m_linkedTextComponent.text = text;
+ // m_linkedTextComponent.firstVisibleCharacter = m_characterCount;
+ // m_linkedTextComponent.ForceMeshUpdate();
+ //}
+ break;
+ }
+ #endregion End Text Overflow
+
+ }
+ #endregion End Check for Characters Exceeding Width of Text Container
+
+
+ // Special handling of characters that are not ignored at the end of a line.
+ if (charCode == 9 || charCode == 0xA0 || charCode == 0x2007)
+ {
+ m_textInfo.characterInfo[m_characterCount].isVisible = false;
+ m_lastVisibleCharacterOfLine = m_characterCount;
+ m_textInfo.lineInfo[m_lineNumber].spaceCount += 1;
+ m_textInfo.spaceCount += 1;
+
+ if (charCode == 0xA0)
+ m_textInfo.lineInfo[m_lineNumber].controlCharacterCount += 1;
+ }
+ else
+ {
+ // Determine Vertex Color
+ if (m_overrideHtmlColors)
+ vertexColor = m_fontColor32;
+ else
+ vertexColor = m_htmlColor;
+
+ // Store Character & Sprite Vertex Information
+ if (m_textElementType == TMP_TextElementType.Character)
+ {
+ // Save Character Vertex Data
+ SaveGlyphVertexInfo(padding, style_padding, vertexColor);
+ }
+ else if (m_textElementType == TMP_TextElementType.Sprite)
+ {
+ SaveSpriteVertexInfo(vertexColor);
+ }
+ }
+
+
+ // Increase visible count for Characters.
+ if (m_textInfo.characterInfo[m_characterCount].isVisible && charCode != 0xAD)
+ {
+ if (isStartOfNewLine) { isStartOfNewLine = false; m_firstVisibleCharacterOfLine = m_characterCount; }
+
+ m_lineVisibleCharacterCount += 1;
+ m_lastVisibleCharacterOfLine = m_characterCount;
+ }
+ }
+ else
+ { // This is a Space, Tab, LineFeed or Carriage Return
+
+ // Track # of spaces per line which is used for line justification.
+ if ((charCode == 10 || char.IsSeparator((char)charCode)) && charCode != 0xAD && charCode != 0x200B && charCode != 0x2060)
+ {
+ m_textInfo.lineInfo[m_lineNumber].spaceCount += 1;
+ m_textInfo.spaceCount += 1;
+ }
+ }
+ #endregion Handle Visible Characters
+
+
+ // Check if Line Spacing of previous line needs to be adjusted.
+ #region Adjust Line Spacing
+ if (m_lineNumber > 0 && !TMP_Math.Approximately(m_maxLineAscender, m_startOfLineAscender) && m_lineHeight == TMP_Math.FLOAT_UNSET && !m_isNewPage)
+ {
+ //Debug.Log("Inline - Adjusting Line Spacing on line #" + m_lineNumber);
+ //float gap = 0; // Compute gap.
+
+ float offsetDelta = m_maxLineAscender - m_startOfLineAscender;
+ AdjustLineOffset(m_firstCharacterOfLine, m_characterCount, offsetDelta);
+ elementDescenderII -= offsetDelta;
+ m_lineOffset += offsetDelta;
+
+ m_startOfLineAscender += offsetDelta;
+ m_SavedWordWrapState.lineOffset = m_lineOffset;
+ m_SavedWordWrapState.previousLineAscender = m_startOfLineAscender;
+ }
+ #endregion
+
+
+ // Store Rectangle positions for each Character.
+ #region Store Character Data
+ m_textInfo.characterInfo[m_characterCount].lineNumber = m_lineNumber;
+ m_textInfo.characterInfo[m_characterCount].pageNumber = m_pageNumber;
+
+ if (charCode != 10 && charCode != 13 && charCode != 8230 || m_textInfo.lineInfo[m_lineNumber].characterCount == 1)
+ m_textInfo.lineInfo[m_lineNumber].alignment = m_lineJustification;
+ #endregion Store Character Data
+
+
+ // Check if text Exceeds the vertical bounds of the margin area.
+ #region Check Vertical Bounds & Auto-Sizing
+ if (m_maxAscender - elementDescenderII > marginHeight + 0.0001f)
+ {
+ // Handle Line spacing adjustments
+ #region Line Spacing Adjustments
+ if (m_enableAutoSizing && m_lineSpacingDelta > m_lineSpacingMax && m_lineNumber > 0)
+ {
+ loopCountA = 0;
+
+ m_lineSpacingDelta -= 1;
+ GenerateTextMesh();
+ return;
+ }
+ #endregion
+
+
+ // Handle Text Auto-sizing resulting from text exceeding vertical bounds.
+ #region Text Auto-Sizing (Text greater than vertical bounds)
+ if (m_enableAutoSizing && m_fontSize > m_fontSizeMin)
+ {
+ m_maxFontSize = m_fontSize;
+
+ m_fontSize -= Mathf.Max((m_fontSize - m_minFontSize) / 2, 0.05f);
+ m_fontSize = (int)(Mathf.Max(m_fontSize, m_fontSizeMin) * 20 + 0.5f) / 20f;
+
+ //m_recursiveCount = 0;
+ if (loopCountA > 20) return; // Added to debug
+ GenerateTextMesh();
+ return;
+ }
+ #endregion Text Auto-Sizing
+
+ // Set isTextOverflowing and firstOverflowCharacterIndex
+ if (m_firstOverflowCharacterIndex == -1)
+ m_firstOverflowCharacterIndex = m_characterCount;
+
+ // Handle Text Overflow
+ #region Text Overflow
+ switch (m_overflowMode)
+ {
+ case TextOverflowModes.Overflow:
+ if (m_isMaskingEnabled)
+ DisableMasking();
+
+ break;
+ case TextOverflowModes.Ellipsis:
+ if (m_isMaskingEnabled)
+ DisableMasking();
+
+ if (m_lineNumber > 0)
+ {
+ m_TextParsingBuffer[m_textInfo.characterInfo[ellipsisIndex].index].unicode = 8230;
+ m_TextParsingBuffer[m_textInfo.characterInfo[ellipsisIndex].index + 1].unicode = (char)0;
+
+ if (m_cached_Ellipsis_Character != null)
+ {
+ m_textInfo.characterInfo[ellipsisIndex].character = (char)8230;
+ m_textInfo.characterInfo[ellipsisIndex].textElement = m_cached_Ellipsis_Character;
+ m_textInfo.characterInfo[ellipsisIndex].fontAsset = m_materialReferences[0].fontAsset;
+ m_textInfo.characterInfo[ellipsisIndex].material = m_materialReferences[0].material;
+ m_textInfo.characterInfo[ellipsisIndex].materialReferenceIndex = 0;
+ }
+ else
+ {
+ Debug.LogWarning("Unable to use Ellipsis character since it wasn't found in the current Font Asset [" + m_fontAsset.name + "]. Consider regenerating this font asset to include the Ellipsis character (u+2026).\nNote: Warnings can be disabled in the TMP Settings file.", this);
+ }
+
+ m_totalCharacterCount = ellipsisIndex + 1;
+
+ GenerateTextMesh();
+ m_isTextTruncated = true;
+ return;
+ }
+ else
+ {
+ ClearMesh(false);
+ return;
+ }
+ //case TextOverflowModes.Masking:
+ // if (!m_isMaskingEnabled)
+ // EnableMasking();
+ // break;
+ //case TextOverflowModes.ScrollRect:
+ // if (!m_isMaskingEnabled)
+ // EnableMasking();
+ // break;
+ case TextOverflowModes.Truncate:
+ if (m_isMaskingEnabled)
+ DisableMasking();
+
+ // TODO : Optimize
+ if (m_lineNumber > 0)
+ {
+ m_TextParsingBuffer[m_textInfo.characterInfo[ellipsisIndex].index + 1].unicode = (char)0;
+
+ m_totalCharacterCount = ellipsisIndex + 1;
+
+ GenerateTextMesh();
+ m_isTextTruncated = true;
+ return;
+ }
+ else
+ {
+ ClearMesh(false);
+ return;
+ }
+ case TextOverflowModes.Page:
+ if (m_isMaskingEnabled)
+ DisableMasking();
+
+ // Ignore Page Break, Linefeed or carriage return
+ if (charCode == 13 || charCode == 10)
+ break;
+
+ // Return if the first character doesn't fit.
+ if (i == 0)
+ {
+ ClearMesh();
+ return;
+ }
+ else if (previousPageOverflowChar == i)
+ {
+ m_TextParsingBuffer[i].unicode = 0;
+ m_isTextTruncated = true;
+ }
+
+ previousPageOverflowChar = i;
+
+ // Go back to previous line and re-layout
+ i = RestoreWordWrappingState(ref m_SavedLineState);
+
+ m_isNewPage = true;
+ m_xAdvance = 0 + tag_Indent;
+ m_lineOffset = 0;
+ m_maxAscender = 0;
+ pageAscender = 0;
+ m_lineNumber += 1;
+ m_pageNumber += 1;
+ continue;
+ case TextOverflowModes.Linked:
+ if (m_linkedTextComponent != null)
+ {
+ m_linkedTextComponent.text = text;
+ m_linkedTextComponent.firstVisibleCharacter = m_characterCount;
+ m_linkedTextComponent.ForceMeshUpdate();
+ }
+
+ // Truncate remaining text
+ if (m_lineNumber > 0)
+ {
+ m_TextParsingBuffer[i].unicode = (char)0;
+
+ m_totalCharacterCount = m_characterCount;
+
+ // TODO : Optimize as we should be able to end the layout phase here without having to do another pass.
+ GenerateTextMesh();
+ m_isTextTruncated = true;
+ return;
+ }
+ else
+ {
+ ClearMesh(true);
+ return;
+ }
+ }
+ #endregion End Text Overflow
+
+ }
+ #endregion Check Vertical Bounds
+
+
+ // Handle xAdvance & Tabulation Stops. Tab stops at every 25% of Font Size.
+ #region XAdvance, Tabulation & Stops
+ if (charCode == 9)
+ {
+ float tabSize = m_currentFontAsset.faceInfo.tabWidth * m_currentFontAsset.tabSize * currentElementScale;
+ float tabs = Mathf.Ceil(m_xAdvance / tabSize) * tabSize;
+ m_xAdvance = tabs > m_xAdvance ? tabs : m_xAdvance + tabSize;
+ }
+ else if (m_monoSpacing != 0)
+ {
+ m_xAdvance += (m_monoSpacing - monoAdvance + ((characterSpacingAdjustment + m_currentFontAsset.normalSpacingOffset) * currentElementScale) + m_cSpacing) * (1 - m_charWidthAdjDelta);
+
+ if (char.IsWhiteSpace((char)charCode) || charCode == 0x200B)
+ m_xAdvance += m_wordSpacing * currentElementScale;
+ }
+ else if (!m_isRightToLeft)
+ {
+ float scaleFXMultiplier = 1;
+ if (m_isFXMatrixSet) scaleFXMultiplier = m_FXMatrix.lossyScale.x;
+
+ m_xAdvance += ((m_cached_TextElement.glyph.metrics.horizontalAdvance * scaleFXMultiplier * bold_xAdvance_multiplier + characterSpacingAdjustment + m_currentFontAsset.normalSpacingOffset + glyphAdjustments.xAdvance) * currentElementScale + m_cSpacing) * (1 - m_charWidthAdjDelta);
+
+ if (char.IsWhiteSpace((char)charCode) || charCode == 0x200B)
+ m_xAdvance += m_wordSpacing * currentElementScale;
+ }
+ else
+ {
+ m_xAdvance -= glyphAdjustments.xAdvance * currentElementScale;
+ }
+
+
+ // Store xAdvance information
+ m_textInfo.characterInfo[m_characterCount].xAdvance = m_xAdvance;
+
+ #endregion Tabulation & Stops
+
+
+ // Handle Carriage Return
+ #region Carriage Return
+ if (charCode == 13)
+ {
+ m_xAdvance = 0 + tag_Indent;
+ }
+ #endregion Carriage Return
+
+
+ // Handle Line Spacing Adjustments + Word Wrapping & special case for last line.
+ #region Check for Line Feed and Last Character
+ if (charCode == 10 || m_characterCount == totalCharacterCount - 1)
+ {
+ // Check if Line Spacing of previous line needs to be adjusted.
+ if (m_lineNumber > 0 && !TMP_Math.Approximately(m_maxLineAscender, m_startOfLineAscender) && m_lineHeight == TMP_Math.FLOAT_UNSET && !m_isNewPage)
+ {
+ //Debug.Log("Line Feed - Adjusting Line Spacing on line #" + m_lineNumber);
+ float offsetDelta = m_maxLineAscender - m_startOfLineAscender;
+ AdjustLineOffset(m_firstCharacterOfLine, m_characterCount, offsetDelta);
+ elementDescenderII -= offsetDelta;
+ m_lineOffset += offsetDelta;
+ }
+ m_isNewPage = false;
+
+ // Calculate lineAscender & make sure if last character is superscript or subscript that we check that as well.
+ float lineAscender = m_maxLineAscender - m_lineOffset;
+ float lineDescender = m_maxLineDescender - m_lineOffset;
+
+ // Update maxDescender and maxVisibleDescender
+ m_maxDescender = m_maxDescender < lineDescender ? m_maxDescender : lineDescender;
+ if (!isMaxVisibleDescenderSet)
+ maxVisibleDescender = m_maxDescender;
+
+ if (m_useMaxVisibleDescender && (m_characterCount >= m_maxVisibleCharacters || m_lineNumber >= m_maxVisibleLines))
+ isMaxVisibleDescenderSet = true;
+
+ // Save Line Information
+ m_textInfo.lineInfo[m_lineNumber].firstCharacterIndex = m_firstCharacterOfLine;
+ m_textInfo.lineInfo[m_lineNumber].firstVisibleCharacterIndex = m_firstVisibleCharacterOfLine = m_firstCharacterOfLine > m_firstVisibleCharacterOfLine ? m_firstCharacterOfLine : m_firstVisibleCharacterOfLine;
+ m_textInfo.lineInfo[m_lineNumber].lastCharacterIndex = m_lastCharacterOfLine = m_characterCount;
+ m_textInfo.lineInfo[m_lineNumber].lastVisibleCharacterIndex = m_lastVisibleCharacterOfLine = m_lastVisibleCharacterOfLine < m_firstVisibleCharacterOfLine ? m_firstVisibleCharacterOfLine : m_lastVisibleCharacterOfLine;
+
+ m_textInfo.lineInfo[m_lineNumber].characterCount = m_textInfo.lineInfo[m_lineNumber].lastCharacterIndex - m_textInfo.lineInfo[m_lineNumber].firstCharacterIndex + 1;
+ m_textInfo.lineInfo[m_lineNumber].visibleCharacterCount = m_lineVisibleCharacterCount;
+ m_textInfo.lineInfo[m_lineNumber].lineExtents.min = new Vector2(m_textInfo.characterInfo[m_firstVisibleCharacterOfLine].bottomLeft.x, lineDescender);
+ m_textInfo.lineInfo[m_lineNumber].lineExtents.max = new Vector2(m_textInfo.characterInfo[m_lastVisibleCharacterOfLine].topRight.x, lineAscender);
+ m_textInfo.lineInfo[m_lineNumber].length = m_textInfo.lineInfo[m_lineNumber].lineExtents.max.x - (padding * currentElementScale);
+ m_textInfo.lineInfo[m_lineNumber].width = width;
+
+ if (m_textInfo.lineInfo[m_lineNumber].characterCount == 1)
+ m_textInfo.lineInfo[m_lineNumber].alignment = m_lineJustification;
+
+ if (m_textInfo.characterInfo[m_lastVisibleCharacterOfLine].isVisible)
+ m_textInfo.lineInfo[m_lineNumber].maxAdvance = m_textInfo.characterInfo[m_lastVisibleCharacterOfLine].xAdvance - (characterSpacingAdjustment + m_currentFontAsset.normalSpacingOffset) * currentElementScale - m_cSpacing;
+ else
+ m_textInfo.lineInfo[m_lineNumber].maxAdvance = m_textInfo.characterInfo[m_lastCharacterOfLine].xAdvance - (characterSpacingAdjustment + m_currentFontAsset.normalSpacingOffset) * currentElementScale - m_cSpacing;
+
+ m_textInfo.lineInfo[m_lineNumber].baseline = 0 - m_lineOffset;
+ m_textInfo.lineInfo[m_lineNumber].ascender = lineAscender;
+ m_textInfo.lineInfo[m_lineNumber].descender = lineDescender;
+ m_textInfo.lineInfo[m_lineNumber].lineHeight = lineAscender - lineDescender + lineGap * baseScale;
+
+ m_firstCharacterOfLine = m_characterCount + 1;
+ m_lineVisibleCharacterCount = 0;
+
+ // Add new line if not last line or character.
+ if (charCode == 10)
+ {
+ // Store the state of the line before starting on the new line.
+ SaveWordWrappingState(ref m_SavedLineState, i, m_characterCount);
+ // Store the state of the last Character before the new line.
+ SaveWordWrappingState(ref m_SavedWordWrapState, i, m_characterCount);
+
+ m_lineNumber += 1;
+ isStartOfNewLine = true;
+ ignoreNonBreakingSpace = false;
+ isFirstWord = true;
+
+ // Check to make sure Array is large enough to hold a new line.
+ if (m_lineNumber >= m_textInfo.lineInfo.Length)
+ ResizeLineExtents(m_lineNumber);
+
+ // Apply Line Spacing
+ if (m_lineHeight == TMP_Math.FLOAT_UNSET)
+ {
+ lineOffsetDelta = 0 - m_maxLineDescender + elementAscender + (lineGap + m_lineSpacing + m_paragraphSpacing + m_lineSpacingDelta) * baseScale;
+ m_lineOffset += lineOffsetDelta;
+ }
+ else
+ m_lineOffset += m_lineHeight + (m_lineSpacing + m_paragraphSpacing) * baseScale;
+
+ m_maxLineAscender = k_LargeNegativeFloat;
+ m_maxLineDescender = k_LargePositiveFloat;
+ m_startOfLineAscender = elementAscender;
+
+ m_xAdvance = 0 + tag_LineIndent + tag_Indent;
+
+ ellipsisIndex = m_characterCount - 1;
+
+ m_characterCount += 1;
+ continue;
+ }
+ }
+ #endregion Check for Linefeed or Last Character
+
+
+ // Store Rectangle positions for each Character.
+ #region Save CharacterInfo for the current character.
+ // Determine the bounds of the Mesh.
+ if (m_textInfo.characterInfo[m_characterCount].isVisible)
+ {
+ m_meshExtents.min.x = Mathf.Min(m_meshExtents.min.x, m_textInfo.characterInfo[m_characterCount].bottomLeft.x);
+ m_meshExtents.min.y = Mathf.Min(m_meshExtents.min.y, m_textInfo.characterInfo[m_characterCount].bottomLeft.y);
+
+ m_meshExtents.max.x = Mathf.Max(m_meshExtents.max.x, m_textInfo.characterInfo[m_characterCount].topRight.x);
+ m_meshExtents.max.y = Mathf.Max(m_meshExtents.max.y, m_textInfo.characterInfo[m_characterCount].topRight.y);
+
+ //m_meshExtents.min = new Vector2(Mathf.Min(m_meshExtents.min.x, m_textInfo.characterInfo[m_characterCount].bottomLeft.x), Mathf.Min(m_meshExtents.min.y, m_textInfo.characterInfo[m_characterCount].bottomLeft.y));
+ //m_meshExtents.max = new Vector2(Mathf.Max(m_meshExtents.max.x, m_textInfo.characterInfo[m_characterCount].topRight.x), Mathf.Max(m_meshExtents.max.y, m_textInfo.characterInfo[m_characterCount].topRight.y));
+ }
+
+
+ // Save pageInfo Data
+ if (m_overflowMode == TextOverflowModes.Page && charCode != 13 && charCode != 10) // && m_pageNumber < 16)
+ {
+ // Check if we need to increase allocations for the pageInfo array.
+ if (m_pageNumber + 1 > m_textInfo.pageInfo.Length)
+ TMP_TextInfo.Resize(ref m_textInfo.pageInfo, m_pageNumber + 1, true);
+
+ m_textInfo.pageInfo[m_pageNumber].ascender = pageAscender;
+ m_textInfo.pageInfo[m_pageNumber].descender = elementDescender < m_textInfo.pageInfo[m_pageNumber].descender ? elementDescender : m_textInfo.pageInfo[m_pageNumber].descender;
+
+ if (m_pageNumber == 0 && m_characterCount == 0)
+ m_textInfo.pageInfo[m_pageNumber].firstCharacterIndex = m_characterCount;
+ else if (m_characterCount > 0 && m_pageNumber != m_textInfo.characterInfo[m_characterCount - 1].pageNumber)
+ {
+ m_textInfo.pageInfo[m_pageNumber - 1].lastCharacterIndex = m_characterCount - 1;
+ m_textInfo.pageInfo[m_pageNumber].firstCharacterIndex = m_characterCount;
+ }
+ else if (m_characterCount == totalCharacterCount - 1)
+ m_textInfo.pageInfo[m_pageNumber].lastCharacterIndex = m_characterCount;
+ }
+ #endregion Saving CharacterInfo
+
+
+ // Save State of Mesh Creation for handling of Word Wrapping
+ #region Save Word Wrapping State
+ if (m_enableWordWrapping || m_overflowMode == TextOverflowModes.Truncate || m_overflowMode == TextOverflowModes.Ellipsis)
+ {
+ if ((char.IsWhiteSpace((char)charCode) || charCode == 0x200B || charCode == 0x2D || charCode == 0xAD) && (!m_isNonBreakingSpace || ignoreNonBreakingSpace) && charCode != 0xA0 && charCode != 0x2007 && charCode != 0x2011 && charCode != 0x202F && charCode != 0x2060)
+ {
+ // We store the state of numerous variables for the most recent Space, LineFeed or Carriage Return to enable them to be restored
+ // for Word Wrapping.
+ SaveWordWrappingState(ref m_SavedWordWrapState, i, m_characterCount);
+ m_isCharacterWrappingEnabled = false;
+ isFirstWord = false;
+ }
+ // Handling for East Asian languages
+ else if (( charCode > 0x1100 && charCode < 0x11ff || /* Hangul Jamo */
+ charCode > 0x2E80 && charCode < 0x9FFF || /* CJK */
+ charCode > 0xA960 && charCode < 0xA97F || /* Hangul Jame Extended-A */
+ charCode > 0xAC00 && charCode < 0xD7FF || /* Hangul Syllables */
+ charCode > 0xF900 && charCode < 0xFAFF || /* CJK Compatibility Ideographs */
+ charCode > 0xFE30 && charCode < 0xFE4F || /* CJK Compatibility Forms */
+ charCode > 0xFF00 && charCode < 0xFFEF) /* CJK Halfwidth */
+ && !m_isNonBreakingSpace)
+ {
+ if (isFirstWord || isLastBreakingChar || TMP_Settings.linebreakingRules.leadingCharacters.ContainsKey(charCode) == false &&
+ (m_characterCount < totalCharacterCount - 1 &&
+ TMP_Settings.linebreakingRules.followingCharacters.ContainsKey(m_textInfo.characterInfo[m_characterCount + 1].character) == false))
+ {
+ SaveWordWrappingState(ref m_SavedWordWrapState, i, m_characterCount);
+ m_isCharacterWrappingEnabled = false;
+ isFirstWord = false;
+ }
+ }
+ else if ((isFirstWord || m_isCharacterWrappingEnabled == true || isLastBreakingChar))
+ SaveWordWrappingState(ref m_SavedWordWrapState, i, m_characterCount);
+ }
+ #endregion Save Word Wrapping State
+
+ m_characterCount += 1;
+ }
+
+ // Check Auto Sizing and increase font size to fill text container.
+ #region Check Auto-Sizing (Upper Font Size Bounds)
+ fontSizeDelta = m_maxFontSize - m_minFontSize;
+ if (!m_isCharacterWrappingEnabled && m_enableAutoSizing && fontSizeDelta > 0.051f && m_fontSize < m_fontSizeMax)
+ {
+ m_minFontSize = m_fontSize;
+ m_fontSize += Mathf.Max((m_maxFontSize - m_fontSize) / 2, 0.05f);
+ m_fontSize = (int)(Mathf.Min(m_fontSize, m_fontSizeMax) * 20 + 0.5f) / 20f;
+
+ //Debug.Log(m_fontSize);
+
+ if (loopCountA > 20) return; // Added to debug
+ GenerateTextMesh();
+ return;
+ }
+ #endregion End Auto-sizing Check
+
+
+ m_isCharacterWrappingEnabled = false;
+
+
+ //Debug.Log("Iteration Count: " + loopCountA + ". Final Point Size: " + m_fontSize); // + " B: " + loopCountB + " C: " + loopCountC + " D: " + loopCountD);
+
+
+
+ // If there are no visible characters... no need to continue
+ if (m_characterCount == 0) // && m_visibleSpriteCount == 0)
+ {
+ ClearMesh(true);
+
+ // Event indicating the text has been regenerated.
+ TMPro_EventManager.ON_TEXT_CHANGED(this);
+ return;
+ }
+
+
+ // *** PHASE II of Text Generation ***
+ int last_vert_index = m_materialReferences[0].referenceCount * (!m_isVolumetricText ? 4 : 8);
+
+ // Partial clear of the vertices array to mark unused vertices as degenerate.
+ m_textInfo.meshInfo[0].Clear(false);
+
+ // Handle Text Alignment
+ #region Text Vertical Alignment
+ Vector3 anchorOffset = Vector3.zero;
+ Vector3[] corners = m_RectTransformCorners; // GetTextContainerLocalCorners();
+
+ // Handle Vertical Text Alignment
+ switch (m_textAlignment)
+ {
+ // Top Vertically
+ case TextAlignmentOptions.Top:
+ case TextAlignmentOptions.TopLeft:
+ case TextAlignmentOptions.TopRight:
+ case TextAlignmentOptions.TopJustified:
+ case TextAlignmentOptions.TopFlush:
+ case TextAlignmentOptions.TopGeoAligned:
+ if (m_overflowMode != TextOverflowModes.Page)
+ anchorOffset = corners[1] + new Vector3(0 + margins.x, 0 - m_maxAscender - margins.y, 0);
+ else
+ anchorOffset = corners[1] + new Vector3(0 + margins.x, 0 - m_textInfo.pageInfo[pageToDisplay].ascender - margins.y, 0);
+ break;
+
+ // Middle Vertically
+ case TextAlignmentOptions.Left:
+ case TextAlignmentOptions.Right:
+ case TextAlignmentOptions.Center:
+ case TextAlignmentOptions.Justified:
+ case TextAlignmentOptions.Flush:
+ case TextAlignmentOptions.CenterGeoAligned:
+ if (m_overflowMode != TextOverflowModes.Page)
+ anchorOffset = (corners[0] + corners[1]) / 2 + new Vector3(0 + margins.x, 0 - (m_maxAscender + margins.y + maxVisibleDescender - margins.w) / 2, 0);
+ else
+ anchorOffset = (corners[0] + corners[1]) / 2 + new Vector3(0 + margins.x, 0 - (m_textInfo.pageInfo[pageToDisplay].ascender + margins.y + m_textInfo.pageInfo[pageToDisplay].descender - margins.w) / 2, 0);
+ break;
+
+ // Bottom Vertically
+ case TextAlignmentOptions.Bottom:
+ case TextAlignmentOptions.BottomLeft:
+ case TextAlignmentOptions.BottomRight:
+ case TextAlignmentOptions.BottomJustified:
+ case TextAlignmentOptions.BottomFlush:
+ case TextAlignmentOptions.BottomGeoAligned:
+ if (m_overflowMode != TextOverflowModes.Page)
+ anchorOffset = corners[0] + new Vector3(0 + margins.x, 0 - maxVisibleDescender + margins.w, 0);
+ else
+ anchorOffset = corners[0] + new Vector3(0 + margins.x, 0 - m_textInfo.pageInfo[pageToDisplay].descender + margins.w, 0);
+ break;
+
+ // Baseline Vertically
+ case TextAlignmentOptions.Baseline:
+ case TextAlignmentOptions.BaselineLeft:
+ case TextAlignmentOptions.BaselineRight:
+ case TextAlignmentOptions.BaselineJustified:
+ case TextAlignmentOptions.BaselineFlush:
+ case TextAlignmentOptions.BaselineGeoAligned:
+ anchorOffset = (corners[0] + corners[1]) / 2 + new Vector3(0 + margins.x, 0, 0);
+ break;
+
+ // Midline Vertically
+ case TextAlignmentOptions.MidlineLeft:
+ case TextAlignmentOptions.Midline:
+ case TextAlignmentOptions.MidlineRight:
+ case TextAlignmentOptions.MidlineJustified:
+ case TextAlignmentOptions.MidlineFlush:
+ case TextAlignmentOptions.MidlineGeoAligned:
+ anchorOffset = (corners[0] + corners[1]) / 2 + new Vector3(0 + margins.x, 0 - (m_meshExtents.max.y + margins.y + m_meshExtents.min.y - margins.w) / 2, 0);
+ break;
+
+ // Capline Vertically
+ case TextAlignmentOptions.CaplineLeft:
+ case TextAlignmentOptions.Capline:
+ case TextAlignmentOptions.CaplineRight:
+ case TextAlignmentOptions.CaplineJustified:
+ case TextAlignmentOptions.CaplineFlush:
+ case TextAlignmentOptions.CaplineGeoAligned:
+ anchorOffset = (corners[0] + corners[1]) / 2 + new Vector3(0 + margins.x, 0 - (m_maxCapHeight - margins.y - margins.w) / 2, 0);
+ break;
+ }
+ #endregion
+
+
+ // Initialization for Second Pass
+ Vector3 justificationOffset = Vector3.zero;
+ Vector3 offset = Vector3.zero;
+ int vert_index_X4 = 0;
+ int sprite_index_X4 = 0;
+
+ int wordCount = 0;
+ int lineCount = 0;
+ int lastLine = 0;
+ bool isFirstSeperator = false;
+
+ bool isStartOfWord = false;
+ int wordFirstChar = 0;
+ int wordLastChar = 0;
+
+ // Second Pass : Line Justification, UV Mapping, Character & Line Visibility & more.
+ float lossyScale = m_previousLossyScaleY = this.transform.lossyScale.y;
+
+ Color32 underlineColor = Color.white;
+ Color32 strikethroughColor = Color.white;
+ Color32 highlightColor = new Color32(255, 255, 0, 64);
+ float xScale = 0;
+ float xScaleMax = 0;
+ float underlineStartScale = 0;
+ float underlineEndScale = 0;
+ float underlineMaxScale = 0;
+ float underlineBaseLine = k_LargePositiveFloat;
+ int lastPage = 0;
+
+ float strikethroughPointSize = 0;
+ float strikethroughScale = 0;
+ float strikethroughBaseline = 0;
+
+ TMP_CharacterInfo[] characterInfos = m_textInfo.characterInfo;
+ #region Handle Line Justification & UV Mapping & Character Visibility & More
+ for (int i = 0; i < m_characterCount; i++)
+ {
+ TMP_FontAsset currentFontAsset = characterInfos[i].fontAsset;
+
+ char currentCharacter = characterInfos[i].character;
+
+ int currentLine = characterInfos[i].lineNumber;
+ TMP_LineInfo lineInfo = m_textInfo.lineInfo[currentLine];
+ lineCount = currentLine + 1;
+
+ TextAlignmentOptions lineAlignment = lineInfo.alignment;
+
+ // Process Line Justification
+ #region Handle Line Justification
+ switch (lineAlignment)
+ {
+ case TextAlignmentOptions.TopLeft:
+ case TextAlignmentOptions.Left:
+ case TextAlignmentOptions.BottomLeft:
+ case TextAlignmentOptions.BaselineLeft:
+ case TextAlignmentOptions.MidlineLeft:
+ case TextAlignmentOptions.CaplineLeft:
+ if (!m_isRightToLeft)
+ justificationOffset = new Vector3(0 + lineInfo.marginLeft, 0, 0);
+ else
+ justificationOffset = new Vector3(0 - lineInfo.maxAdvance, 0, 0);
+ break;
+
+ case TextAlignmentOptions.Top:
+ case TextAlignmentOptions.Center:
+ case TextAlignmentOptions.Bottom:
+ case TextAlignmentOptions.Baseline:
+ case TextAlignmentOptions.Midline:
+ case TextAlignmentOptions.Capline:
+ justificationOffset = new Vector3(lineInfo.marginLeft + lineInfo.width / 2 - lineInfo.maxAdvance / 2, 0, 0);
+ break;
+
+ case TextAlignmentOptions.TopGeoAligned:
+ case TextAlignmentOptions.CenterGeoAligned:
+ case TextAlignmentOptions.BottomGeoAligned:
+ case TextAlignmentOptions.BaselineGeoAligned:
+ case TextAlignmentOptions.MidlineGeoAligned:
+ case TextAlignmentOptions.CaplineGeoAligned:
+ justificationOffset = new Vector3(lineInfo.marginLeft + lineInfo.width / 2 - (lineInfo.lineExtents.min.x + lineInfo.lineExtents.max.x) / 2, 0, 0);
+ break;
+
+ case TextAlignmentOptions.TopRight:
+ case TextAlignmentOptions.Right:
+ case TextAlignmentOptions.BottomRight:
+ case TextAlignmentOptions.BaselineRight:
+ case TextAlignmentOptions.MidlineRight:
+ case TextAlignmentOptions.CaplineRight:
+ if (!m_isRightToLeft)
+ justificationOffset = new Vector3(lineInfo.marginLeft + lineInfo.width - lineInfo.maxAdvance, 0, 0);
+ else
+ justificationOffset = new Vector3(lineInfo.marginLeft + lineInfo.width, 0, 0);
+ break;
+
+ case TextAlignmentOptions.TopJustified:
+ case TextAlignmentOptions.Justified:
+ case TextAlignmentOptions.BottomJustified:
+ case TextAlignmentOptions.BaselineJustified:
+ case TextAlignmentOptions.MidlineJustified:
+ case TextAlignmentOptions.CaplineJustified:
+ case TextAlignmentOptions.TopFlush:
+ case TextAlignmentOptions.Flush:
+ case TextAlignmentOptions.BottomFlush:
+ case TextAlignmentOptions.BaselineFlush:
+ case TextAlignmentOptions.MidlineFlush:
+ case TextAlignmentOptions.CaplineFlush:
+ // Skip Zero Width Characters
+ if (currentCharacter == 0xAD || currentCharacter == 0x200B || currentCharacter == 0x2060) break;
+
+ char lastCharOfCurrentLine = characterInfos[lineInfo.lastCharacterIndex].character;
+ bool isFlush = ((_HorizontalAlignmentOptions)lineAlignment & _HorizontalAlignmentOptions.Flush) == _HorizontalAlignmentOptions.Flush;
+
+ // In Justified mode, all lines are justified except the last one.
+ // In Flush mode, all lines are justified.
+ if (char.IsControl(lastCharOfCurrentLine) == false && currentLine < m_lineNumber || isFlush || lineInfo.maxAdvance > lineInfo.width)
+ {
+ // First character of each line.
+ if (currentLine != lastLine || i == 0 || i == m_firstVisibleCharacter)
+ {
+ if (!m_isRightToLeft)
+ justificationOffset = new Vector3(lineInfo.marginLeft, 0, 0);
+ else
+ justificationOffset = new Vector3(lineInfo.marginLeft + lineInfo.width, 0, 0);
+
+ if (char.IsSeparator(currentCharacter))
+ isFirstSeperator = true;
+ else
+ isFirstSeperator = false;
+ }
+ else
+ {
+ float gap = !m_isRightToLeft ? lineInfo.width - lineInfo.maxAdvance : lineInfo.width + lineInfo.maxAdvance;
+
+ int visibleCount = lineInfo.visibleCharacterCount - 1 + lineInfo.controlCharacterCount;
+
+ // Get the number of spaces for each line ignoring the last character if it is not visible (ie. a space or linefeed).
+ int spaces = (characterInfos[lineInfo.lastCharacterIndex].isVisible ? lineInfo.spaceCount : lineInfo.spaceCount - 1) - lineInfo.controlCharacterCount;
+
+ if (isFirstSeperator) { spaces -= 1; visibleCount += 1; }
+
+ float ratio = spaces > 0 ? m_wordWrappingRatios : 1;
+
+ if (spaces < 1) spaces = 1;
+
+ if (currentCharacter != 0xA0 && (currentCharacter == 9 || char.IsSeparator((char)currentCharacter)))
+ {
+ if (!m_isRightToLeft)
+ justificationOffset += new Vector3(gap * (1 - ratio) / spaces, 0, 0);
+ else
+ justificationOffset -= new Vector3(gap * (1 - ratio) / spaces, 0, 0);
+ }
+ else
+ {
+ if (!m_isRightToLeft)
+ justificationOffset += new Vector3(gap * ratio / visibleCount, 0, 0);
+ else
+ justificationOffset -= new Vector3(gap * ratio / visibleCount, 0, 0);
+ }
+ }
+ }
+ else
+ {
+ if (!m_isRightToLeft)
+ justificationOffset = new Vector3(lineInfo.marginLeft, 0, 0); // Keep last line left justified.
+ else
+ justificationOffset = new Vector3(lineInfo.marginLeft + lineInfo.width, 0, 0); // Keep last line right justified.
+ }
+ //Debug.Log("Char [" + (char)charCode + "] Code:" + charCode + " Line # " + currentLine + " Offset:" + justificationOffset + " # Spaces:" + lineInfo.spaceCount + " # Characters:" + lineInfo.characterCount);
+ break;
+ }
+ #endregion End Text Justification
+
+ offset = anchorOffset + justificationOffset;
+
+ // Handle UV2 mapping options and packing of scale information into UV2.
+ #region Handling of UV2 mapping & Scale packing
+ bool isCharacterVisible = characterInfos[i].isVisible;
+ if (isCharacterVisible)
+ {
+ TMP_TextElementType elementType = characterInfos[i].elementType;
+ switch (elementType)
+ {
+ // CHARACTERS
+ case TMP_TextElementType.Character:
+ Extents lineExtents = lineInfo.lineExtents;
+ float uvOffset = (m_uvLineOffset * currentLine) % 1; // + m_uvOffset.x;
+
+ // Setup UV2 based on Character Mapping Options Selected
+ #region Handle UV Mapping Options
+ switch (m_horizontalMapping)
+ {
+ case TextureMappingOptions.Character:
+ characterInfos[i].vertex_BL.uv2.x = 0; //+ m_uvOffset.x;
+ characterInfos[i].vertex_TL.uv2.x = 0; //+ m_uvOffset.x;
+ characterInfos[i].vertex_TR.uv2.x = 1; //+ m_uvOffset.x;
+ characterInfos[i].vertex_BR.uv2.x = 1; //+ m_uvOffset.x;
+ break;
+
+ case TextureMappingOptions.Line:
+ if (m_textAlignment != TextAlignmentOptions.Justified)
+ {
+ characterInfos[i].vertex_BL.uv2.x = (characterInfos[i].vertex_BL.position.x - lineExtents.min.x) / (lineExtents.max.x - lineExtents.min.x) + uvOffset;
+ characterInfos[i].vertex_TL.uv2.x = (characterInfos[i].vertex_TL.position.x - lineExtents.min.x) / (lineExtents.max.x - lineExtents.min.x) + uvOffset;
+ characterInfos[i].vertex_TR.uv2.x = (characterInfos[i].vertex_TR.position.x - lineExtents.min.x) / (lineExtents.max.x - lineExtents.min.x) + uvOffset;
+ characterInfos[i].vertex_BR.uv2.x = (characterInfos[i].vertex_BR.position.x - lineExtents.min.x) / (lineExtents.max.x - lineExtents.min.x) + uvOffset;
+ break;
+ }
+ else // Special Case if Justified is used in Line Mode.
+ {
+ characterInfos[i].vertex_BL.uv2.x = (characterInfos[i].vertex_BL.position.x + justificationOffset.x - m_meshExtents.min.x) / (m_meshExtents.max.x - m_meshExtents.min.x) + uvOffset;
+ characterInfos[i].vertex_TL.uv2.x = (characterInfos[i].vertex_TL.position.x + justificationOffset.x - m_meshExtents.min.x) / (m_meshExtents.max.x - m_meshExtents.min.x) + uvOffset;
+ characterInfos[i].vertex_TR.uv2.x = (characterInfos[i].vertex_TR.position.x + justificationOffset.x - m_meshExtents.min.x) / (m_meshExtents.max.x - m_meshExtents.min.x) + uvOffset;
+ characterInfos[i].vertex_BR.uv2.x = (characterInfos[i].vertex_BR.position.x + justificationOffset.x - m_meshExtents.min.x) / (m_meshExtents.max.x - m_meshExtents.min.x) + uvOffset;
+ break;
+ }
+
+ case TextureMappingOptions.Paragraph:
+ characterInfos[i].vertex_BL.uv2.x = (characterInfos[i].vertex_BL.position.x + justificationOffset.x - m_meshExtents.min.x) / (m_meshExtents.max.x - m_meshExtents.min.x) + uvOffset;
+ characterInfos[i].vertex_TL.uv2.x = (characterInfos[i].vertex_TL.position.x + justificationOffset.x - m_meshExtents.min.x) / (m_meshExtents.max.x - m_meshExtents.min.x) + uvOffset;
+ characterInfos[i].vertex_TR.uv2.x = (characterInfos[i].vertex_TR.position.x + justificationOffset.x - m_meshExtents.min.x) / (m_meshExtents.max.x - m_meshExtents.min.x) + uvOffset;
+ characterInfos[i].vertex_BR.uv2.x = (characterInfos[i].vertex_BR.position.x + justificationOffset.x - m_meshExtents.min.x) / (m_meshExtents.max.x - m_meshExtents.min.x) + uvOffset;
+ break;
+
+ case TextureMappingOptions.MatchAspect:
+
+ switch (m_verticalMapping)
+ {
+ case TextureMappingOptions.Character:
+ characterInfos[i].vertex_BL.uv2.y = 0; //+ m_uvOffset.y;
+ characterInfos[i].vertex_TL.uv2.y = 1; //+ m_uvOffset.y;
+ characterInfos[i].vertex_TR.uv2.y = 0; //+ m_uvOffset.y;
+ characterInfos[i].vertex_BR.uv2.y = 1; //+ m_uvOffset.y;
+ break;
+
+ case TextureMappingOptions.Line:
+ characterInfos[i].vertex_BL.uv2.y = (characterInfos[i].vertex_BL.position.y - lineExtents.min.y) / (lineExtents.max.y - lineExtents.min.y) + uvOffset;
+ characterInfos[i].vertex_TL.uv2.y = (characterInfos[i].vertex_TL.position.y - lineExtents.min.y) / (lineExtents.max.y - lineExtents.min.y) + uvOffset;
+ characterInfos[i].vertex_TR.uv2.y = characterInfos[i].vertex_BL.uv2.y;
+ characterInfos[i].vertex_BR.uv2.y = characterInfos[i].vertex_TL.uv2.y;
+ break;
+
+ case TextureMappingOptions.Paragraph:
+ characterInfos[i].vertex_BL.uv2.y = (characterInfos[i].vertex_BL.position.y - m_meshExtents.min.y) / (m_meshExtents.max.y - m_meshExtents.min.y) + uvOffset;
+ characterInfos[i].vertex_TL.uv2.y = (characterInfos[i].vertex_TL.position.y - m_meshExtents.min.y) / (m_meshExtents.max.y - m_meshExtents.min.y) + uvOffset;
+ characterInfos[i].vertex_TR.uv2.y = characterInfos[i].vertex_BL.uv2.y;
+ characterInfos[i].vertex_BR.uv2.y = characterInfos[i].vertex_TL.uv2.y;
+ break;
+
+ case TextureMappingOptions.MatchAspect:
+ Debug.Log("ERROR: Cannot Match both Vertical & Horizontal.");
+ break;
+ }
+
+ //float xDelta = 1 - (_uv2s[vert_index + 0].y * textMeshCharacterInfo[i].AspectRatio); // Left aligned
+ float xDelta = (1 - ((characterInfos[i].vertex_BL.uv2.y + characterInfos[i].vertex_TL.uv2.y) * characterInfos[i].aspectRatio)) / 2; // Center of Rectangle
+
+ characterInfos[i].vertex_BL.uv2.x = (characterInfos[i].vertex_BL.uv2.y * characterInfos[i].aspectRatio) + xDelta + uvOffset;
+ characterInfos[i].vertex_TL.uv2.x = characterInfos[i].vertex_BL.uv2.x;
+ characterInfos[i].vertex_TR.uv2.x = (characterInfos[i].vertex_TL.uv2.y * characterInfos[i].aspectRatio) + xDelta + uvOffset;
+ characterInfos[i].vertex_BR.uv2.x = characterInfos[i].vertex_TR.uv2.x;
+ break;
+ }
+
+ switch (m_verticalMapping)
+ {
+ case TextureMappingOptions.Character:
+ characterInfos[i].vertex_BL.uv2.y = 0; //+ m_uvOffset.y;
+ characterInfos[i].vertex_TL.uv2.y = 1; //+ m_uvOffset.y;
+ characterInfos[i].vertex_TR.uv2.y = 1; //+ m_uvOffset.y;
+ characterInfos[i].vertex_BR.uv2.y = 0; //+ m_uvOffset.y;
+ break;
+
+ case TextureMappingOptions.Line:
+ characterInfos[i].vertex_BL.uv2.y = (characterInfos[i].vertex_BL.position.y - lineInfo.descender) / (lineInfo.ascender - lineInfo.descender); // + m_uvOffset.y;
+ characterInfos[i].vertex_TL.uv2.y = (characterInfos[i].vertex_TL.position.y - lineInfo.descender) / (lineInfo.ascender - lineInfo.descender); // + m_uvOffset.y;
+ characterInfos[i].vertex_TR.uv2.y = characterInfos[i].vertex_TL.uv2.y;
+ characterInfos[i].vertex_BR.uv2.y = characterInfos[i].vertex_BL.uv2.y;
+ break;
+
+ case TextureMappingOptions.Paragraph:
+ characterInfos[i].vertex_BL.uv2.y = (characterInfos[i].vertex_BL.position.y - m_meshExtents.min.y) / (m_meshExtents.max.y - m_meshExtents.min.y); // + m_uvOffset.y;
+ characterInfos[i].vertex_TL.uv2.y = (characterInfos[i].vertex_TL.position.y - m_meshExtents.min.y) / (m_meshExtents.max.y - m_meshExtents.min.y); // + m_uvOffset.y;
+ characterInfos[i].vertex_TR.uv2.y = characterInfos[i].vertex_TL.uv2.y;
+ characterInfos[i].vertex_BR.uv2.y = characterInfos[i].vertex_BL.uv2.y;
+ break;
+
+ case TextureMappingOptions.MatchAspect:
+ float yDelta = (1 - ((characterInfos[i].vertex_BL.uv2.x + characterInfos[i].vertex_TR.uv2.x) / characterInfos[i].aspectRatio)) / 2; // Center of Rectangle
+
+ characterInfos[i].vertex_BL.uv2.y = yDelta + (characterInfos[i].vertex_BL.uv2.x / characterInfos[i].aspectRatio); // + m_uvOffset.y;
+ characterInfos[i].vertex_TL.uv2.y = yDelta + (characterInfos[i].vertex_TR.uv2.x / characterInfos[i].aspectRatio); // + m_uvOffset.y;
+ characterInfos[i].vertex_BR.uv2.y = characterInfos[i].vertex_BL.uv2.y;
+ characterInfos[i].vertex_TR.uv2.y = characterInfos[i].vertex_TL.uv2.y;
+ break;
+ }
+ #endregion
+
+ // Pack UV's so that we can pass Xscale needed for Shader to maintain 1:1 ratio.
+ #region Pack Scale into UV2
+ xScale = characterInfos[i].scale * Mathf.Abs(lossyScale) * (1 - m_charWidthAdjDelta);
+ if (!characterInfos[i].isUsingAlternateTypeface && (characterInfos[i].style & FontStyles.Bold) == FontStyles.Bold) xScale *= -1;
+
+ //int isBold = (m_textInfo.characterInfo[i].style & FontStyles.Bold) == FontStyles.Bold ? 1 : 0;
+ //Vector2 vertexData = new Vector2(isBold, xScale);
+ //characterInfos[i].vertex_BL.uv4 = vertexData;
+ //characterInfos[i].vertex_TL.uv4 = vertexData;
+ //characterInfos[i].vertex_TR.uv4 = vertexData;
+ //characterInfos[i].vertex_BR.uv4 = vertexData;
+
+ float x0 = characterInfos[i].vertex_BL.uv2.x;
+ float y0 = characterInfos[i].vertex_BL.uv2.y;
+ float x1 = characterInfos[i].vertex_TR.uv2.x;
+ float y1 = characterInfos[i].vertex_TR.uv2.y;
+
+ float dx = (int)x0;
+ float dy = (int)y0;
+
+ x0 = x0 - dx;
+ x1 = x1 - dx;
+ y0 = y0 - dy;
+ y1 = y1 - dy;
+
+ // Optimization to avoid having a vector2 returned from the Pack UV function.
+ characterInfos[i].vertex_BL.uv2.x = PackUV(x0, y0); characterInfos[i].vertex_BL.uv2.y = xScale;
+ characterInfos[i].vertex_TL.uv2.x = PackUV(x0, y1); characterInfos[i].vertex_TL.uv2.y = xScale;
+ characterInfos[i].vertex_TR.uv2.x = PackUV(x1, y1); characterInfos[i].vertex_TR.uv2.y = xScale;
+ characterInfos[i].vertex_BR.uv2.x = PackUV(x1, y0); characterInfos[i].vertex_BR.uv2.y = xScale;
+ #endregion
+ break;
+
+ // SPRITES
+ case TMP_TextElementType.Sprite:
+ // Nothing right now
+ break;
+ }
+
+ // Handle maxVisibleCharacters, maxVisibleLines and Overflow Page Mode.
+ #region Handle maxVisibleCharacters / maxVisibleLines / Page Mode
+ if (i < m_maxVisibleCharacters && wordCount < m_maxVisibleWords && currentLine < m_maxVisibleLines && m_overflowMode != TextOverflowModes.Page)
+ {
+ characterInfos[i].vertex_BL.position += offset;
+ characterInfos[i].vertex_TL.position += offset;
+ characterInfos[i].vertex_TR.position += offset;
+ characterInfos[i].vertex_BR.position += offset;
+ }
+ else if (i < m_maxVisibleCharacters && wordCount < m_maxVisibleWords && currentLine < m_maxVisibleLines && m_overflowMode == TextOverflowModes.Page && characterInfos[i].pageNumber == pageToDisplay)
+ {
+ characterInfos[i].vertex_BL.position += offset;
+ characterInfos[i].vertex_TL.position += offset;
+ characterInfos[i].vertex_TR.position += offset;
+ characterInfos[i].vertex_BR.position += offset;
+ }
+ else
+ {
+ characterInfos[i].vertex_BL.position = Vector3.zero;
+ characterInfos[i].vertex_TL.position = Vector3.zero;
+ characterInfos[i].vertex_TR.position = Vector3.zero;
+ characterInfos[i].vertex_BR.position = Vector3.zero;
+ characterInfos[i].isVisible = false;
+ }
+ #endregion
+
+
+ // Fill Vertex Buffers for the various types of element
+ if (elementType == TMP_TextElementType.Character)
+ {
+ FillCharacterVertexBuffers(i, vert_index_X4, m_isVolumetricText);
+ }
+ else if (elementType == TMP_TextElementType.Sprite)
+ {
+ FillSpriteVertexBuffers(i, sprite_index_X4);
+ }
+ }
+ #endregion
+
+ // Apply Alignment and Justification Offset
+ m_textInfo.characterInfo[i].bottomLeft += offset;
+ m_textInfo.characterInfo[i].topLeft += offset;
+ m_textInfo.characterInfo[i].topRight += offset;
+ m_textInfo.characterInfo[i].bottomRight += offset;
+
+ m_textInfo.characterInfo[i].origin += offset.x;
+ m_textInfo.characterInfo[i].xAdvance += offset.x;
+
+ m_textInfo.characterInfo[i].ascender += offset.y;
+ m_textInfo.characterInfo[i].descender += offset.y;
+ m_textInfo.characterInfo[i].baseLine += offset.y;
+
+ // Update MeshExtents
+ if (isCharacterVisible)
+ {
+ //m_meshExtents.min = new Vector2(Mathf.Min(m_meshExtents.min.x, m_textInfo.characterInfo[i].bottomLeft.x), Mathf.Min(m_meshExtents.min.y, m_textInfo.characterInfo[i].bottomLeft.y));
+ //m_meshExtents.max = new Vector2(Mathf.Max(m_meshExtents.max.x, m_textInfo.characterInfo[i].topRight.x), Mathf.Max(m_meshExtents.max.y, m_textInfo.characterInfo[i].topLeft.y));
+ }
+
+ // Need to recompute lineExtent to account for the offset from justification.
+ #region Adjust lineExtents resulting from alignment offset
+ if (currentLine != lastLine || i == m_characterCount - 1)
+ {
+ // Update the previous line's extents
+ if (currentLine != lastLine)
+ {
+ m_textInfo.lineInfo[lastLine].baseline += offset.y;
+ m_textInfo.lineInfo[lastLine].ascender += offset.y;
+ m_textInfo.lineInfo[lastLine].descender += offset.y;
+
+ m_textInfo.lineInfo[lastLine].lineExtents.min = new Vector2(m_textInfo.characterInfo[m_textInfo.lineInfo[lastLine].firstCharacterIndex].bottomLeft.x, m_textInfo.lineInfo[lastLine].descender);
+ m_textInfo.lineInfo[lastLine].lineExtents.max = new Vector2(m_textInfo.characterInfo[m_textInfo.lineInfo[lastLine].lastVisibleCharacterIndex].topRight.x, m_textInfo.lineInfo[lastLine].ascender);
+ }
+
+ // Update the current line's extents
+ if (i == m_characterCount - 1)
+ {
+ m_textInfo.lineInfo[currentLine].baseline += offset.y;
+ m_textInfo.lineInfo[currentLine].ascender += offset.y;
+ m_textInfo.lineInfo[currentLine].descender += offset.y;
+
+ m_textInfo.lineInfo[currentLine].lineExtents.min = new Vector2(m_textInfo.characterInfo[m_textInfo.lineInfo[currentLine].firstCharacterIndex].bottomLeft.x, m_textInfo.lineInfo[currentLine].descender);
+ m_textInfo.lineInfo[currentLine].lineExtents.max = new Vector2(m_textInfo.characterInfo[m_textInfo.lineInfo[currentLine].lastVisibleCharacterIndex].topRight.x, m_textInfo.lineInfo[currentLine].ascender);
+ }
+ }
+ #endregion
+
+
+ // Track Word Count per line and for the object
+ #region Track Word Count
+ if (char.IsLetterOrDigit(currentCharacter) || currentCharacter == 0x2D || currentCharacter == 0xAD || currentCharacter == 0x2010 || currentCharacter == 0x2011)
+ {
+ if (isStartOfWord == false)
+ {
+ isStartOfWord = true;
+ wordFirstChar = i;
+ }
+
+ // If last character is a word
+ if (isStartOfWord && i == m_characterCount - 1)
+ {
+ int size = m_textInfo.wordInfo.Length;
+ int index = m_textInfo.wordCount;
+
+ if (m_textInfo.wordCount + 1 > size)
+ TMP_TextInfo.Resize(ref m_textInfo.wordInfo, size + 1);
+
+ wordLastChar = i;
+
+ m_textInfo.wordInfo[index].firstCharacterIndex = wordFirstChar;
+ m_textInfo.wordInfo[index].lastCharacterIndex = wordLastChar;
+ m_textInfo.wordInfo[index].characterCount = wordLastChar - wordFirstChar + 1;
+ m_textInfo.wordInfo[index].textComponent = this;
+
+ wordCount += 1;
+ m_textInfo.wordCount += 1;
+ m_textInfo.lineInfo[currentLine].wordCount += 1;
+ }
+ }
+ else if (isStartOfWord || i == 0 && (!char.IsPunctuation(currentCharacter) || char.IsWhiteSpace(currentCharacter) || currentCharacter == 0x200B || i == m_characterCount - 1))
+ {
+ if (i > 0 && i < characterInfos.Length - 1 && i < m_characterCount && (currentCharacter == 39 || currentCharacter == 8217) && char.IsLetterOrDigit(characterInfos[i - 1].character) && char.IsLetterOrDigit(characterInfos[i + 1].character))
+ {
+
+ }
+ else
+ {
+ wordLastChar = i == m_characterCount - 1 && char.IsLetterOrDigit(currentCharacter) ? i : i - 1;
+ isStartOfWord = false;
+
+ int size = m_textInfo.wordInfo.Length;
+ int index = m_textInfo.wordCount;
+
+ if (m_textInfo.wordCount + 1 > size)
+ TMP_TextInfo.Resize(ref m_textInfo.wordInfo, size + 1);
+
+ m_textInfo.wordInfo[index].firstCharacterIndex = wordFirstChar;
+ m_textInfo.wordInfo[index].lastCharacterIndex = wordLastChar;
+ m_textInfo.wordInfo[index].characterCount = wordLastChar - wordFirstChar + 1;
+ m_textInfo.wordInfo[index].textComponent = this;
+
+ wordCount += 1;
+ m_textInfo.wordCount += 1;
+ m_textInfo.lineInfo[currentLine].wordCount += 1;
+ }
+ }
+ #endregion
+
+
+ // Setup & Handle Underline
+ #region Underline
+ // NOTE: Need to figure out how underline will be handled with multiple fonts and which font will be used for the underline.
+ bool isUnderline = (m_textInfo.characterInfo[i].style & FontStyles.Underline) == FontStyles.Underline;
+ if (isUnderline)
+ {
+ bool isUnderlineVisible = true;
+ int currentPage = m_textInfo.characterInfo[i].pageNumber;
+
+ if (i > m_maxVisibleCharacters || currentLine > m_maxVisibleLines || (m_overflowMode == TextOverflowModes.Page && currentPage + 1 != m_pageToDisplay))
+ isUnderlineVisible = false;
+
+ // We only use the scale of visible characters.
+ if (!char.IsWhiteSpace(currentCharacter) && currentCharacter != 0x200B)
+ {
+ underlineMaxScale = Mathf.Max(underlineMaxScale, m_textInfo.characterInfo[i].scale);
+ xScaleMax = Mathf.Max(xScaleMax, Mathf.Abs(xScale));
+ underlineBaseLine = Mathf.Min(currentPage == lastPage ? underlineBaseLine : k_LargePositiveFloat, m_textInfo.characterInfo[i].baseLine + font.faceInfo.underlineOffset * underlineMaxScale);
+ lastPage = currentPage; // Need to track pages to ensure we reset baseline for the new pages.
+ }
+
+ if (beginUnderline == false && isUnderlineVisible == true && i <= lineInfo.lastVisibleCharacterIndex && currentCharacter != 10 && currentCharacter != 13)
+ {
+ if (i == lineInfo.lastVisibleCharacterIndex && char.IsSeparator(currentCharacter))
+ { }
+ else
+ {
+ beginUnderline = true;
+ underlineStartScale = m_textInfo.characterInfo[i].scale;
+ if (underlineMaxScale == 0)
+ {
+ underlineMaxScale = underlineStartScale;
+ xScaleMax = xScale;
+ }
+ underline_start = new Vector3(m_textInfo.characterInfo[i].bottomLeft.x, underlineBaseLine, 0);
+ underlineColor = m_textInfo.characterInfo[i].underlineColor;
+ }
+ }
+
+ // End Underline if text only contains one character.
+ if (beginUnderline && m_characterCount == 1)
+ {
+ beginUnderline = false;
+ underline_end = new Vector3(m_textInfo.characterInfo[i].topRight.x, underlineBaseLine, 0);
+ underlineEndScale = m_textInfo.characterInfo[i].scale;
+
+ DrawUnderlineMesh(underline_start, underline_end, ref last_vert_index, underlineStartScale, underlineEndScale, underlineMaxScale, xScaleMax, underlineColor);
+ underlineMaxScale = 0;
+ xScaleMax = 0;
+ underlineBaseLine = k_LargePositiveFloat;
+ }
+ else if (beginUnderline && (i == lineInfo.lastCharacterIndex || i >= lineInfo.lastVisibleCharacterIndex))
+ {
+ // Terminate underline at previous visible character if space or carriage return.
+ if (char.IsWhiteSpace(currentCharacter) || currentCharacter == 0x200B)
+ {
+ int lastVisibleCharacterIndex = lineInfo.lastVisibleCharacterIndex;
+ underline_end = new Vector3(m_textInfo.characterInfo[lastVisibleCharacterIndex].topRight.x, underlineBaseLine, 0);
+ underlineEndScale = m_textInfo.characterInfo[lastVisibleCharacterIndex].scale;
+ }
+ else
+ { // End underline if last character of the line.
+ underline_end = new Vector3(m_textInfo.characterInfo[i].topRight.x, underlineBaseLine, 0);
+ underlineEndScale = m_textInfo.characterInfo[i].scale;
+ }
+
+ beginUnderline = false;
+ DrawUnderlineMesh(underline_start, underline_end, ref last_vert_index, underlineStartScale, underlineEndScale, underlineMaxScale, xScaleMax, underlineColor);
+ underlineMaxScale = 0;
+ xScaleMax = 0;
+ underlineBaseLine = k_LargePositiveFloat;
+ }
+ else if (beginUnderline && !isUnderlineVisible)
+ {
+ beginUnderline = false;
+ underline_end = new Vector3(m_textInfo.characterInfo[i - 1].topRight.x, underlineBaseLine, 0);
+ underlineEndScale = m_textInfo.characterInfo[i - 1].scale;
+
+ DrawUnderlineMesh(underline_start, underline_end, ref last_vert_index, underlineStartScale, underlineEndScale, underlineMaxScale, xScaleMax, underlineColor);
+ underlineMaxScale = 0;
+ xScaleMax = 0;
+ underlineBaseLine = k_LargePositiveFloat;
+ }
+ else if (beginUnderline && i < m_characterCount - 1 && !underlineColor.Compare(m_textInfo.characterInfo[i + 1].underlineColor))
+ {
+ // End underline if underline color has changed.
+ beginUnderline = false;
+ underline_end = new Vector3(m_textInfo.characterInfo[i].topRight.x, underlineBaseLine, 0);
+ underlineEndScale = m_textInfo.characterInfo[i].scale;
+
+ DrawUnderlineMesh(underline_start, underline_end, ref last_vert_index, underlineStartScale, underlineEndScale, underlineMaxScale, xScaleMax, underlineColor);
+ underlineMaxScale = 0;
+ xScaleMax = 0;
+ underlineBaseLine = k_LargePositiveFloat;
+ }
+ }
+ else
+ {
+ // End Underline
+ if (beginUnderline == true)
+ {
+ beginUnderline = false;
+ underline_end = new Vector3(m_textInfo.characterInfo[i - 1].topRight.x, underlineBaseLine, 0);
+ underlineEndScale = m_textInfo.characterInfo[i - 1].scale;
+
+ DrawUnderlineMesh(underline_start, underline_end, ref last_vert_index, underlineStartScale, underlineEndScale, underlineMaxScale, xScaleMax, underlineColor);
+ underlineMaxScale = 0;
+ xScaleMax = 0;
+ underlineBaseLine = k_LargePositiveFloat;
+ }
+ }
+ #endregion
+
+
+ // Setup & Handle Strikethrough
+ #region Strikethrough
+ // NOTE: Need to figure out how underline will be handled with multiple fonts and which font will be used for the underline.
+ bool isStrikethrough = (m_textInfo.characterInfo[i].style & FontStyles.Strikethrough) == FontStyles.Strikethrough;
+ float strikethroughOffset = currentFontAsset.faceInfo.strikethroughOffset;
+
+ if (isStrikethrough)
+ {
+ bool isStrikeThroughVisible = true;
+
+ if (i > m_maxVisibleCharacters || currentLine > m_maxVisibleLines || (m_overflowMode == TextOverflowModes.Page && m_textInfo.characterInfo[i].pageNumber + 1 != m_pageToDisplay))
+ isStrikeThroughVisible = false;
+
+ if (beginStrikethrough == false && isStrikeThroughVisible && i <= lineInfo.lastVisibleCharacterIndex && currentCharacter != 10 && currentCharacter != 13)
+ {
+ if (i == lineInfo.lastVisibleCharacterIndex && char.IsSeparator(currentCharacter))
+ { }
+ else
+ {
+ beginStrikethrough = true;
+ strikethroughPointSize = m_textInfo.characterInfo[i].pointSize;
+ strikethroughScale = m_textInfo.characterInfo[i].scale;
+ strikethrough_start = new Vector3(m_textInfo.characterInfo[i].bottomLeft.x, m_textInfo.characterInfo[i].baseLine + strikethroughOffset * strikethroughScale, 0);
+ strikethroughColor = m_textInfo.characterInfo[i].strikethroughColor;
+ strikethroughBaseline = m_textInfo.characterInfo[i].baseLine;
+ //Debug.Log("Char [" + currentCharacter + "] Start Strikethrough POS: " + strikethrough_start);
+ }
+ }
+
+ // End Strikethrough if text only contains one character.
+ if (beginStrikethrough && m_characterCount == 1)
+ {
+ beginStrikethrough = false;
+ strikethrough_end = new Vector3(m_textInfo.characterInfo[i].topRight.x, m_textInfo.characterInfo[i].baseLine + strikethroughOffset * strikethroughScale, 0);
+
+ DrawUnderlineMesh(strikethrough_start, strikethrough_end, ref last_vert_index, strikethroughScale, strikethroughScale, strikethroughScale, xScale, strikethroughColor);
+ }
+ else if (beginStrikethrough && i == lineInfo.lastCharacterIndex)
+ {
+ // Terminate Strikethrough at previous visible character if space or carriage return.
+ if (char.IsWhiteSpace(currentCharacter) || currentCharacter == 0x200B)
+ {
+ int lastVisibleCharacterIndex = lineInfo.lastVisibleCharacterIndex;
+ strikethrough_end = new Vector3(m_textInfo.characterInfo[lastVisibleCharacterIndex].topRight.x, m_textInfo.characterInfo[lastVisibleCharacterIndex].baseLine + strikethroughOffset * strikethroughScale, 0);
+ }
+ else
+ {
+ // Terminate Strikethrough at last character of line.
+ strikethrough_end = new Vector3(m_textInfo.characterInfo[i].topRight.x, m_textInfo.characterInfo[i].baseLine + strikethroughOffset * strikethroughScale, 0);
+ }
+
+ beginStrikethrough = false;
+ DrawUnderlineMesh(strikethrough_start, strikethrough_end, ref last_vert_index, strikethroughScale, strikethroughScale, strikethroughScale, xScale, strikethroughColor);
+ }
+ else if (beginStrikethrough && i < m_characterCount && (m_textInfo.characterInfo[i + 1].pointSize != strikethroughPointSize || !TMP_Math.Approximately(m_textInfo.characterInfo[i + 1].baseLine + offset.y, strikethroughBaseline)))
+ {
+ // Terminate Strikethrough if scale changes.
+ beginStrikethrough = false;
+
+ int lastVisibleCharacterIndex = lineInfo.lastVisibleCharacterIndex;
+ if (i > lastVisibleCharacterIndex)
+ strikethrough_end = new Vector3(m_textInfo.characterInfo[lastVisibleCharacterIndex].topRight.x, m_textInfo.characterInfo[lastVisibleCharacterIndex].baseLine + strikethroughOffset * strikethroughScale, 0);
+ else
+ strikethrough_end = new Vector3(m_textInfo.characterInfo[i].topRight.x, m_textInfo.characterInfo[i].baseLine + strikethroughOffset * strikethroughScale, 0);
+
+ DrawUnderlineMesh(strikethrough_start, strikethrough_end, ref last_vert_index, strikethroughScale, strikethroughScale, strikethroughScale, xScale, strikethroughColor);
+ //Debug.Log("Char [" + currentCharacter + "] at Index: " + i + " End Strikethrough POS: " + strikethrough_end + " Baseline: " + m_textInfo.characterInfo[i].baseLine.ToString("f3"));
+ }
+ else if (beginStrikethrough && i < m_characterCount && currentFontAsset.GetInstanceID() != characterInfos[i + 1].fontAsset.GetInstanceID())
+ {
+ // Terminate Strikethrough if font asset changes.
+ beginStrikethrough = false;
+ strikethrough_end = new Vector3(m_textInfo.characterInfo[i].topRight.x, m_textInfo.characterInfo[i].baseLine + strikethroughOffset * strikethroughScale, 0);
+
+ DrawUnderlineMesh(strikethrough_start, strikethrough_end, ref last_vert_index, strikethroughScale, strikethroughScale, strikethroughScale, xScale, strikethroughColor);
+ }
+ else if (beginStrikethrough && !isStrikeThroughVisible)
+ {
+ // Terminate Strikethrough if character is not visible.
+ beginStrikethrough = false;
+ strikethrough_end = new Vector3(m_textInfo.characterInfo[i - 1].topRight.x, m_textInfo.characterInfo[i - 1].baseLine + strikethroughOffset * strikethroughScale, 0);
+
+ DrawUnderlineMesh(strikethrough_start, strikethrough_end, ref last_vert_index, strikethroughScale, strikethroughScale, strikethroughScale, xScale, strikethroughColor);
+ }
+ }
+ else
+ {
+ // End Strikethrough
+ if (beginStrikethrough == true)
+ {
+ beginStrikethrough = false;
+ strikethrough_end = new Vector3(m_textInfo.characterInfo[i - 1].topRight.x, m_textInfo.characterInfo[i - 1].baseLine + strikethroughOffset * strikethroughScale, 0);
+
+ DrawUnderlineMesh(strikethrough_start, strikethrough_end, ref last_vert_index, strikethroughScale, strikethroughScale, strikethroughScale, xScale, strikethroughColor);
+ }
+ }
+ #endregion
+
+
+ // HANDLE TEXT HIGHLIGHTING
+ #region Text Highlighting
+ bool isHighlight = (m_textInfo.characterInfo[i].style & FontStyles.Highlight) == FontStyles.Highlight;
+ if (isHighlight)
+ {
+ bool isHighlightVisible = true;
+ int currentPage = m_textInfo.characterInfo[i].pageNumber;
+
+ if (i > m_maxVisibleCharacters || currentLine > m_maxVisibleLines || (m_overflowMode == TextOverflowModes.Page && currentPage + 1 != m_pageToDisplay))
+ isHighlightVisible = false;
+
+ if (beginHighlight == false && isHighlightVisible == true && i <= lineInfo.lastVisibleCharacterIndex && currentCharacter != 10 && currentCharacter != 13)
+ {
+ if (i == lineInfo.lastVisibleCharacterIndex && char.IsSeparator(currentCharacter))
+ { }
+ else
+ {
+ beginHighlight = true;
+ highlight_start = k_LargePositiveVector2;
+ highlight_end = k_LargeNegativeVector2;
+ highlightColor = m_textInfo.characterInfo[i].highlightColor;
+ }
+ }
+
+ if (beginHighlight)
+ {
+ Color32 currentHighlightColor = m_textInfo.characterInfo[i].highlightColor;
+ bool isColorTransition = false;
+
+ // Handle Highlight color changes
+ if (!highlightColor.Compare(currentHighlightColor))
+ {
+ // End drawing at the start of new highlight color to prevent a gap between highlight sections.
+ highlight_end.x = (highlight_end.x + m_textInfo.characterInfo[i].bottomLeft.x) / 2;
+
+ highlight_start.y = Mathf.Min(highlight_start.y, m_textInfo.characterInfo[i].descender);
+ highlight_end.y = Mathf.Max(highlight_end.y, m_textInfo.characterInfo[i].ascender);
+
+ DrawTextHighlight(highlight_start, highlight_end, ref last_vert_index, highlightColor);
+
+ beginHighlight = true;
+ highlight_start = highlight_end;
+
+ highlight_end = new Vector3(m_textInfo.characterInfo[i].topRight.x, m_textInfo.characterInfo[i].descender, 0);
+ highlightColor = m_textInfo.characterInfo[i].highlightColor;
+
+ isColorTransition = true;
+ }
+
+ if (!isColorTransition)
+ {
+ // Use the Min / Max Extents of the Highlight area to handle different character sizes and fonts.
+ highlight_start.x = Mathf.Min(highlight_start.x, m_textInfo.characterInfo[i].bottomLeft.x);
+ highlight_start.y = Mathf.Min(highlight_start.y, m_textInfo.characterInfo[i].descender);
+
+ highlight_end.x = Mathf.Max(highlight_end.x, m_textInfo.characterInfo[i].topRight.x);
+ highlight_end.y = Mathf.Max(highlight_end.y, m_textInfo.characterInfo[i].ascender);
+ }
+ }
+
+ // End Highlight if text only contains one character.
+ if (beginHighlight && m_characterCount == 1)
+ {
+ beginHighlight = false;
+
+ DrawTextHighlight(highlight_start, highlight_end, ref last_vert_index, highlightColor);
+ }
+ else if (beginHighlight && (i == lineInfo.lastCharacterIndex || i >= lineInfo.lastVisibleCharacterIndex))
+ {
+ beginHighlight = false;
+ DrawTextHighlight(highlight_start, highlight_end, ref last_vert_index, highlightColor);
+ }
+ else if (beginHighlight && !isHighlightVisible)
+ {
+ beginHighlight = false;
+ DrawTextHighlight(highlight_start, highlight_end, ref last_vert_index, highlightColor);
+ }
+ }
+ else
+ {
+ // End Highlight
+ if (beginHighlight == true)
+ {
+ beginHighlight = false;
+ DrawTextHighlight(highlight_start, highlight_end, ref last_vert_index, highlightColor);
+ }
+ }
+ #endregion
+
+
+ lastLine = currentLine;
+ }
+ #endregion
+
+
+ // METRICS ABOUT THE TEXT OBJECT
+ m_textInfo.characterCount = m_characterCount;
+ m_textInfo.spriteCount = m_spriteCount;
+ m_textInfo.lineCount = lineCount;
+ m_textInfo.wordCount = wordCount != 0 && m_characterCount > 0 ? wordCount : 1;
+ m_textInfo.pageCount = m_pageNumber + 1;
+
+
+ ////Profiler.BeginSample("TMP Generate Text - Phase III");
+ // Update Mesh Vertex Data
+ if (m_renderMode == TextRenderFlags.Render && IsActive())
+ {
+ // Clear unused vertices
+ //m_textInfo.meshInfo[0].ClearUnusedVertices();
+
+ // Sort the geometry of the text object if needed.
+ if (m_geometrySortingOrder != VertexSortingOrder.Normal)
+ m_textInfo.meshInfo[0].SortGeometry(VertexSortingOrder.Reverse);
+
+ // Upload Mesh Data
+ m_mesh.MarkDynamic();
+ m_mesh.vertices = m_textInfo.meshInfo[0].vertices;
+ m_mesh.uv = m_textInfo.meshInfo[0].uvs0;
+ m_mesh.uv2 = m_textInfo.meshInfo[0].uvs2;
+ //m_mesh.uv4 = m_textInfo.meshInfo[0].uvs4;
+ m_mesh.colors32 = m_textInfo.meshInfo[0].colors32;
+
+ // Compute Bounds for the mesh. Manual computation is more efficient then using Mesh.recalcualteBounds.
+ m_mesh.RecalculateBounds();
+ //m_mesh.bounds = new Bounds(new Vector3((m_meshExtents.max.x + m_meshExtents.min.x) / 2, (m_meshExtents.max.y + m_meshExtents.min.y) / 2, 0) + offset, new Vector3(m_meshExtents.max.x - m_meshExtents.min.x, m_meshExtents.max.y - m_meshExtents.min.y, 0));
+
+ for (int i = 1; i < m_textInfo.materialCount; i++)
+ {
+ // Clear unused vertices
+ m_textInfo.meshInfo[i].ClearUnusedVertices();
+
+ if (m_subTextObjects[i] == null) continue;
+
+ // Sort the geometry of the sub-text objects if needed.
+ if (m_geometrySortingOrder != VertexSortingOrder.Normal)
+ m_textInfo.meshInfo[i].SortGeometry(VertexSortingOrder.Reverse);
+
+ m_subTextObjects[i].mesh.vertices = m_textInfo.meshInfo[i].vertices;
+ m_subTextObjects[i].mesh.uv = m_textInfo.meshInfo[i].uvs0;
+ m_subTextObjects[i].mesh.uv2 = m_textInfo.meshInfo[i].uvs2;
+ //m_subTextObjects[i].mesh.uv4 = m_textInfo.meshInfo[i].uvs4;
+ m_subTextObjects[i].mesh.colors32 = m_textInfo.meshInfo[i].colors32;
+
+ m_subTextObjects[i].mesh.RecalculateBounds();
+
+ // Update the collider on the sub text object
+ //m_subTextObjects[i].UpdateColliders(m_textInfo.meshInfo[i].vertexCount);
+ }
+ }
+
+ // Event indicating the text has been regenerated.
+ TMPro_EventManager.ON_TEXT_CHANGED(this);
+
+ ////Profiler.EndSample();
+ //Debug.Log("Done Rendering Text.");
+ }
+
+
+ /// <summary>
+ /// Method to return the local corners of the Text Container or RectTransform.
+ /// </summary>
+ /// <returns></returns>
+ protected override Vector3[] GetTextContainerLocalCorners()
+ {
+ if (m_rectTransform == null) m_rectTransform = this.rectTransform;
+
+ m_rectTransform.GetLocalCorners(m_RectTransformCorners);
+
+ return m_RectTransformCorners;
+ }
+
+
+ /// <summary>
+ /// Method to disable the renderers.
+ /// </summary>
+ void SetMeshFilters(bool state)
+ {
+ // Parent text object
+ if (m_meshFilter != null)
+ {
+ if (state)
+ m_meshFilter.sharedMesh = m_mesh;
+ else
+ m_meshFilter.sharedMesh = null;
+ }
+
+ for (int i = 1; i < m_subTextObjects.Length && m_subTextObjects[i] != null; i++)
+ {
+ if (m_subTextObjects[i].meshFilter != null)
+ {
+ if (state)
+ m_subTextObjects[i].meshFilter.sharedMesh = m_subTextObjects[i].mesh;
+ else
+ m_subTextObjects[i].meshFilter.sharedMesh = null;
+ }
+ }
+ }
+
+
+ /// <summary>
+ /// Method to Enable or Disable child SubMesh objects.
+ /// </summary>
+ /// <param name="state"></param>
+ protected override void SetActiveSubMeshes(bool state)
+ {
+ for (int i = 1; i < m_subTextObjects.Length && m_subTextObjects[i] != null; i++)
+ {
+ if (m_subTextObjects[i].enabled != state)
+ m_subTextObjects[i].enabled = state;
+ }
+ }
+
+
+ /// <summary>
+ /// Destroy Sub Mesh Objects
+ /// </summary>
+ protected override void ClearSubMeshObjects()
+ {
+ for (int i = 1; i < m_subTextObjects.Length && m_subTextObjects[i] != null; i++)
+ {
+ Debug.Log("Destroying Sub Text object[" + i + "].");
+ DestroyImmediate(m_subTextObjects[i]);
+ }
+ }
+
+
+ /// <summary>
+ /// Method returning the compound bounds of the text object and child sub objects.
+ /// </summary>
+ /// <returns></returns>
+ protected override Bounds GetCompoundBounds()
+ {
+ Bounds mainBounds = m_mesh.bounds;
+ Vector3 min = mainBounds.min;
+ Vector3 max = mainBounds.max;
+
+ for (int i = 1; i < m_subTextObjects.Length && m_subTextObjects[i] != null; i++)
+ {
+ Bounds subBounds = m_subTextObjects[i].mesh.bounds;
+ min.x = min.x < subBounds.min.x ? min.x : subBounds.min.x;
+ min.y = min.y < subBounds.min.y ? min.y : subBounds.min.y;
+
+ max.x = max.x > subBounds.max.x ? max.x : subBounds.max.x;
+ max.y = max.y > subBounds.max.y ? max.y : subBounds.max.y;
+ }
+
+ Vector3 center = (min + max) / 2;
+ Vector2 size = max - min;
+ return new Bounds(center, size);
+ }
+
+
+ /// <summary>
+ /// Method to Update Scale in UV2
+ /// </summary>
+ //void UpdateSDFScale(float lossyScale)
+ //{
+ // // TODO: Resolve - Underline / Strikethrough segments not getting their SDF Scale adjusted.
+
+ // //Debug.Log("*** UpdateSDFScale() ***");
+
+ // // Iterate through each of the characters.
+ // for (int i = 0; i < m_textInfo.characterCount; i++)
+ // {
+ // // Only update scale for visible characters.
+ // if (m_textInfo.characterInfo[i].isVisible && m_textInfo.characterInfo[i].elementType == TMP_TextElementType.Character)
+ // {
+ // float scale = lossyScale * m_textInfo.characterInfo[i].scale * (1 - m_charWidthAdjDelta);
+ // if (!m_textInfo.characterInfo[i].isUsingAlternateTypeface && (m_textInfo.characterInfo[i].style & FontStyles.Bold) == FontStyles.Bold) scale *= -1;
+
+ // int index = m_textInfo.characterInfo[i].materialReferenceIndex;
+ // int vertexIndex = m_textInfo.characterInfo[i].vertexIndex;
+
+ // m_textInfo.meshInfo[index].uvs2[vertexIndex + 0].y = scale;
+ // m_textInfo.meshInfo[index].uvs2[vertexIndex + 1].y = scale;
+ // m_textInfo.meshInfo[index].uvs2[vertexIndex + 2].y = scale;
+ // m_textInfo.meshInfo[index].uvs2[vertexIndex + 3].y = scale;
+ // }
+ // }
+
+ // // Push the updated uv2 scale information to the meshes.
+ // for (int i = 0; i < m_textInfo.meshInfo.Length; i++)
+ // {
+ // if (i == 0)
+ // m_mesh.uv2 = m_textInfo.meshInfo[0].uvs2;
+ // else
+ // m_subTextObjects[i].mesh.uv2 = m_textInfo.meshInfo[i].uvs2;
+ // }
+ //}
+
+ /// <summary>
+ /// Method to update the SDF Scale in UV2.
+ /// </summary>
+ /// <param name="scaleDelta"></param>
+ void UpdateSDFScale(float scaleDelta)
+ {
+ if (scaleDelta == 0 || scaleDelta == float.PositiveInfinity)
+ {
+ m_havePropertiesChanged = true;
+ OnPreRenderObject();
+ return;
+ }
+
+ for (int materialIndex = 0; materialIndex < m_textInfo.materialCount; materialIndex++)
+ {
+ TMP_MeshInfo meshInfo = m_textInfo.meshInfo[materialIndex];
+
+ for (int i = 0; i < meshInfo.uvs2.Length; i++)
+ {
+ meshInfo.uvs2[i].y *= Mathf.Abs(scaleDelta);
+ }
+ }
+
+ // Push the updated uv2 scale information to the meshes.
+ for (int i = 0; i < m_textInfo.meshInfo.Length; i++)
+ {
+ if (i == 0)
+ m_mesh.uv2 = m_textInfo.meshInfo[0].uvs2;
+ else
+ m_subTextObjects[i].mesh.uv2 = m_textInfo.meshInfo[i].uvs2;
+ }
+ }
+
+
+ // Function to offset vertices position to account for line spacing changes.
+ protected override void AdjustLineOffset(int startIndex, int endIndex, float offset)
+ {
+ Vector3 vertexOffset = new Vector3(0, offset, 0);
+
+ for (int i = startIndex; i <= endIndex; i++)
+ {
+ m_textInfo.characterInfo[i].bottomLeft -= vertexOffset;
+ m_textInfo.characterInfo[i].topLeft -= vertexOffset;
+ m_textInfo.characterInfo[i].topRight -= vertexOffset;
+ m_textInfo.characterInfo[i].bottomRight -= vertexOffset;
+
+ m_textInfo.characterInfo[i].ascender -= vertexOffset.y;
+ m_textInfo.characterInfo[i].baseLine -= vertexOffset.y;
+ m_textInfo.characterInfo[i].descender -= vertexOffset.y;
+
+ if (m_textInfo.characterInfo[i].isVisible)
+ {
+ m_textInfo.characterInfo[i].vertex_BL.position -= vertexOffset;
+ m_textInfo.characterInfo[i].vertex_TL.position -= vertexOffset;
+ m_textInfo.characterInfo[i].vertex_TR.position -= vertexOffset;
+ m_textInfo.characterInfo[i].vertex_BR.position -= vertexOffset;
+ }
+ }
+ }
+
+ }
+} \ No newline at end of file
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMPro_Private.cs.meta b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMPro_Private.cs.meta
new file mode 100644
index 0000000..8672258
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMPro_Private.cs.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: 8f8b248abe6b4dcebd6cdd0d754717f4
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {fileID: 2800000, guid: 8e098d8d28c5182419f7a1c8b91ca722, type: 3}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMPro_UGUI_Private.cs b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMPro_UGUI_Private.cs
new file mode 100644
index 0000000..7134458
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMPro_UGUI_Private.cs
@@ -0,0 +1,4314 @@
+//#define TMP_PROFILE_ON
+//#define TMP_PROFILE_PHASES_ON
+
+
+using UnityEngine;
+using UnityEngine.TextCore;
+using System;
+using System.Collections.Generic;
+using UnityEngine.UI;
+
+#pragma warning disable 0414 // Disabled a few warnings related to serialized variables not used in this script but used in the editor.
+#pragma warning disable 0618 // Disabled warning due to SetVertices being deprecated until new release with SetMesh() is available.
+
+namespace TMPro
+{
+
+ public partial class TextMeshProUGUI
+ {
+ [SerializeField]
+ private bool m_hasFontAssetChanged = false; // Used to track when font properties have changed.
+
+ [SerializeField]
+ protected TMP_SubMeshUI[] m_subTextObjects = new TMP_SubMeshUI[8];
+
+ private float m_previousLossyScaleY = -1; // Used for Tracking lossy scale changes in the transform;
+
+ private Vector3[] m_RectTransformCorners = new Vector3[4];
+ private CanvasRenderer m_canvasRenderer;
+ private Canvas m_canvas;
+
+
+ private bool m_isFirstAllocation; // Flag to determine if this is the first allocation of the buffers.
+ private int m_max_characters = 8; // Determines the initial allocation and size of the character array / buffer.
+ //private int m_max_numberOfLines = 4; // Determines the initial allocation and maximum number of lines of text.
+
+ // MASKING RELATED PROPERTIES
+ private bool m_isMaskingEnabled;
+ // This property is now obsolete and used for compatibility with previous releases (prior to release 0.1.54).
+ [SerializeField]
+ private Material m_baseMaterial;
+
+
+ private bool m_isScrollRegionSet;
+ //private Mask m_mask;
+ private int m_stencilID = 0;
+
+ [SerializeField]
+ private Vector4 m_maskOffset;
+
+ // Matrix used to animated Env Map
+ private Matrix4x4 m_EnvMapMatrix = new Matrix4x4();
+
+
+ //private bool m_isEnabled;
+ [NonSerialized]
+ private bool m_isRegisteredForEvents;
+
+ // DEBUG Variables
+ //private System.Diagnostics.Stopwatch m_StopWatch;
+ //private int frame = 0;
+ //private int m_recursiveCount = 0;
+ private int m_recursiveCountA = 0;
+ private int loopCountA = 0;
+ //private int loopCountB = 0;
+ //private int loopCountC = 0;
+ //private int loopCountD = 0;
+ //private int loopCountE = 0;
+
+ //[SerializeField]
+ //private new Material m_MaskMaterial;
+
+
+ protected override void Awake()
+ {
+ //Debug.Log("***** Awake() called on object ID " + GetInstanceID() + ". *****");
+
+ #if UNITY_EDITOR
+ // Special handling for TMP Settings and importing Essential Resources
+ if (TMP_Settings.instance == null)
+ {
+ if (m_isWaitingOnResourceLoad == false)
+ TMPro_EventManager.RESOURCE_LOAD_EVENT.Add(ON_RESOURCES_LOADED);
+
+ m_isWaitingOnResourceLoad = true;
+ return;
+ }
+ #endif
+
+ // Cache Reference to the Canvas
+ m_canvas = this.canvas;
+
+ m_isOrthographic = true;
+
+ // Cache Reference to RectTransform.
+ m_rectTransform = gameObject.GetComponent<RectTransform>();
+ if (m_rectTransform == null)
+ m_rectTransform = gameObject.AddComponent<RectTransform>();
+
+ // Cache a reference to the CanvasRenderer.
+ m_canvasRenderer = GetComponent<CanvasRenderer>();
+ if (m_canvasRenderer == null)
+ m_canvasRenderer = gameObject.AddComponent<CanvasRenderer> ();
+
+ if (m_mesh == null)
+ {
+ m_mesh = new Mesh();
+ m_mesh.hideFlags = HideFlags.HideAndDontSave;
+
+ // Create new TextInfo for the text object.
+ m_textInfo = new TMP_TextInfo(this);
+ }
+
+ // Load TMP Settings for new text object instances.
+ LoadDefaultSettings();
+
+ // Load the font asset and assign material to renderer.
+ LoadFontAsset();
+
+ // Load Default TMP StyleSheet
+ TMP_StyleSheet.LoadDefaultStyleSheet();
+
+ // Allocate our initial buffers.
+ if (m_TextParsingBuffer == null)
+ m_TextParsingBuffer = new UnicodeChar[m_max_characters];
+
+ m_cached_TextElement = new TMP_Character();
+ m_isFirstAllocation = true;
+
+ // Check if we have a font asset assigned. Return if we don't because no one likes to see purple squares on screen.
+ if (m_fontAsset == null)
+ {
+ Debug.LogWarning("Please assign a Font Asset to this " + transform.name + " gameobject.", this);
+ return;
+ }
+
+ // Check to make sure Sub Text Objects are tracked correctly in the event a Prefab is used.
+ TMP_SubMeshUI[] subTextObjects = GetComponentsInChildren<TMP_SubMeshUI>();
+ if (subTextObjects.Length > 0)
+ {
+ for (int i = 0; i < subTextObjects.Length; i++)
+ m_subTextObjects[i + 1] = subTextObjects[i];
+ }
+
+ // Set flags to ensure our text is parsed and redrawn.
+ m_isInputParsingRequired = true;
+ m_havePropertiesChanged = true;
+ m_isCalculateSizeRequired = true;
+
+ m_isAwake = true;
+ }
+
+
+ protected override void OnEnable()
+ {
+ //Debug.Log("***** OnEnable() called on object ID " + GetInstanceID() + ". *****");
+
+ // Return if Awake() has not been called on the text object.
+ if (m_isAwake == false)
+ return;
+
+ if (!m_isRegisteredForEvents)
+ {
+ //Debug.Log("Registering for Events.");
+
+ #if UNITY_EDITOR
+ // Register Callbacks for various events.
+ TMPro_EventManager.MATERIAL_PROPERTY_EVENT.Add(ON_MATERIAL_PROPERTY_CHANGED);
+ TMPro_EventManager.FONT_PROPERTY_EVENT.Add(ON_FONT_PROPERTY_CHANGED);
+ TMPro_EventManager.TEXTMESHPRO_UGUI_PROPERTY_EVENT.Add(ON_TEXTMESHPRO_UGUI_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.COLOR_GRADIENT_PROPERTY_EVENT.Add(ON_COLOR_GRADIENT_CHANGED);
+ TMPro_EventManager.TMP_SETTINGS_PROPERTY_EVENT.Add(ON_TMP_SETTINGS_CHANGED);
+ #endif
+ m_isRegisteredForEvents = true;
+ }
+
+ // Cache Reference to the Canvas
+ m_canvas = GetCanvas();
+
+ SetActiveSubMeshes(true);
+
+ // Register Graphic Component to receive event triggers
+ GraphicRegistry.RegisterGraphicForCanvas(m_canvas, this);
+
+ // Register text object for updates
+ TMP_UpdateManager.RegisterTextObjectForUpdate(this);
+
+ ComputeMarginSize();
+
+ m_verticesAlreadyDirty = false;
+ m_layoutAlreadyDirty = false;
+ m_ShouldRecalculateStencil = true;
+ m_isInputParsingRequired = true;
+ SetAllDirty();
+
+ RecalculateClipping();
+ }
+
+
+ protected override void OnDisable()
+ {
+ //Debug.Log("***** OnDisable() called on object ID " + GetInstanceID() + ". *****");
+
+ // Return if Awake() has not been called on the text object.
+ if (m_isAwake == false)
+ return;
+
+ if (m_MaskMaterial != null)
+ {
+ TMP_MaterialManager.ReleaseStencilMaterial(m_MaskMaterial);
+ m_MaskMaterial = null;
+ }
+
+ // UnRegister Graphic Component
+ GraphicRegistry.UnregisterGraphicForCanvas(m_canvas, this);
+ CanvasUpdateRegistry.UnRegisterCanvasElementForRebuild((ICanvasElement)this);
+
+ TMP_UpdateManager.UnRegisterTextObjectForUpdate(this);
+
+ if (m_canvasRenderer != null)
+ m_canvasRenderer.Clear();
+
+ SetActiveSubMeshes(false);
+
+ LayoutRebuilder.MarkLayoutForRebuild(m_rectTransform);
+ RecalculateClipping();
+ }
+
+
+ protected override void OnDestroy()
+ {
+ //Debug.Log("***** OnDestroy() called on object ID " + GetInstanceID() + ". *****");
+
+ // UnRegister Graphic Component
+ GraphicRegistry.UnregisterGraphicForCanvas(m_canvas, this);
+
+ TMP_UpdateManager.UnRegisterTextObjectForUpdate(this);
+
+ // Clean up remaining mesh
+ if (m_mesh != null)
+ DestroyImmediate(m_mesh);
+
+ // Clean up mask material
+ if (m_MaskMaterial != null)
+ {
+ TMP_MaterialManager.ReleaseStencilMaterial(m_MaskMaterial);
+ m_MaskMaterial = 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_UGUI_PROPERTY_EVENT.Remove(ON_TEXTMESHPRO_UGUI_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.COLOR_GRADIENT_PROPERTY_EVENT.Remove(ON_COLOR_GRADIENT_CHANGED);
+ TMPro_EventManager.TMP_SETTINGS_PROPERTY_EVENT.Remove(ON_TMP_SETTINGS_CHANGED);
+ TMPro_EventManager.RESOURCE_LOAD_EVENT.Remove(ON_RESOURCES_LOADED);
+ #endif
+ m_isRegisteredForEvents = false;
+ }
+
+
+ #if UNITY_EDITOR
+ protected override void Reset()
+ {
+ //Debug.Log("***** Reset() *****"); //has been called.");
+
+ // Return if Awake() has not been called on the text object.
+ if (m_isAwake == false)
+ return;
+
+ LoadDefaultSettings();
+ LoadFontAsset();
+
+ m_isInputParsingRequired = true;
+ m_havePropertiesChanged = true;
+ }
+
+
+ protected override void OnValidate()
+ {
+ //Debug.Log("***** OnValidate() ***** Frame:" + Time.frameCount); // ID " + GetInstanceID()); // New Material [" + m_sharedMaterial.name + "] with ID " + m_sharedMaterial.GetInstanceID() + ". Base Material is [" + m_baseMaterial.name + "] with ID " + m_baseMaterial.GetInstanceID() + ". Previous Base Material is [" + (m_lastBaseMaterial == null ? "Null" : m_lastBaseMaterial.name) + "].");
+
+ // Return if Awake() has not been called on the text object.
+ if (m_isAwake == false)
+ return;
+
+ // Handle Font Asset changes in the inspector.
+ if (m_fontAsset == null || m_hasFontAssetChanged)
+ {
+ LoadFontAsset();
+ m_isCalculateSizeRequired = true;
+ m_hasFontAssetChanged = false;
+ }
+
+
+ if (m_canvasRenderer == null || m_canvasRenderer.GetMaterial() == null || m_canvasRenderer.GetMaterial().GetTexture(ShaderUtilities.ID_MainTex) == null || m_fontAsset == null || m_fontAsset.atlasTexture.GetInstanceID() != m_canvasRenderer.GetMaterial().GetTexture(ShaderUtilities.ID_MainTex).GetInstanceID())
+ {
+ LoadFontAsset();
+ m_isCalculateSizeRequired = true;
+ m_hasFontAssetChanged = false;
+ }
+
+ m_padding = GetPaddingForMaterial();
+ ComputeMarginSize();
+
+ m_isInputParsingRequired = true;
+ m_inputSource = TextInputSources.Text;
+ m_havePropertiesChanged = true;
+ m_isCalculateSizeRequired = true;
+ m_isPreferredWidthDirty = true;
+ m_isPreferredHeightDirty = true;
+
+ SetAllDirty();
+ }
+
+
+ // Event received when TMP resources have been loaded.
+ void ON_RESOURCES_LOADED()
+ {
+ TMPro_EventManager.RESOURCE_LOAD_EVENT.Remove(ON_RESOURCES_LOADED);
+
+ if (this == null)
+ return;
+
+ Awake();
+ OnEnable();
+ }
+
+
+ // Event received when custom material editor properties are changed.
+ void ON_MATERIAL_PROPERTY_CHANGED(bool isChanged, Material mat)
+ {
+ //Debug.Log("ON_MATERIAL_PROPERTY_CHANGED event received."); // Targeted Material is: " + mat.name + " m_sharedMaterial: " + m_sharedMaterial.name + " with ID:" + m_sharedMaterial.GetInstanceID() + " m_renderer.sharedMaterial: " + m_canvasRenderer.GetMaterial() + " Masking Material:" + m_MaskMaterial.GetInstanceID());
+
+ ShaderUtilities.GetShaderPropertyIDs(); // Initialize ShaderUtilities and get shader property IDs.
+
+ int materialID = mat.GetInstanceID();
+ int sharedMaterialID = m_sharedMaterial.GetInstanceID();
+ int maskingMaterialID = m_MaskMaterial == null ? 0 : m_MaskMaterial.GetInstanceID();
+
+ if (m_canvasRenderer == null || m_canvasRenderer.GetMaterial() == null)
+ {
+ if (m_canvasRenderer == null) return;
+
+ if (m_fontAsset != null)
+ {
+ m_canvasRenderer.SetMaterial(m_fontAsset.material, m_sharedMaterial.GetTexture(ShaderUtilities.ID_MainTex));
+ //Debug.LogWarning("No Material was assigned to " + name + ". " + m_fontAsset.material.name + " was assigned.");
+ }
+ else
+ Debug.LogWarning("No Font Asset assigned to " + name + ". Please assign a Font Asset.", this);
+ }
+
+
+ if (m_canvasRenderer.GetMaterial() != m_sharedMaterial && m_fontAsset == null) // || m_renderer.sharedMaterials.Contains(mat))
+ {
+ //Debug.Log("ON_MATERIAL_PROPERTY_CHANGED Called on Target ID: " + GetInstanceID() + ". Previous Material:" + m_sharedMaterial + " New Material:" + m_uiRenderer.GetMaterial()); // on Object ID:" + GetInstanceID() + ". m_sharedMaterial: " + m_sharedMaterial.name + " m_renderer.sharedMaterial: " + m_renderer.sharedMaterial.name);
+ m_sharedMaterial = m_canvasRenderer.GetMaterial();
+ }
+
+
+ // 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 (materialID == 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);
+ //float stencilOp = m_MaskMaterial.GetFloat(ShaderUtilities.ID_StencilOp);
+ //float stencilRead = m_MaskMaterial.GetFloat(ShaderUtilities.ID_StencilReadMask);
+ //float stencilWrite = m_MaskMaterial.GetFloat(ShaderUtilities.ID_StencilWriteMask);
+
+ m_MaskMaterial.CopyPropertiesFromMaterial(mat);
+ m_MaskMaterial.shaderKeywords = mat.shaderKeywords;
+
+ m_MaskMaterial.SetFloat(ShaderUtilities.ID_StencilID, stencilID);
+ m_MaskMaterial.SetFloat(ShaderUtilities.ID_StencilComp, stencilComp);
+ //m_MaskMaterial.SetFloat(ShaderUtilities.ID_StencilOp, stencilOp);
+ //m_MaskMaterial.SetFloat(ShaderUtilities.ID_StencilReadMask, stencilID);
+ //m_MaskMaterial.SetFloat(ShaderUtilities.ID_StencilWriteMask, 0);
+ }
+ else if (materialID == 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);
+ //m_sharedMaterial.SetFloat(ShaderUtilities.ID_StencilOp, 0);
+ //m_sharedMaterial.SetFloat(ShaderUtilities.ID_StencilReadMask, 255);
+ //m_sharedMaterial.SetFloat(ShaderUtilities.ID_StencilWriteMask, 255);
+ }
+
+ }
+
+ m_padding = GetPaddingForMaterial();
+ m_havePropertiesChanged = true;
+ SetVerticesDirty();
+ //SetMaterialDirty();
+ }
+
+
+ // Event received when font asset properties are changed in Font Inspector
+ void ON_FONT_PROPERTY_CHANGED(bool isChanged, TMP_FontAsset font)
+ {
+ if (MaterialReference.Contains(m_materialReferences, font))
+ {
+ //Debug.Log("ON_FONT_PROPERTY_CHANGED event received.");
+ m_isInputParsingRequired = true;
+ m_havePropertiesChanged = true;
+
+ UpdateMeshPadding();
+
+ SetLayoutDirty();
+ SetVerticesDirty();
+ }
+ }
+
+
+ // Event received when UNDO / REDO Event alters the properties of the object.
+ void ON_TEXTMESHPRO_UGUI_PROPERTY_CHANGED(bool isChanged, TextMeshProUGUI obj)
+ {
+ //Debug.Log("Event Received by " + obj);
+
+ if (obj == this)
+ {
+ //Debug.Log("Undo / Redo Event Received by Object ID:" + GetInstanceID());
+ m_havePropertiesChanged = true;
+ m_isInputParsingRequired = true;
+
+ ComputeMarginSize(); // Review this change
+ SetVerticesDirty();
+ }
+ }
+
+
+ // Event to Track Material Changed resulting from Drag-n-drop.
+ void ON_DRAG_AND_DROP_MATERIAL(GameObject obj, Material currentMaterial, Material newMaterial)
+ {
+ //Debug.Log("Drag-n-Drop Event - Receiving Object ID " + GetInstanceID() + ". Sender ID " + obj.GetInstanceID()); // + ". Prefab Parent is " + UnityEditor.PrefabUtility.GetPrefabParent(gameObject).GetInstanceID()); // + ". New Material is " + newMaterial.name + " with ID " + newMaterial.GetInstanceID() + ". Base Material is " + m_baseMaterial.name + " with ID " + m_baseMaterial.GetInstanceID());
+
+ // 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
+ {
+ UnityEditor.Undo.RecordObject(this, "Material Assignment");
+ UnityEditor.Undo.RecordObject(m_canvasRenderer, "Material Assignment");
+
+ m_sharedMaterial = newMaterial;
+
+ m_padding = GetPaddingForMaterial();
+
+ m_havePropertiesChanged = true;
+ SetVerticesDirty();
+ SetMaterialDirty();
+ }
+ }
+
+
+ // Event received when Text Styles are changed.
+ void ON_TEXT_STYLE_CHANGED(bool isChanged)
+ {
+ m_havePropertiesChanged = true;
+ m_isInputParsingRequired = true;
+ SetVerticesDirty();
+ }
+
+
+ /// <summary>
+ /// Event received when a Color Gradient Preset is modified.
+ /// </summary>
+ /// <param name="textObject"></param>
+ void ON_COLOR_GRADIENT_CHANGED(TMP_ColorGradient gradient)
+ {
+ if (m_fontColorGradientPreset != null && gradient.GetInstanceID() == m_fontColorGradientPreset.GetInstanceID())
+ {
+ m_havePropertiesChanged = true;
+ SetVerticesDirty();
+ }
+ }
+
+
+ /// <summary>
+ /// Event received when the TMP Settings are changed.
+ /// </summary>
+ void ON_TMP_SETTINGS_CHANGED()
+ {
+ m_defaultSpriteAsset = null;
+ m_havePropertiesChanged = true;
+ m_isInputParsingRequired = true;
+ SetAllDirty();
+ }
+ #endif
+
+
+ // Function which loads either the default font or a newly assigned font asset. This function also assigned the appropriate material to the renderer.
+ protected override void LoadFontAsset()
+ {
+ //Debug.Log("***** LoadFontAsset() *****"); //TextMeshPro LoadFontAsset() has been called."); // Current Font Asset is " + (font != null ? font.name: "Null") );
+
+ ShaderUtilities.GetShaderPropertyIDs(); // Initialize & Get shader property IDs.
+
+ if (m_fontAsset == null)
+ {
+ if (TMP_Settings.defaultFontAsset != null)
+ m_fontAsset = TMP_Settings.defaultFontAsset;
+ else
+ m_fontAsset = Resources.Load<TMP_FontAsset>("Fonts & Materials/LiberationSans SDF");
+
+ if (m_fontAsset == null)
+ {
+ Debug.LogWarning("The LiberationSans SDF Font Asset was not found. There is no Font Asset assigned to " + gameObject.name + ".", this);
+ return;
+ }
+
+ if (m_fontAsset.characterLookupTable == null)
+ {
+ Debug.Log("Dictionary is Null!");
+ }
+
+ m_sharedMaterial = m_fontAsset.material;
+ }
+ else
+ {
+ // Read font definition if needed.
+ if (m_fontAsset.characterLookupTable == null)
+ m_fontAsset.ReadFontAssetDefinition();
+
+ // Added for compatibility with previous releases.
+ if (m_sharedMaterial == null && m_baseMaterial != null)
+ {
+ m_sharedMaterial = m_baseMaterial;
+ m_baseMaterial = null;
+ }
+
+ // If font atlas texture doesn't match the assigned material font atlas, switch back to default material specified in the Font Asset.
+ if (m_sharedMaterial == null || m_sharedMaterial.GetTexture(ShaderUtilities.ID_MainTex) == null || m_fontAsset.atlasTexture.GetInstanceID() != m_sharedMaterial.GetTexture(ShaderUtilities.ID_MainTex).GetInstanceID())
+ {
+ if (m_fontAsset.material == null)
+ Debug.LogWarning("The Font Atlas Texture of the Font Asset " + m_fontAsset.name + " assigned to " + gameObject.name + " is missing.", this);
+ else
+ m_sharedMaterial = m_fontAsset.material;
+ }
+ }
+
+
+ // Find and cache Underline & Ellipsis characters.
+ GetSpecialCharacters(m_fontAsset);
+
+ m_padding = GetPaddingForMaterial();
+
+ SetMaterialDirty();
+ }
+
+
+ /// <summary>
+ /// Method to retrieve the parent Canvas.
+ /// </summary>
+ private Canvas GetCanvas()
+ {
+ Canvas canvas = null;
+ var list = TMP_ListPool<Canvas>.Get();
+
+ gameObject.GetComponentsInParent(false, list);
+ if (list.Count > 0)
+ {
+ // Find the first active and enabled canvas.
+ for (int i = 0; i < list.Count; ++i)
+ {
+ if (list[i].isActiveAndEnabled)
+ {
+ canvas = list[i];
+ break;
+ }
+ }
+ }
+
+ TMP_ListPool<Canvas>.Release(list);
+
+ return canvas;
+ }
+
+
+ /// <summary>
+ /// Method used when animating the Env Map on the material.
+ /// </summary>
+ void UpdateEnvMapMatrix()
+ {
+ if (!m_sharedMaterial.HasProperty(ShaderUtilities.ID_EnvMap) || m_sharedMaterial.GetTexture(ShaderUtilities.ID_EnvMap) == null)
+ return;
+
+ //Debug.Log("Updating Env Matrix...");
+ Vector3 rotation = m_sharedMaterial.GetVector(ShaderUtilities.ID_EnvMatrixRotation);
+ m_EnvMapMatrix = Matrix4x4.TRS(Vector3.zero, Quaternion.Euler(rotation), Vector3.one);
+
+ m_sharedMaterial.SetMatrix(ShaderUtilities.ID_EnvMatrix, m_EnvMapMatrix);
+ }
+
+
+ // Enable Masking in the Shader
+ void EnableMasking()
+ {
+ if (m_fontMaterial == null)
+ {
+ m_fontMaterial = CreateMaterialInstance(m_sharedMaterial);
+ m_canvasRenderer.SetMaterial(m_fontMaterial, m_sharedMaterial.GetTexture(ShaderUtilities.ID_MainTex));
+ }
+
+ m_sharedMaterial = m_fontMaterial;
+ if (m_sharedMaterial.HasProperty(ShaderUtilities.ID_ClipRect))
+ {
+ m_sharedMaterial.EnableKeyword(ShaderUtilities.Keyword_MASK_SOFT);
+ m_sharedMaterial.DisableKeyword(ShaderUtilities.Keyword_MASK_HARD);
+ m_sharedMaterial.DisableKeyword(ShaderUtilities.Keyword_MASK_TEX);
+
+ UpdateMask(); // Update Masking Coordinates
+ }
+
+ m_isMaskingEnabled = true;
+
+ //m_uiRenderer.SetMaterial(m_sharedMaterial, null);
+
+ //m_padding = ShaderUtilities.GetPadding(m_sharedMaterial, m_enableExtraPadding, m_isUsingBold);
+ //m_alignmentPadding = ShaderUtilities.GetFontExtent(m_sharedMaterial);
+
+ /*
+ Material mat = m_uiRenderer.GetMaterial();
+ if (mat.HasProperty(ShaderUtilities.ID_MaskCoord))
+ {
+ mat.EnableKeyword("MASK_SOFT");
+ mat.DisableKeyword("MASK_HARD");
+ mat.DisableKeyword("MASK_OFF");
+
+ m_isMaskingEnabled = true;
+ UpdateMask();
+ }
+ */
+ }
+
+
+ // Enable Masking in the Shader
+ void DisableMasking()
+ {
+ if (m_fontMaterial != null)
+ {
+ if (m_stencilID > 0)
+ m_sharedMaterial = m_MaskMaterial;
+ else
+ m_sharedMaterial = m_baseMaterial;
+
+ m_canvasRenderer.SetMaterial(m_sharedMaterial, m_sharedMaterial.GetTexture(ShaderUtilities.ID_MainTex));
+
+ DestroyImmediate(m_fontMaterial);
+ }
+
+ m_isMaskingEnabled = false;
+
+ /*
+ if (m_maskingMaterial != null && m_stencilID == 0)
+ {
+ m_sharedMaterial = m_baseMaterial;
+ m_uiRenderer.SetMaterial(m_sharedMaterial, null);
+ }
+ else if (m_stencilID > 0)
+ {
+ m_sharedMaterial.EnableKeyword("MASK_OFF");
+ m_sharedMaterial.DisableKeyword("MASK_HARD");
+ m_sharedMaterial.DisableKeyword("MASK_SOFT");
+ }
+ */
+
+
+ /*
+ Material mat = m_uiRenderer.GetMaterial();
+ if (mat.HasProperty(ShaderUtilities.ID_MaskCoord))
+ {
+ mat.EnableKeyword("MASK_OFF");
+ mat.DisableKeyword("MASK_HARD");
+ mat.DisableKeyword("MASK_SOFT");
+
+ m_isMaskingEnabled = false;
+ UpdateMask();
+ }
+ */
+ }
+
+
+ // Update & recompute Mask offset
+ void UpdateMask()
+ {
+ //Debug.Log("Updating Mask...");
+
+ if (m_rectTransform != null)
+ {
+ //Material mat = m_uiRenderer.GetMaterial();
+ //if (mat == null || (m_overflowMode == TextOverflowModes.ScrollRect && m_isScrollRegionSet))
+ // return;
+
+ if (!ShaderUtilities.isInitialized)
+ ShaderUtilities.GetShaderPropertyIDs();
+
+ //Debug.Log("Setting Mask for the first time.");
+
+ m_isScrollRegionSet = true;
+
+ float softnessX = Mathf.Min(Mathf.Min(m_margin.x, m_margin.z), m_sharedMaterial.GetFloat(ShaderUtilities.ID_MaskSoftnessX));
+ float softnessY = Mathf.Min(Mathf.Min(m_margin.y, m_margin.w), m_sharedMaterial.GetFloat(ShaderUtilities.ID_MaskSoftnessY));
+
+ softnessX = softnessX > 0 ? softnessX : 0;
+ softnessY = softnessY > 0 ? softnessY : 0;
+
+ float width = (m_rectTransform.rect.width - Mathf.Max(m_margin.x, 0) - Mathf.Max(m_margin.z, 0)) / 2 + softnessX;
+ float height = (m_rectTransform.rect.height - Mathf.Max(m_margin.y, 0) - Mathf.Max(m_margin.w, 0)) / 2 + softnessY;
+
+
+ Vector2 center = m_rectTransform.localPosition + new Vector3((0.5f - m_rectTransform.pivot.x) * m_rectTransform.rect.width + (Mathf.Max(m_margin.x, 0) - Mathf.Max(m_margin.z, 0)) / 2, (0.5f - m_rectTransform.pivot.y) * m_rectTransform.rect.height + (-Mathf.Max(m_margin.y, 0) + Mathf.Max(m_margin.w, 0)) / 2);
+
+ //Vector2 center = m_rectTransform.localPosition + new Vector3((0.5f - m_rectTransform.pivot.x) * m_rectTransform.rect.width + (margin.x - margin.z) / 2, (0.5f - m_rectTransform.pivot.y) * m_rectTransform.rect.height + (-margin.y + margin.w) / 2);
+ Vector4 mask = new Vector4(center.x, center.y, width, height);
+ //Debug.Log(mask);
+
+
+
+ //Rect rect = new Rect(0, 0, m_rectTransform.rect.width + margin.x + margin.z, m_rectTransform.rect.height + margin.y + margin.w);
+ //int softness = (int)m_sharedMaterial.GetFloat(ShaderUtilities.ID_MaskSoftnessX) / 2;
+ m_sharedMaterial.SetVector(ShaderUtilities.ID_ClipRect, mask);
+ }
+ }
+
+
+ // Function called internally when a new material is assigned via the fontMaterial property.
+ protected override Material GetMaterial(Material mat)
+ {
+ // Get Shader PropertyIDs if they haven't been cached already.
+ ShaderUtilities.GetShaderPropertyIDs();
+
+ // 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_canvasRenderer == null)
+ // m_canvasRenderer = GetComponent<CanvasRenderer>();
+
+ // Create Instance Material only if the new material is not the same instance previously used.
+ if (m_fontMaterial == null || m_fontMaterial.GetInstanceID() != mat.GetInstanceID())
+ m_fontMaterial = CreateMaterialInstance(mat);
+
+ m_sharedMaterial = m_fontMaterial;
+
+ m_padding = GetPaddingForMaterial();
+
+ m_ShouldRecalculateStencil = true;
+ SetVerticesDirty();
+ SetMaterialDirty();
+
+ return m_sharedMaterial;
+ }
+
+
+ /// <summary>
+ /// Method returning instances of the materials used by the text object.
+ /// </summary>
+ /// <returns></returns>
+ protected override Material[] GetMaterials(Material[] mats)
+ {
+ int materialCount = m_textInfo.materialCount;
+
+ if (m_fontMaterials == null)
+ m_fontMaterials = new Material[materialCount];
+ else if (m_fontMaterials.Length != materialCount)
+ TMP_TextInfo.Resize(ref m_fontMaterials, materialCount, false);
+
+ // Get instances of the materials
+ for (int i = 0; i < materialCount; i++)
+ {
+ if (i == 0)
+ m_fontMaterials[i] = fontMaterial;
+ else
+ m_fontMaterials[i] = m_subTextObjects[i].material;
+ }
+
+ m_fontSharedMaterials = m_fontMaterials;
+
+ return m_fontMaterials;
+ }
+
+
+ // Function called internally when a new shared material is assigned via the fontSharedMaterial property.
+ protected override void SetSharedMaterial(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_canvasRenderer == null)
+ // m_canvasRenderer = GetComponent<CanvasRenderer>();
+
+ m_sharedMaterial = mat;
+
+ m_padding = GetPaddingForMaterial();
+
+ SetMaterialDirty();
+ }
+
+
+ /// <summary>
+ /// Method returning an array containing the materials used by the text object.
+ /// </summary>
+ /// <returns></returns>
+ protected override Material[] GetSharedMaterials()
+ {
+ int materialCount = m_textInfo.materialCount;
+
+ if (m_fontSharedMaterials == null)
+ m_fontSharedMaterials = new Material[materialCount];
+ else if (m_fontSharedMaterials.Length != materialCount)
+ TMP_TextInfo.Resize(ref m_fontSharedMaterials, materialCount, false);
+
+ for (int i = 0; i < materialCount; i++)
+ {
+ if (i == 0)
+ m_fontSharedMaterials[i] = m_sharedMaterial;
+ else
+ m_fontSharedMaterials[i] = m_subTextObjects[i].sharedMaterial;
+ }
+
+ return m_fontSharedMaterials;
+ }
+
+
+ /// <summary>
+ /// Method used to assign new materials to the text and sub text objects.
+ /// </summary>
+ protected override void SetSharedMaterials(Material[] materials)
+ {
+ int materialCount = m_textInfo.materialCount;
+
+ // Check allocation of the fontSharedMaterials array.
+ if (m_fontSharedMaterials == null)
+ m_fontSharedMaterials = new Material[materialCount];
+ else if (m_fontSharedMaterials.Length != materialCount)
+ TMP_TextInfo.Resize(ref m_fontSharedMaterials, materialCount, false);
+
+ // Only assign as many materials as the text object contains.
+ for (int i = 0; i < materialCount; i++)
+ {
+ if (i == 0)
+ {
+ // Only assign new material if the font atlas textures match.
+ if (materials[i].GetTexture(ShaderUtilities.ID_MainTex) == null || materials[i].GetTexture(ShaderUtilities.ID_MainTex).GetInstanceID() != m_sharedMaterial.GetTexture(ShaderUtilities.ID_MainTex).GetInstanceID())
+ continue;
+
+ m_sharedMaterial = m_fontSharedMaterials[i] = materials[i];
+ m_padding = GetPaddingForMaterial(m_sharedMaterial);
+ }
+ else
+ {
+ // Only assign new material if the font atlas textures match.
+ if (materials[i].GetTexture(ShaderUtilities.ID_MainTex) == null || materials[i].GetTexture(ShaderUtilities.ID_MainTex).GetInstanceID() != m_subTextObjects[i].sharedMaterial.GetTexture(ShaderUtilities.ID_MainTex).GetInstanceID())
+ continue;
+
+ // Only assign a new material if none were specified in the text input.
+ if (m_subTextObjects[i].isDefaultMaterial)
+ m_subTextObjects[i].sharedMaterial = m_fontSharedMaterials[i] = materials[i];
+ }
+ }
+ }
+
+
+ // This function will create an instance of the Font Material.
+ protected override void SetOutlineThickness(float thickness)
+ {
+ // Use material instance if one exists. Otherwise, create a new instance of the shared material.
+ if (m_fontMaterial != null && m_sharedMaterial.GetInstanceID() != m_fontMaterial.GetInstanceID())
+ {
+ m_sharedMaterial = m_fontMaterial;
+ m_canvasRenderer.SetMaterial(m_sharedMaterial, m_sharedMaterial.GetTexture(ShaderUtilities.ID_MainTex));
+ }
+ else if(m_fontMaterial == null)
+ {
+ m_fontMaterial = CreateMaterialInstance(m_sharedMaterial);
+ m_sharedMaterial = m_fontMaterial;
+ m_canvasRenderer.SetMaterial(m_sharedMaterial, m_sharedMaterial.GetTexture(ShaderUtilities.ID_MainTex));
+ }
+
+ thickness = Mathf.Clamp01(thickness);
+ m_sharedMaterial.SetFloat(ShaderUtilities.ID_OutlineWidth, thickness);
+ m_padding = GetPaddingForMaterial();
+ }
+
+
+ // This function will create an instance of the Font Material.
+ protected override void SetFaceColor(Color32 color)
+ {
+ // Use material instance if one exists. Otherwise, create a new instance of the shared material.
+ if (m_fontMaterial == null)
+ m_fontMaterial = CreateMaterialInstance(m_sharedMaterial);
+
+ m_sharedMaterial = m_fontMaterial;
+ m_padding = GetPaddingForMaterial();
+
+ m_sharedMaterial.SetColor(ShaderUtilities.ID_FaceColor, color);
+ }
+
+
+ // This function will create an instance of the Font Material.
+ protected override void SetOutlineColor(Color32 color)
+ {
+ // Use material instance if one exists. Otherwise, create a new instance of the shared material.
+ if (m_fontMaterial == null)
+ m_fontMaterial = CreateMaterialInstance(m_sharedMaterial);
+
+ m_sharedMaterial = m_fontMaterial;
+ m_padding = GetPaddingForMaterial();
+
+ m_sharedMaterial.SetColor(ShaderUtilities.ID_OutlineColor, color);
+ }
+
+
+ // Sets the Render Queue and Ztest mode
+ protected override void SetShaderDepth()
+ {
+ if (m_canvas == null || m_sharedMaterial == null)
+ return;
+
+ if (m_canvas.renderMode == RenderMode.ScreenSpaceOverlay || m_isOverlay)
+ {
+ // Should this use an instanced material?
+ m_sharedMaterial.SetFloat(ShaderUtilities.ShaderTag_ZTestMode, 0);
+ }
+ else
+ { // TODO: This section needs to be tested.
+ m_sharedMaterial.SetFloat(ShaderUtilities.ShaderTag_ZTestMode, 4);
+ }
+ }
+
+
+ // Sets the Culling mode of the material
+ protected override void SetCulling()
+ {
+ if (m_isCullingEnabled)
+ {
+ Material mat = materialForRendering;
+
+ if (mat != null)
+ mat.SetFloat("_CullMode", 2);
+
+ for (int i = 1; i < m_subTextObjects.Length && m_subTextObjects[i] != null; i++)
+ {
+ mat = m_subTextObjects[i].materialForRendering;
+
+ if (mat != null)
+ {
+ mat.SetFloat(ShaderUtilities.ShaderTag_CullMode, 2);
+ }
+ }
+ }
+ else
+ {
+ Material mat = materialForRendering;
+
+ if (mat != null)
+ mat.SetFloat("_CullMode", 0);
+
+ for (int i = 1; i < m_subTextObjects.Length && m_subTextObjects[i] != null; i++)
+ {
+ mat = m_subTextObjects[i].materialForRendering;
+
+ if (mat != null)
+ {
+ mat.SetFloat(ShaderUtilities.ShaderTag_CullMode, 0);
+ }
+ }
+ }
+ }
+
+
+ // Set Perspective Correction Mode based on whether Camera is Orthographic or Perspective
+ void SetPerspectiveCorrection()
+ {
+ if (m_isOrthographic)
+ m_sharedMaterial.SetFloat(ShaderUtilities.ID_PerspectiveFilter, 0.0f);
+ else
+ m_sharedMaterial.SetFloat(ShaderUtilities.ID_PerspectiveFilter, 0.875f);
+ }
+
+
+ /// <summary>
+ /// Get the padding value for the currently assigned material.
+ /// </summary>
+ /// <returns></returns>
+ protected override float GetPaddingForMaterial(Material mat)
+ {
+ m_padding = ShaderUtilities.GetPadding(mat, m_enableExtraPadding, m_isUsingBold);
+ m_isMaskingEnabled = ShaderUtilities.IsMaskingEnabled(m_sharedMaterial);
+ m_isSDFShader = mat.HasProperty(ShaderUtilities.ID_WeightNormal);
+
+ return m_padding;
+ }
+
+
+ /// <summary>
+ /// Get the padding value for the currently assigned material.
+ /// </summary>
+ /// <returns></returns>
+ protected override float GetPaddingForMaterial()
+ {
+ ShaderUtilities.GetShaderPropertyIDs();
+
+ m_padding = ShaderUtilities.GetPadding(m_sharedMaterial, m_enableExtraPadding, m_isUsingBold);
+ m_isMaskingEnabled = ShaderUtilities.IsMaskingEnabled(m_sharedMaterial);
+ m_isSDFShader = m_sharedMaterial.HasProperty(ShaderUtilities.ID_WeightNormal);
+
+ return m_padding;
+ }
+
+ // Function to allocate the necessary buffers to render the text. This function is called whenever the buffer size needs to be increased.
+ void SetMeshArrays(int size)
+ {
+ m_textInfo.meshInfo[0].ResizeMeshInfo(size);
+
+ m_canvasRenderer.SetMesh(m_textInfo.meshInfo[0].mesh);
+ }
+
+
+ // This function parses through the Char[] to determine how many characters will be visible. It then makes sure the arrays are large enough for all those characters.
+ protected override int SetArraySizes(UnicodeChar[] chars)
+ {
+ //Debug.Log("*** SetArraySizes() on Instance ID (" + GetInstanceID() + ") ***");
+ #if TMP_PROFILE_ON
+ Profiler.BeginSample("SetArraySizes");
+ #endif
+
+ int spriteCount = 0;
+
+ m_totalCharacterCount = 0;
+ m_isUsingBold = false;
+ m_isParsingText = false;
+ tag_NoParsing = false;
+ m_FontStyleInternal = m_fontStyle;
+
+ m_FontWeightInternal = (m_FontStyleInternal & FontStyles.Bold) == FontStyles.Bold ? FontWeight.Bold : m_fontWeight;
+ m_FontWeightStack.SetDefault(m_FontWeightInternal);
+
+ m_currentFontAsset = m_fontAsset;
+ m_currentMaterial = m_sharedMaterial;
+ m_currentMaterialIndex = 0;
+
+ m_materialReferenceStack.SetDefault(new MaterialReference(m_currentMaterialIndex, m_currentFontAsset, null, m_currentMaterial, m_padding));
+
+ m_materialReferenceIndexLookup.Clear();
+ MaterialReference.AddMaterialReference(m_currentMaterial, m_currentFontAsset, m_materialReferences, m_materialReferenceIndexLookup);
+
+ if (m_textInfo == null) m_textInfo = new TMP_TextInfo();
+ m_textElementType = TMP_TextElementType.Character;
+
+ // Clear Linked Text object if we have one.
+ if (m_linkedTextComponent != null)
+ {
+ m_linkedTextComponent.text = string.Empty;
+ m_linkedTextComponent.ForceMeshUpdate();
+ }
+
+ // Parsing XML tags in the text
+ for (int i = 0; i < chars.Length && chars[i].unicode != 0; i++)
+ {
+ //Make sure the characterInfo array can hold the next text element.
+ if (m_textInfo.characterInfo == null || m_totalCharacterCount >= m_textInfo.characterInfo.Length)
+ TMP_TextInfo.Resize(ref m_textInfo.characterInfo, m_totalCharacterCount + 1, true);
+
+ int unicode = chars[i].unicode;
+
+ // PARSE XML TAGS
+ #region PARSE XML TAGS
+ if (m_isRichText && unicode == 60) // if Char '<'
+ {
+ int prev_MaterialIndex = m_currentMaterialIndex;
+
+ // Check if Tag is Valid
+ if (ValidateHtmlTag(chars, i + 1, out int tagEnd))
+ {
+ int tagStartIndex = chars[i].stringIndex;
+ i = tagEnd;
+
+ if ((m_FontStyleInternal & FontStyles.Bold) == FontStyles.Bold) m_isUsingBold = true;
+
+ if (m_textElementType == TMP_TextElementType.Sprite)
+ {
+ m_materialReferences[m_currentMaterialIndex].referenceCount += 1;
+
+ m_textInfo.characterInfo[m_totalCharacterCount].character = (char)(57344 + m_spriteIndex);
+ m_textInfo.characterInfo[m_totalCharacterCount].spriteIndex = m_spriteIndex;
+ m_textInfo.characterInfo[m_totalCharacterCount].fontAsset = m_currentFontAsset;
+ m_textInfo.characterInfo[m_totalCharacterCount].spriteAsset = m_currentSpriteAsset;
+ m_textInfo.characterInfo[m_totalCharacterCount].materialReferenceIndex = m_currentMaterialIndex;
+ m_textInfo.characterInfo[m_totalCharacterCount].textElement = m_currentSpriteAsset.spriteCharacterTable[m_spriteIndex];
+ m_textInfo.characterInfo[m_totalCharacterCount].elementType = m_textElementType;
+ m_textInfo.characterInfo[m_totalCharacterCount].index = tagStartIndex;
+ m_textInfo.characterInfo[m_totalCharacterCount].stringLength = chars[i].stringIndex - tagStartIndex + 1;
+
+ // Restore element type and material index to previous values.
+ m_textElementType = TMP_TextElementType.Character;
+ m_currentMaterialIndex = prev_MaterialIndex;
+
+ spriteCount += 1;
+ m_totalCharacterCount += 1;
+ }
+
+ continue;
+ }
+ }
+ #endregion
+
+ bool isUsingAlternativeTypeface = false;
+ bool isUsingFallbackOrAlternativeTypeface = false;
+
+ TMP_Character character;
+ TMP_FontAsset tempFontAsset;
+ TMP_FontAsset prev_fontAsset = m_currentFontAsset;
+ Material prev_material = m_currentMaterial;
+ int prev_materialIndex = m_currentMaterialIndex;
+
+
+ // Handle Font Styles like LowerCase, UpperCase and SmallCaps.
+ #region Handling of LowerCase, UpperCase and SmallCaps Font Styles
+ if (m_textElementType == TMP_TextElementType.Character)
+ {
+ if ((m_FontStyleInternal & FontStyles.UpperCase) == FontStyles.UpperCase)
+ {
+ // If this character is lowercase, switch to uppercase.
+ if (char.IsLower((char)unicode))
+ unicode = char.ToUpper((char)unicode);
+
+ }
+ else if ((m_FontStyleInternal & FontStyles.LowerCase) == FontStyles.LowerCase)
+ {
+ // If this character is uppercase, switch to lowercase.
+ if (char.IsUpper((char)unicode))
+ unicode = char.ToLower((char)unicode);
+ }
+ else if ((m_FontStyleInternal & FontStyles.SmallCaps) == FontStyles.SmallCaps)
+ {
+ // Only convert lowercase characters to uppercase.
+ if (char.IsLower((char)unicode))
+ unicode = char.ToUpper((char)unicode);
+ }
+ }
+ #endregion
+
+
+ // Lookup the Glyph data for each character and cache it.
+ #region LOOKUP GLYPH
+ character = TMP_FontAssetUtilities.GetCharacterFromFontAsset((uint)unicode, m_currentFontAsset, false, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface, out tempFontAsset);
+
+ // Search for the glyph in the list of fallback assigned to the primary font asset.
+ if (character == null)
+ {
+ if (m_currentFontAsset.fallbackFontAssetTable != null && m_currentFontAsset.fallbackFontAssetTable.Count > 0)
+ character = TMP_FontAssetUtilities.GetCharacterFromFontAssets((uint)unicode, m_currentFontAsset.fallbackFontAssetTable, true, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface, out tempFontAsset);
+ }
+
+ // Search for the glyph in the Sprite Asset assigned to the text object.
+ if (character == null)
+ {
+ TMP_SpriteAsset spriteAsset = this.spriteAsset;
+
+ if (spriteAsset != null)
+ {
+ int spriteIndex = -1;
+
+ // Check Default Sprite Asset and its Fallbacks
+ spriteAsset = TMP_SpriteAsset.SearchForSpriteByUnicode(spriteAsset, (uint)unicode, true, out spriteIndex);
+
+ if (spriteIndex != -1)
+ {
+ m_textElementType = TMP_TextElementType.Sprite;
+ m_textInfo.characterInfo[m_totalCharacterCount].elementType = m_textElementType;
+
+ m_currentMaterialIndex = MaterialReference.AddMaterialReference(spriteAsset.material, spriteAsset, m_materialReferences, m_materialReferenceIndexLookup);
+ m_materialReferences[m_currentMaterialIndex].referenceCount += 1;
+
+ m_textInfo.characterInfo[m_totalCharacterCount].character = (char)unicode;
+ m_textInfo.characterInfo[m_totalCharacterCount].spriteIndex = spriteIndex;
+ m_textInfo.characterInfo[m_totalCharacterCount].fontAsset = m_currentFontAsset;
+ m_textInfo.characterInfo[m_totalCharacterCount].spriteAsset = spriteAsset;
+ m_textInfo.characterInfo[m_totalCharacterCount].textElement = spriteAsset.spriteCharacterTable[m_spriteIndex];
+ m_textInfo.characterInfo[m_totalCharacterCount].materialReferenceIndex = m_currentMaterialIndex;
+ m_textInfo.characterInfo[m_totalCharacterCount].index = chars[i].stringIndex;
+ m_textInfo.characterInfo[m_totalCharacterCount].stringLength = chars[i].length;
+
+ // Restore element type and material index to previous values.
+ m_textElementType = TMP_TextElementType.Character;
+ m_currentMaterialIndex = prev_materialIndex;
+
+ spriteCount += 1;
+ m_totalCharacterCount += 1;
+
+ continue;
+ }
+ }
+ }
+
+ // Search for the glyph in the list of fallback assigned in the TMP Settings (General Fallbacks).
+ if (character == null)
+ {
+ if (TMP_Settings.fallbackFontAssets != null && TMP_Settings.fallbackFontAssets.Count > 0)
+ character = TMP_FontAssetUtilities.GetCharacterFromFontAssets((uint)unicode, TMP_Settings.fallbackFontAssets, true, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface, out tempFontAsset);
+ }
+
+ // Search for the glyph in the Default Font Asset assigned in the TMP Settings file.
+ if (character == null)
+ {
+ if (TMP_Settings.defaultFontAsset != null)
+ character = TMP_FontAssetUtilities.GetCharacterFromFontAsset((uint)unicode, TMP_Settings.defaultFontAsset, true, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface, out tempFontAsset);
+ }
+
+ // TODO: Add support for using Sprite Assets like a special Emoji only Sprite Asset when UTF16 or UTF32 glyphs are requested.
+ // This would kind of mirror native Emoji support.
+ if (character == null)
+ {
+ TMP_SpriteAsset spriteAsset = TMP_Settings.defaultSpriteAsset;
+
+ if (spriteAsset != null)
+ {
+ int spriteIndex = -1;
+
+ // Check Default Sprite Asset and its Fallbacks
+ spriteAsset = TMP_SpriteAsset.SearchForSpriteByUnicode(spriteAsset, (uint)unicode, true, out spriteIndex);
+
+ if (spriteIndex != -1)
+ {
+ m_textElementType = TMP_TextElementType.Sprite;
+ m_textInfo.characterInfo[m_totalCharacterCount].elementType = m_textElementType;
+
+ m_currentMaterialIndex = MaterialReference.AddMaterialReference(spriteAsset.material, spriteAsset, m_materialReferences, m_materialReferenceIndexLookup);
+ m_materialReferences[m_currentMaterialIndex].referenceCount += 1;
+
+ m_textInfo.characterInfo[m_totalCharacterCount].character = (char)unicode;
+ m_textInfo.characterInfo[m_totalCharacterCount].spriteIndex = spriteIndex;
+ m_textInfo.characterInfo[m_totalCharacterCount].fontAsset = m_currentFontAsset;
+ m_textInfo.characterInfo[m_totalCharacterCount].spriteAsset = spriteAsset;
+ m_textInfo.characterInfo[m_totalCharacterCount].textElement = spriteAsset.spriteCharacterTable[m_spriteIndex];
+ m_textInfo.characterInfo[m_totalCharacterCount].materialReferenceIndex = m_currentMaterialIndex;
+ m_textInfo.characterInfo[m_totalCharacterCount].index = chars[i].stringIndex;
+ m_textInfo.characterInfo[m_totalCharacterCount].stringLength = chars[i].length;
+
+ // Restore element type and material index to previous values.
+ m_textElementType = TMP_TextElementType.Character;
+ m_currentMaterialIndex = prev_materialIndex;
+
+ spriteCount += 1;
+ m_totalCharacterCount += 1;
+
+ continue;
+ }
+
+ }
+ }
+
+ //Check if Lowercase or Uppercase variant of the character is available.
+ // Not sure this is necessary anyone as it is very unlikely with recursive search through fallback fonts.
+ //if (glyph == null)
+ //{
+ // if (char.IsLower((char)c))
+ // {
+ // if (m_currentFontAsset.characterDictionary.TryGetValue(char.ToUpper((char)c), out glyph))
+ // c = chars[i] = char.ToUpper((char)c);
+ // }
+ // else if (char.IsUpper((char)c))
+ // {
+ // if (m_currentFontAsset.characterDictionary.TryGetValue(char.ToLower((char)c), out glyph))
+ // c = chars[i] = char.ToLower((char)c);
+ // }
+ //}
+
+ // Replace missing glyph by the Square (9633) glyph or possibly the Space (32) glyph.
+ if (character == null)
+ {
+ // Save the original unicode character
+ int srcGlyph = unicode;
+
+ // Try replacing the missing glyph character by TMP Settings Missing Glyph or Square (9633) character.
+ unicode = chars[i].unicode = TMP_Settings.missingGlyphCharacter == 0 ? 9633 : TMP_Settings.missingGlyphCharacter;
+
+ // Check for the missing glyph character in the currently assigned font asset and its fallbacks
+ character = TMP_FontAssetUtilities.GetCharacterFromFontAsset((uint)unicode, m_currentFontAsset, true, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface, out tempFontAsset);
+
+ if (character == null)
+ {
+ // Search for the missing glyph character in the TMP Settings Fallback list.
+ if (TMP_Settings.fallbackFontAssets != null && TMP_Settings.fallbackFontAssets.Count > 0)
+ character = TMP_FontAssetUtilities.GetCharacterFromFontAssets((uint)unicode, TMP_Settings.fallbackFontAssets, true, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface, out tempFontAsset);
+ }
+
+ if (character == null)
+ {
+ // Search for the missing glyph in the TMP Settings Default Font Asset.
+ if (TMP_Settings.defaultFontAsset != null)
+ character = TMP_FontAssetUtilities.GetCharacterFromFontAsset((uint)unicode, TMP_Settings.defaultFontAsset, true, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface, out tempFontAsset);
+ }
+
+ if (character == null)
+ {
+ // Use Space (32) Glyph from the currently assigned font asset.
+ unicode = chars[i].unicode = 32;
+ character = TMP_FontAssetUtilities.GetCharacterFromFontAsset((uint)unicode, m_currentFontAsset, true, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface, out tempFontAsset);
+ if (!TMP_Settings.warningsDisabled) Debug.LogWarning("Character with ASCII value of " + srcGlyph + " was not found in the Font Asset Glyph Table. It was replaced by a space.", this);
+ }
+ }
+
+ // Determine if the font asset is still the current font asset or a fallback.
+ if (tempFontAsset != null)
+ {
+ if (tempFontAsset.GetInstanceID() != m_currentFontAsset.GetInstanceID())
+ {
+ isUsingFallbackOrAlternativeTypeface = true;
+ m_currentFontAsset = tempFontAsset;
+ }
+ }
+ #endregion
+
+
+ m_textInfo.characterInfo[m_totalCharacterCount].elementType = TMP_TextElementType.Character;
+ m_textInfo.characterInfo[m_totalCharacterCount].textElement = character;
+ m_textInfo.characterInfo[m_totalCharacterCount].isUsingAlternateTypeface = isUsingAlternativeTypeface;
+ m_textInfo.characterInfo[m_totalCharacterCount].character = (char)unicode;
+ m_textInfo.characterInfo[m_totalCharacterCount].fontAsset = m_currentFontAsset;
+ m_textInfo.characterInfo[m_totalCharacterCount].index = chars[i].stringIndex;
+ m_textInfo.characterInfo[m_totalCharacterCount].stringLength = chars[i].length;
+
+ if (isUsingFallbackOrAlternativeTypeface)
+ {
+ // Create Fallback material instance matching current material preset if necessary
+ if (TMP_Settings.matchMaterialPreset)
+ m_currentMaterial = TMP_MaterialManager.GetFallbackMaterial(m_currentMaterial, m_currentFontAsset.material);
+ else
+ m_currentMaterial = m_currentFontAsset.material;
+
+ m_currentMaterialIndex = MaterialReference.AddMaterialReference(m_currentMaterial, m_currentFontAsset, m_materialReferences, m_materialReferenceIndexLookup);
+ }
+
+ if (!char.IsWhiteSpace((char)unicode) && unicode != 0x200B)
+ {
+ // Limit the mesh of the main text object to 65535 vertices and use sub objects for the overflow.
+ if (m_materialReferences[m_currentMaterialIndex].referenceCount < 16383)
+ m_materialReferences[m_currentMaterialIndex].referenceCount += 1;
+ else
+ {
+ m_currentMaterialIndex = MaterialReference.AddMaterialReference(new Material(m_currentMaterial), m_currentFontAsset, m_materialReferences, m_materialReferenceIndexLookup);
+ m_materialReferences[m_currentMaterialIndex].referenceCount += 1;
+ }
+ }
+
+ m_textInfo.characterInfo[m_totalCharacterCount].material = m_currentMaterial;
+ m_textInfo.characterInfo[m_totalCharacterCount].materialReferenceIndex = m_currentMaterialIndex;
+ m_materialReferences[m_currentMaterialIndex].isFallbackMaterial = isUsingFallbackOrAlternativeTypeface;
+
+ // Restore previous font asset and material if fallback font was used.
+ if (isUsingFallbackOrAlternativeTypeface)
+ {
+ m_materialReferences[m_currentMaterialIndex].fallbackMaterial = prev_material;
+ m_currentFontAsset = prev_fontAsset;
+ m_currentMaterial = prev_material;
+ m_currentMaterialIndex = prev_materialIndex;
+ }
+
+ m_totalCharacterCount += 1;
+ }
+
+ // Early return if we are calculating the preferred values.
+ if (m_isCalculatingPreferredValues)
+ {
+ m_isCalculatingPreferredValues = false;
+ m_isInputParsingRequired = true;
+ return m_totalCharacterCount;
+ }
+
+ // Save material and sprite count.
+ m_textInfo.spriteCount = spriteCount;
+ int materialCount = m_textInfo.materialCount = m_materialReferenceIndexLookup.Count;
+
+ // Check if we need to resize the MeshInfo array for handling different materials.
+ if (materialCount > m_textInfo.meshInfo.Length)
+ TMP_TextInfo.Resize(ref m_textInfo.meshInfo, materialCount, false);
+
+ // Resize SubTextObject array if necessary
+ if (materialCount > m_subTextObjects.Length)
+ TMP_TextInfo.Resize(ref m_subTextObjects, Mathf.NextPowerOfTwo(materialCount + 1));
+
+ // Resize CharacterInfo[] if allocations are excessive
+ if (m_textInfo.characterInfo.Length - m_totalCharacterCount > 256)
+ TMP_TextInfo.Resize(ref m_textInfo.characterInfo, Mathf.Max(m_totalCharacterCount + 1, 256), true);
+
+
+ // Iterate through the material references to set the mesh buffer allocations
+ for (int i = 0; i < materialCount; i++)
+ {
+ // Add new sub text object for each material reference
+ if (i > 0)
+ {
+ if (m_subTextObjects[i] == null)
+ {
+ m_subTextObjects[i] = TMP_SubMeshUI.AddSubTextObject(this, m_materialReferences[i]);
+
+ // Not sure this is necessary
+ m_textInfo.meshInfo[i].vertices = null;
+ }
+ //else if (m_subTextObjects[i].gameObject.activeInHierarchy == false)
+ // m_subTextObjects[i].gameObject.SetActive(true);
+
+ // Make sure the pivots are synchronized
+ if (m_rectTransform.pivot != m_subTextObjects[i].rectTransform.pivot)
+ m_subTextObjects[i].rectTransform.pivot = m_rectTransform.pivot;
+
+ // Check if the material has changed.
+ if (m_subTextObjects[i].sharedMaterial == null || m_subTextObjects[i].sharedMaterial.GetInstanceID() != m_materialReferences[i].material.GetInstanceID())
+ {
+ bool isDefaultMaterial = m_materialReferences[i].isDefaultMaterial;
+
+ m_subTextObjects[i].isDefaultMaterial = isDefaultMaterial;
+
+ // Assign new material if we are not using the default material or if the font asset has changed.
+ if (!isDefaultMaterial || m_subTextObjects[i].sharedMaterial == null || m_subTextObjects[i].sharedMaterial.GetTexture(ShaderUtilities.ID_MainTex).GetInstanceID() != m_materialReferences[i].material.GetTexture(ShaderUtilities.ID_MainTex).GetInstanceID())
+ {
+ m_subTextObjects[i].sharedMaterial = m_materialReferences[i].material;
+ m_subTextObjects[i].fontAsset = m_materialReferences[i].fontAsset;
+ m_subTextObjects[i].spriteAsset = m_materialReferences[i].spriteAsset;
+ }
+ }
+
+ // Check if we need to use a Fallback Material
+ if (m_materialReferences[i].isFallbackMaterial)
+ {
+ m_subTextObjects[i].fallbackMaterial = m_materialReferences[i].material;
+ m_subTextObjects[i].fallbackSourceMaterial = m_materialReferences[i].fallbackMaterial;
+ }
+
+ }
+
+ int referenceCount = m_materialReferences[i].referenceCount;
+
+ // Check to make sure our buffers allocations can accommodate the required text elements.
+ if (m_textInfo.meshInfo[i].vertices == null || m_textInfo.meshInfo[i].vertices.Length < referenceCount * 4)
+ {
+ if (m_textInfo.meshInfo[i].vertices == null)
+ {
+ if (i == 0)
+ m_textInfo.meshInfo[i] = new TMP_MeshInfo(m_mesh, referenceCount + 1);
+ else
+ m_textInfo.meshInfo[i] = new TMP_MeshInfo(m_subTextObjects[i].mesh, referenceCount + 1);
+ }
+ else
+ m_textInfo.meshInfo[i].ResizeMeshInfo(referenceCount > 1024 ? referenceCount + 256 : Mathf.NextPowerOfTwo(referenceCount + 1));
+ }
+ else if (m_VertexBufferAutoSizeReduction && referenceCount > 0 && m_textInfo.meshInfo[i].vertices.Length / 4 - referenceCount > 256)
+ {
+ // Resize vertex buffers if allocations are excessive.
+ //Debug.Log("Reducing the size of the vertex buffers.");
+ m_textInfo.meshInfo[i].ResizeMeshInfo(referenceCount > 1024 ? referenceCount + 256 : Mathf.NextPowerOfTwo(referenceCount + 1));
+ }
+ }
+
+ //TMP_MaterialManager.CleanupFallbackMaterials();
+
+ // Clean up unused SubMeshes
+ for (int i = materialCount; i < m_subTextObjects.Length && m_subTextObjects[i] != null; i++)
+ {
+ if (i < m_textInfo.meshInfo.Length)
+ {
+ m_subTextObjects[i].canvasRenderer.SetMesh(null);
+
+ // TODO: Figure out a way to handle this without running into Unity's Rebuild loop issue.
+ //m_subTextObjects[i].gameObject.SetActive(false);
+ }
+ }
+
+ #if TMP_PROFILE_ON
+ Profiler.EndSample();
+ #endif
+
+ return m_totalCharacterCount;
+ }
+
+
+ // Added to sort handle the potential issue with OnWillRenderObject() not getting called when objects are not visible by camera.
+ //void OnBecameInvisible()
+ //{
+ // if (m_mesh != null)
+ // m_mesh.bounds = new Bounds(transform.position, new Vector3(1000, 1000, 0));
+ //}
+
+
+ /// <summary>
+ /// Update the margin width and height
+ /// </summary>
+ public override void ComputeMarginSize()
+ {
+ if (this.rectTransform != null)
+ {
+ //Debug.Log("*** ComputeMarginSize() *** Current RectTransform's Width is " + m_rectTransform.rect.width + " and Height is " + m_rectTransform.rect.height); // + " and size delta is " + m_rectTransform.sizeDelta);
+
+ m_marginWidth = m_rectTransform.rect.width - m_margin.x - m_margin.z;
+ m_marginHeight = m_rectTransform.rect.height - m_margin.y - m_margin.w;
+
+ // Update the corners of the RectTransform
+ m_RectTransformCorners = GetTextContainerLocalCorners();
+ }
+ }
+
+
+ /// <summary>
+ ///
+ /// </summary>
+ protected override void OnDidApplyAnimationProperties()
+ {
+ m_havePropertiesChanged = true;
+ SetVerticesDirty();
+ SetLayoutDirty();
+ //Debug.Log("Animation Properties have changed.");
+ }
+
+
+ protected override void OnCanvasHierarchyChanged()
+ {
+ base.OnCanvasHierarchyChanged();
+ m_canvas = this.canvas;
+ }
+
+
+ protected override void OnTransformParentChanged()
+ {
+ //Debug.Log("***** OnTransformParentChanged *****");
+
+ base.OnTransformParentChanged();
+
+ m_canvas = this.canvas;
+
+ ComputeMarginSize();
+ m_havePropertiesChanged = true;
+ }
+
+
+ protected override void OnRectTransformDimensionsChange()
+ {
+ //Debug.Log("*** OnRectTransformDimensionsChange() *** ActiveInHierarchy: " + this.gameObject.activeInHierarchy + " Frame: " + Time.frameCount);
+
+ // Make sure object is active in Hierarchy
+ if (!this.gameObject.activeInHierarchy)
+ return;
+
+ ComputeMarginSize();
+
+ UpdateSubObjectPivot();
+
+ SetVerticesDirty();
+ SetLayoutDirty();
+ }
+
+
+ /// <summary>
+ /// Function used as a replacement for LateUpdate to check if the transform or scale of the text object has changed.
+ /// </summary>
+ internal override void InternalUpdate()
+ {
+ // We need to update the SDF scale or possibly regenerate the text object if lossy scale has changed.
+ if (m_havePropertiesChanged == false)
+ {
+ float lossyScaleY = m_rectTransform.lossyScale.y;
+ if (lossyScaleY != m_previousLossyScaleY && m_text != string.Empty && m_text != null)
+ {
+ float scaleDelta = lossyScaleY / m_previousLossyScaleY;
+
+ UpdateSDFScale(scaleDelta);
+
+ m_previousLossyScaleY = lossyScaleY;
+ }
+ }
+
+ // Added to handle legacy animation mode.
+ if (m_isUsingLegacyAnimationComponent)
+ {
+ //if (m_havePropertiesChanged)
+ m_havePropertiesChanged = true;
+ OnPreRenderCanvas();
+ }
+ }
+
+
+
+ // Called just before the Canvas is rendered.
+ void OnPreRenderCanvas()
+ {
+ //Debug.Log("*** OnPreRenderCanvas() *** Frame: " + Time.frameCount);
+
+ // Make sure object is active and that we have a valid Canvas.
+ if (!m_isAwake || (this.IsActive() == false && m_ignoreActiveState == false)) return;
+
+ if (m_canvas == null) { m_canvas = this.canvas; if (m_canvas == null) return; }
+
+
+ // Debug Variables
+ loopCountA = 0;
+ //loopCountB = 0;
+ //loopCountC = 0;
+ //loopCountD = 0;
+ //loopCountE = 0;
+
+ // Update Margins
+ //ComputeMarginSize();
+
+ // Update Mask
+ // if (m_isMaskingEnabled)
+ // {
+ // UpdateMask();
+ // }
+
+
+ if (m_havePropertiesChanged || m_isLayoutDirty)
+ {
+ //Debug.Log("Properties have changed!"); // Assigned Material is:" + m_sharedMaterial); // New Text is: " + m_text + ".");
+
+ // Update mesh padding if necessary.
+ if (checkPaddingRequired)
+ UpdateMeshPadding();
+
+ // Reparse the text if the input has changed or text was truncated.
+ if (m_isInputParsingRequired || m_isTextTruncated)
+ ParseInputText();
+
+ // Reset Font min / max used with Auto-sizing
+ if (m_enableAutoSizing)
+ m_fontSize = Mathf.Clamp(m_fontSizeBase, m_fontSizeMin, m_fontSizeMax);
+
+ m_maxFontSize = m_fontSizeMax;
+ m_minFontSize = m_fontSizeMin;
+ m_lineSpacingDelta = 0;
+ m_charWidthAdjDelta = 0;
+ //m_recursiveCount = 0;
+
+ m_isCharacterWrappingEnabled = false;
+ m_isTextTruncated = false;
+
+ m_havePropertiesChanged = false;
+ m_isLayoutDirty = false;
+ m_ignoreActiveState = false;
+
+ GenerateTextMesh();
+ }
+ }
+
+
+
+ /// <summary>
+ /// This is the main function that is responsible for creating / displaying the text.
+ /// </summary>
+ protected override void GenerateTextMesh()
+ {
+ //Debug.Log("*** GenerateTextMesh() ***"); // ***** Frame: " + Time.frameCount); // + ". Point Size: " + m_fontSize + ". Margins are (W) " + m_marginWidth + " (H) " + m_marginHeight); // ". Iteration Count: " + loopCountA + ". Min: " + m_minFontSize + " Max: " + m_maxFontSize + " Delta: " + (m_maxFontSize - m_minFontSize) + " Font size is " + m_fontSize); //called for Object with ID " + GetInstanceID()); // Assigned Material is " + m_uiRenderer.GetMaterial().name); // IncludeForMasking " + this.m_IncludeForMasking); // and text is " + m_text);
+ //Profiler.BeginSample("TMP Generate Text - Pre");
+
+ // Early exit if no font asset was assigned. This should not be needed since LiberationSans SDF will be assigned by default.
+ if (m_fontAsset == null || m_fontAsset.characterLookupTable == null)
+ {
+ Debug.LogWarning("Can't Generate Mesh! No Font Asset has been assigned to Object ID: " + this.GetInstanceID());
+ return;
+ }
+
+ // Clear TextInfo
+ if (m_textInfo != null)
+ m_textInfo.Clear();
+
+ // Early exit if we don't have any Text to generate.
+ if (m_TextParsingBuffer == null || m_TextParsingBuffer.Length == 0 || m_TextParsingBuffer[0].unicode == (char)0)
+ {
+ // Clear mesh and upload changes to the mesh.
+ ClearMesh();
+
+ m_preferredWidth = 0;
+ m_preferredHeight = 0;
+
+ // Event indicating the text has been regenerated.
+ TMPro_EventManager.ON_TEXT_CHANGED(this);
+
+ return;
+ }
+
+ m_currentFontAsset = m_fontAsset;
+ m_currentMaterial = m_sharedMaterial;
+ m_currentMaterialIndex = 0;
+ m_materialReferenceStack.SetDefault(new MaterialReference(m_currentMaterialIndex, m_currentFontAsset, null, m_currentMaterial, m_padding));
+
+ m_currentSpriteAsset = m_spriteAsset;
+
+ // Stop all Sprite Animations
+ if (m_spriteAnimator != null)
+ m_spriteAnimator.StopAllAnimations();
+
+ // Total character count is computed when the text is parsed.
+ int totalCharacterCount = m_totalCharacterCount;
+
+ // Calculate the scale of the font based on selected font size and sampling point size.
+ // baseScale is calculated using the font asset assigned to the text object.
+ float baseScale = m_fontScale = (m_fontSize / m_fontAsset.faceInfo.pointSize * m_fontAsset.faceInfo.scale);
+ float currentElementScale = baseScale;
+ m_fontScaleMultiplier = 1;
+
+ m_currentFontSize = m_fontSize;
+ m_sizeStack.SetDefault(m_currentFontSize);
+ float fontSizeDelta = 0;
+
+ int charCode = 0; // Holds the character code of the currently being processed character.
+
+ m_FontStyleInternal = m_fontStyle; // Set the default style.
+ m_FontWeightInternal = (m_FontStyleInternal & FontStyles.Bold) == FontStyles.Bold ? FontWeight.Bold : m_fontWeight;
+ m_FontWeightStack.SetDefault(m_FontWeightInternal);
+ m_fontStyleStack.Clear();
+
+ m_lineJustification = m_textAlignment; // Sets the line justification mode to match editor alignment.
+ m_lineJustificationStack.SetDefault(m_lineJustification);
+
+ float padding = 0;
+ float style_padding = 0; // Extra padding required to accommodate Bold style.
+ float bold_xAdvance_multiplier = 1; // Used to increase spacing between character when style is bold.
+
+ m_baselineOffset = 0; // Used by subscript characters.
+ m_baselineOffsetStack.Clear();
+
+ // Underline
+ bool beginUnderline = false;
+ Vector3 underline_start = Vector3.zero; // Used to track where underline starts & ends.
+ Vector3 underline_end = Vector3.zero;
+
+ // Strike-through
+ bool beginStrikethrough = false;
+ Vector3 strikethrough_start = Vector3.zero;
+ Vector3 strikethrough_end = Vector3.zero;
+
+ // Text Highlight
+ bool beginHighlight = false;
+ Vector3 highlight_start = Vector3.zero;
+ Vector3 highlight_end = Vector3.zero;
+
+ m_fontColor32 = m_fontColor;
+ Color32 vertexColor;
+ m_htmlColor = m_fontColor32;
+ m_underlineColor = m_htmlColor;
+ m_strikethroughColor = m_htmlColor;
+
+ m_colorStack.SetDefault(m_htmlColor);
+ m_underlineColorStack.SetDefault(m_htmlColor);
+ m_strikethroughColorStack.SetDefault(m_htmlColor);
+ m_highlightColorStack.SetDefault(m_htmlColor);
+
+ m_colorGradientPreset = null;
+ m_colorGradientStack.SetDefault(null);
+
+ // Clear the Style stack.
+ //m_styleStack.Clear();
+
+ // Clear the Action stack.
+ m_actionStack.Clear();
+
+ m_isFXMatrixSet = false;
+
+ m_lineOffset = 0; // Amount of space between lines (font line spacing + m_linespacing).
+ m_lineHeight = TMP_Math.FLOAT_UNSET;
+ float lineGap = m_currentFontAsset.faceInfo.lineHeight - (m_currentFontAsset.faceInfo.ascentLine - m_currentFontAsset.faceInfo.descentLine);
+
+ m_cSpacing = 0; // Amount of space added between characters as a result of the use of the <cspace> tag.
+ m_monoSpacing = 0;
+ float lineOffsetDelta = 0;
+ m_xAdvance = 0; // Used to track the position of each character.
+
+ tag_LineIndent = 0; // Used for indentation of text.
+ tag_Indent = 0;
+ m_indentStack.SetDefault(0);
+ tag_NoParsing = false;
+ //m_isIgnoringAlignment = false;
+
+ m_characterCount = 0; // Total characters in the char[]
+
+ // Tracking of line information
+ m_firstCharacterOfLine = 0;
+ m_lastCharacterOfLine = 0;
+ m_firstVisibleCharacterOfLine = 0;
+ m_lastVisibleCharacterOfLine = 0;
+ m_maxLineAscender = k_LargeNegativeFloat;
+ m_maxLineDescender = k_LargePositiveFloat;
+ m_lineNumber = 0;
+ m_lineVisibleCharacterCount = 0;
+ bool isStartOfNewLine = true;
+ m_firstOverflowCharacterIndex = -1;
+
+ m_pageNumber = 0;
+ int pageToDisplay = Mathf.Clamp(m_pageToDisplay - 1, 0, m_textInfo.pageInfo.Length - 1);
+ int previousPageOverflowChar = 0;
+
+ int ellipsisIndex = 0;
+
+ Vector4 margins = m_margin;
+ float marginWidth = m_marginWidth;
+ float marginHeight = m_marginHeight;
+ m_marginLeft = 0;
+ m_marginRight = 0;
+ m_width = -1;
+ float width = marginWidth + 0.0001f - m_marginLeft - m_marginRight;
+
+ // Need to initialize these Extents structures
+ m_meshExtents.min = k_LargePositiveVector2;
+ m_meshExtents.max = k_LargeNegativeVector2;
+
+ // Initialize lineInfo
+ m_textInfo.ClearLineInfo();
+
+ // Tracking of the highest Ascender
+ m_maxCapHeight = 0;
+ m_maxAscender = 0;
+ m_maxDescender = 0;
+ float pageAscender = 0;
+ float maxVisibleDescender = 0;
+ bool isMaxVisibleDescenderSet = false;
+ m_isNewPage = false;
+
+ // Initialize struct to track states of word wrapping
+ bool isFirstWord = true;
+ m_isNonBreakingSpace = false;
+ bool ignoreNonBreakingSpace = false;
+ bool isLastBreakingChar = false;
+ float linebreakingWidth = 0;
+ int wrappingIndex = 0;
+
+ // Save character and line state before we begin layout.
+ SaveWordWrappingState(ref m_SavedWordWrapState, -1, -1);
+ SaveWordWrappingState(ref m_SavedLineState, -1, -1);
+
+ loopCountA += 1;
+ //Profiler.EndSample();
+
+ #if TMP_PROFILE_PHASES_ON
+ Profiler.BeginSample("TMP Generate Text - Phase I");
+ #endif
+
+ // Parse through Character buffer to read HTML tags and begin creating mesh.
+ for (int i = 0; i < m_TextParsingBuffer.Length && m_TextParsingBuffer[i].unicode != 0; i++)
+ {
+ charCode = m_TextParsingBuffer[i].unicode;
+
+ // Parse Rich Text Tag
+ #region Parse Rich Text Tag
+ if (m_isRichText && charCode == 60) // '<'
+ {
+ m_isParsingText = true;
+ m_textElementType = TMP_TextElementType.Character;
+
+ // Check if Tag is valid. If valid, skip to the end of the validated tag.
+ if (ValidateHtmlTag(m_TextParsingBuffer, i + 1, out int tagEndIndex))
+ {
+ i = tagEndIndex;
+
+ // Continue to next character or handle the sprite element
+ if (m_textElementType == TMP_TextElementType.Character)
+ continue;
+ }
+ }
+ else
+ {
+ m_textElementType = m_textInfo.characterInfo[m_characterCount].elementType;
+ m_currentMaterialIndex = m_textInfo.characterInfo[m_characterCount].materialReferenceIndex;
+ m_currentFontAsset = m_textInfo.characterInfo[m_characterCount].fontAsset;
+ }
+ #endregion End Parse Rich Text Tag
+
+ int prev_MaterialIndex = m_currentMaterialIndex;
+ bool isUsingAltTypeface = m_textInfo.characterInfo[m_characterCount].isUsingAlternateTypeface;
+
+ m_isParsingText = false;
+
+ // When using Linked text, mark character as ignored and skip to next character.
+ if (m_characterCount < m_firstVisibleCharacter)
+ {
+ m_textInfo.characterInfo[m_characterCount].isVisible = false;
+ m_textInfo.characterInfo[m_characterCount].character = (char)0x200B;
+ m_characterCount += 1;
+ continue;
+ }
+
+ // Handle Font Styles like LowerCase, UpperCase and SmallCaps.
+ #region Handling of LowerCase, UpperCase and SmallCaps Font Styles
+ #if TMP_PROFILE_ON
+ Profiler.BeginSample("Handle Font Style");
+ #endif
+ float smallCapsMultiplier = 1.0f;
+
+ if (m_textElementType == TMP_TextElementType.Character)
+ {
+ if ((m_FontStyleInternal & FontStyles.UpperCase) == FontStyles.UpperCase)
+ {
+ // If this character is lowercase, switch to uppercase.
+ if (char.IsLower((char)charCode))
+ charCode = char.ToUpper((char)charCode);
+
+ }
+ else if ((m_FontStyleInternal & FontStyles.LowerCase) == FontStyles.LowerCase)
+ {
+ // If this character is uppercase, switch to lowercase.
+ if (char.IsUpper((char)charCode))
+ charCode = char.ToLower((char)charCode);
+ }
+ else if ((m_FontStyleInternal & FontStyles.SmallCaps) == FontStyles.SmallCaps)
+ {
+ if (char.IsLower((char)charCode))
+ {
+ smallCapsMultiplier = 0.8f;
+ charCode = char.ToUpper((char)charCode);
+ }
+ }
+ }
+ #if TMP_PROFILE_ON
+ Profiler.EndSample();
+ #endif
+ #endregion
+
+
+ // Look up Character Data from Dictionary and cache it.
+ #region Look up Character Data
+ #if TMP_PROFILE_ON
+ Profiler.BeginSample("Lookup Character Data");
+ #endif
+ if (m_textElementType == TMP_TextElementType.Sprite)
+ {
+ // If a sprite is used as a fallback then get a reference to it and set the color to white.
+ m_currentSpriteAsset = m_textInfo.characterInfo[m_characterCount].spriteAsset;
+ m_spriteIndex = m_textInfo.characterInfo[m_characterCount].spriteIndex;
+
+ TMP_SpriteCharacter sprite = m_currentSpriteAsset.spriteCharacterTable[m_spriteIndex];
+ if (sprite == null) continue;
+
+ // Sprites are assigned in the E000 Private Area + sprite Index
+ if (charCode == 60)
+ charCode = 57344 + m_spriteIndex;
+ else
+ m_spriteColor = s_colorWhite;
+
+ // The sprite scale calculations are based on the font asset assigned to the text object.
+ float spriteScale = (m_currentFontSize / m_currentFontAsset.faceInfo.pointSize * m_currentFontAsset.faceInfo.scale);
+ currentElementScale = m_currentFontAsset.faceInfo.ascentLine / sprite.glyph.metrics.height * sprite.scale * sprite.glyph.scale * spriteScale;
+
+ m_cached_TextElement = sprite;
+
+ m_textInfo.characterInfo[m_characterCount].elementType = TMP_TextElementType.Sprite;
+ m_textInfo.characterInfo[m_characterCount].scale = spriteScale;
+ m_textInfo.characterInfo[m_characterCount].spriteAsset = m_currentSpriteAsset;
+ m_textInfo.characterInfo[m_characterCount].fontAsset = m_currentFontAsset;
+ m_textInfo.characterInfo[m_characterCount].materialReferenceIndex = m_currentMaterialIndex;
+
+ m_currentMaterialIndex = prev_MaterialIndex;
+
+ padding = 0;
+ }
+ else if (m_textElementType == TMP_TextElementType.Character)
+ {
+ m_cached_TextElement = m_textInfo.characterInfo[m_characterCount].textElement;
+ if (m_cached_TextElement == null) continue;
+
+ m_currentFontAsset = m_textInfo.characterInfo[m_characterCount].fontAsset;
+ m_currentMaterial = m_textInfo.characterInfo[m_characterCount].material;
+ m_currentMaterialIndex = m_textInfo.characterInfo[m_characterCount].materialReferenceIndex;
+
+ // Re-calculate font scale as the font asset may have changed.
+ m_fontScale = m_currentFontSize * smallCapsMultiplier / m_currentFontAsset.faceInfo.pointSize * m_currentFontAsset.faceInfo.scale;
+
+ currentElementScale = m_fontScale * m_fontScaleMultiplier * m_cached_TextElement.scale * m_cached_TextElement.glyph.scale;
+
+ m_textInfo.characterInfo[m_characterCount].elementType = TMP_TextElementType.Character;
+ m_textInfo.characterInfo[m_characterCount].scale = currentElementScale;
+
+ padding = m_currentMaterialIndex == 0 ? m_padding : m_subTextObjects[m_currentMaterialIndex].padding;
+ }
+ #if TMP_PROFILE_ON
+ Profiler.EndSample();
+ #endif
+ #endregion
+
+
+ // Handle Soft Hyphen
+ #region Handle Soft Hyphen
+ float old_scale = currentElementScale;
+ if (charCode == 0xAD)
+ {
+ currentElementScale = 0;
+ }
+ #endregion
+
+
+ // Store some of the text object's information
+ m_textInfo.characterInfo[m_characterCount].character = (char)charCode;
+ m_textInfo.characterInfo[m_characterCount].pointSize = m_currentFontSize;
+ m_textInfo.characterInfo[m_characterCount].color = m_htmlColor;
+ m_textInfo.characterInfo[m_characterCount].underlineColor = m_underlineColor;
+ m_textInfo.characterInfo[m_characterCount].strikethroughColor = m_strikethroughColor;
+ m_textInfo.characterInfo[m_characterCount].highlightColor = m_highlightColor;
+ m_textInfo.characterInfo[m_characterCount].style = m_FontStyleInternal;
+ //m_textInfo.characterInfo[m_characterCount].index = m_TextParsingBuffer[i].stringIndex;
+ //m_textInfo.characterInfo[m_characterCount].isIgnoringAlignment = m_isIgnoringAlignment;
+
+
+ // Handle Kerning if Enabled.
+ #region Handle Kerning
+ TMP_GlyphValueRecord glyphAdjustments = new TMP_GlyphValueRecord();
+ float characterSpacingAdjustment = m_characterSpacing;
+ if (m_enableKerning)
+ {
+ if (m_characterCount < totalCharacterCount - 1)
+ {
+ uint firstGlyphIndex = m_cached_TextElement.glyphIndex;
+ uint secondGlyphIndex = m_textInfo.characterInfo[m_characterCount + 1].textElement.glyphIndex;
+ long key = new GlyphPairKey(firstGlyphIndex, secondGlyphIndex).key;
+
+ if (m_currentFontAsset.fontFeatureTable.m_GlyphPairAdjustmentRecordLookupDictionary.TryGetValue(key, out TMP_GlyphPairAdjustmentRecord adjustmentPair))
+ {
+ glyphAdjustments = adjustmentPair.firstAdjustmentRecord.glyphValueRecord;
+ characterSpacingAdjustment = (adjustmentPair.featureLookupFlags & FontFeatureLookupFlags.IgnoreSpacingAdjustments) == FontFeatureLookupFlags.IgnoreSpacingAdjustments ? 0 : characterSpacingAdjustment;
+ }
+ }
+
+ if (m_characterCount >= 1)
+ {
+ uint firstGlyphIndex = m_textInfo.characterInfo[m_characterCount - 1].textElement.glyphIndex;
+ uint secondGlyphIndex = m_cached_TextElement.glyphIndex;
+ long key = new GlyphPairKey(firstGlyphIndex, secondGlyphIndex).key;
+
+ if (m_currentFontAsset.fontFeatureTable.m_GlyphPairAdjustmentRecordLookupDictionary.TryGetValue(key, out TMP_GlyphPairAdjustmentRecord adjustmentPair))
+ {
+ glyphAdjustments += adjustmentPair.secondAdjustmentRecord.glyphValueRecord;
+ characterSpacingAdjustment = (adjustmentPair.featureLookupFlags & FontFeatureLookupFlags.IgnoreSpacingAdjustments) == FontFeatureLookupFlags.IgnoreSpacingAdjustments ? 0 : characterSpacingAdjustment;
+ }
+ }
+ }
+ #endregion
+
+
+ // Initial Implementation for RTL support.
+ #region Handle Right-to-Left
+ if (m_isRightToLeft)
+ {
+ m_xAdvance -= ((m_cached_TextElement.glyph.metrics.horizontalAdvance * bold_xAdvance_multiplier + characterSpacingAdjustment + m_wordSpacing + m_currentFontAsset.normalSpacingOffset) * currentElementScale + m_cSpacing) * (1 - m_charWidthAdjDelta);
+
+ if (char.IsWhiteSpace((char)charCode) || charCode == 0x200B)
+ m_xAdvance -= m_wordSpacing * currentElementScale;
+ }
+ #endregion
+
+
+ // Handle Mono Spacing
+ #region Handle Mono Spacing
+ float monoAdvance = 0;
+ if (m_monoSpacing != 0)
+ {
+ monoAdvance = (m_monoSpacing / 2 - (m_cached_TextElement.glyph.metrics.width / 2 + m_cached_TextElement.glyph.metrics.horizontalBearingX) * currentElementScale) * (1 - m_charWidthAdjDelta);
+ m_xAdvance += monoAdvance;
+ }
+ #endregion
+
+
+ // Set Padding based on selected font style
+ #region Handle Style Padding
+ if (m_textElementType == TMP_TextElementType.Character && !isUsingAltTypeface && ((m_FontStyleInternal & FontStyles.Bold) == FontStyles.Bold)) // Checks for any combination of Bold Style.
+ {
+ if (m_currentMaterial.HasProperty(ShaderUtilities.ID_GradientScale))
+ {
+ float gradientScale = m_currentMaterial.GetFloat(ShaderUtilities.ID_GradientScale);
+ style_padding = m_currentFontAsset.boldStyle / 4.0f * gradientScale * m_currentMaterial.GetFloat(ShaderUtilities.ID_ScaleRatio_A);
+
+ // Clamp overall padding to Gradient Scale size.
+ if (style_padding + padding > gradientScale)
+ padding = gradientScale - style_padding;
+ }
+ else
+ style_padding = 0;
+
+ bold_xAdvance_multiplier = 1 + m_currentFontAsset.boldSpacing * 0.01f;
+ }
+ else
+ {
+ if (m_currentMaterial.HasProperty(ShaderUtilities.ID_GradientScale))
+ {
+ float gradientScale = m_currentMaterial.GetFloat(ShaderUtilities.ID_GradientScale);
+ style_padding = m_currentFontAsset.normalStyle / 4.0f * gradientScale * m_currentMaterial.GetFloat(ShaderUtilities.ID_ScaleRatio_A);
+
+ // Clamp overall padding to Gradient Scale size.
+ if (style_padding + padding > gradientScale)
+ padding = gradientScale - style_padding;
+ }
+ else
+ style_padding = 0;
+
+ bold_xAdvance_multiplier = 1.0f;
+ }
+ #endregion Handle Style Padding
+
+
+ // Determine the position of the vertices of the Character or Sprite.
+ #region Calculate Vertices Position
+ #if TMP_PROFILE_ON
+ Profiler.BeginSample("Calculate Vertices Position");
+ #endif
+ float fontBaseLineOffset = m_currentFontAsset.faceInfo.baseline * m_fontScale * m_fontScaleMultiplier * m_currentFontAsset.faceInfo.scale;
+ Vector3 top_left;
+ top_left.x = m_xAdvance + ((m_cached_TextElement.glyph.metrics.horizontalBearingX - padding - style_padding + glyphAdjustments.xPlacement) * currentElementScale * (1 - m_charWidthAdjDelta));
+ top_left.y = fontBaseLineOffset + (m_cached_TextElement.glyph.metrics.horizontalBearingY + padding + glyphAdjustments.yPlacement) * currentElementScale - m_lineOffset + m_baselineOffset;
+ top_left.z = 0;
+
+ Vector3 bottom_left;
+ bottom_left.x = top_left.x;
+ bottom_left.y = top_left.y - ((m_cached_TextElement.glyph.metrics.height + padding * 2) * currentElementScale);
+ bottom_left.z = 0;
+
+ Vector3 top_right;
+ top_right.x = bottom_left.x + ((m_cached_TextElement.glyph.metrics.width + padding * 2 + style_padding * 2) * currentElementScale * (1 - m_charWidthAdjDelta));
+ top_right.y = top_left.y;
+ top_right.z = 0;
+
+ Vector3 bottom_right;
+ bottom_right.x = top_right.x;
+ bottom_right.y = bottom_left.y;
+ bottom_right.z = 0;
+
+ #if TMP_PROFILE_ON
+ Profiler.EndSample();
+ #endif
+ #endregion
+
+
+ // Check if we need to Shear the rectangles for Italic styles
+ #region Handle Italic & Shearing
+ if (m_textElementType == TMP_TextElementType.Character && !isUsingAltTypeface && ((m_FontStyleInternal & FontStyles.Italic) == FontStyles.Italic))
+ {
+ // Shift Top vertices forward by half (Shear Value * height of character) and Bottom vertices back by same amount.
+ float shear_value = m_currentFontAsset.italicStyle * 0.01f;
+ Vector3 topShear = new Vector3(shear_value * ((m_cached_TextElement.glyph.metrics.horizontalBearingY + padding + style_padding) * currentElementScale), 0, 0);
+ Vector3 bottomShear = new Vector3(shear_value * (((m_cached_TextElement.glyph.metrics.horizontalBearingY - m_cached_TextElement.glyph.metrics.height - padding - style_padding)) * currentElementScale), 0, 0);
+
+ top_left = top_left + topShear;
+ bottom_left = bottom_left + bottomShear;
+ top_right = top_right + topShear;
+ bottom_right = bottom_right + bottomShear;
+ }
+ #endregion Handle Italics & Shearing
+
+
+ // Handle Character Rotation
+ #region Handle Character Rotation
+ if (m_isFXMatrixSet)
+ {
+ // Apply scale matrix when simulating Condensed text.
+ if (m_FXMatrix.lossyScale.x != 1)
+ {
+ //top_left = m_FXMatrix.MultiplyPoint3x4(top_left);
+ //bottom_left = m_FXMatrix.MultiplyPoint3x4(bottom_left);
+ //top_right = m_FXMatrix.MultiplyPoint3x4(top_right);
+ //bottom_right = m_FXMatrix.MultiplyPoint3x4(bottom_right);
+ }
+
+ Vector3 positionOffset = (top_right + bottom_left) / 2;
+
+ top_left = m_FXMatrix.MultiplyPoint3x4(top_left - positionOffset) + positionOffset;
+ bottom_left = m_FXMatrix.MultiplyPoint3x4(bottom_left - positionOffset) + positionOffset;
+ top_right = m_FXMatrix.MultiplyPoint3x4(top_right - positionOffset) + positionOffset;
+ bottom_right = m_FXMatrix.MultiplyPoint3x4(bottom_right - positionOffset) + positionOffset;
+ }
+ #endregion
+
+
+ // Store vertex information for the character or sprite.
+ m_textInfo.characterInfo[m_characterCount].bottomLeft = bottom_left;
+ m_textInfo.characterInfo[m_characterCount].topLeft = top_left;
+ m_textInfo.characterInfo[m_characterCount].topRight = top_right;
+ m_textInfo.characterInfo[m_characterCount].bottomRight = bottom_right;
+
+ m_textInfo.characterInfo[m_characterCount].origin = m_xAdvance;
+ m_textInfo.characterInfo[m_characterCount].baseLine = fontBaseLineOffset - m_lineOffset + m_baselineOffset;
+ m_textInfo.characterInfo[m_characterCount].aspectRatio = (top_right.x - bottom_left.x) / (top_left.y - bottom_left.y);
+
+
+ // Compute and save text element Ascender and maximum line Ascender.
+ float elementAscender = m_currentFontAsset.faceInfo.ascentLine * (m_textElementType == TMP_TextElementType.Character ? currentElementScale / smallCapsMultiplier : m_textInfo.characterInfo[m_characterCount].scale) + m_baselineOffset;
+ m_textInfo.characterInfo[m_characterCount].ascender = elementAscender - m_lineOffset;
+ m_maxLineAscender = elementAscender > m_maxLineAscender ? elementAscender : m_maxLineAscender;
+
+ // Compute and save text element Descender and maximum line Descender.
+ float elementDescender = m_currentFontAsset.faceInfo.descentLine * (m_textElementType == TMP_TextElementType.Character ? currentElementScale / smallCapsMultiplier : m_textInfo.characterInfo[m_characterCount].scale) + m_baselineOffset;
+ float elementDescenderII = m_textInfo.characterInfo[m_characterCount].descender = elementDescender - m_lineOffset;
+ m_maxLineDescender = elementDescender < m_maxLineDescender ? elementDescender : m_maxLineDescender;
+
+ // Adjust maxLineAscender and maxLineDescender if style is superscript or subscript
+ if ((m_FontStyleInternal & FontStyles.Subscript) == FontStyles.Subscript || (m_FontStyleInternal & FontStyles.Superscript) == FontStyles.Superscript)
+ {
+ float baseAscender = (elementAscender - m_baselineOffset) / m_currentFontAsset.faceInfo.subscriptSize;
+ elementAscender = m_maxLineAscender;
+ m_maxLineAscender = baseAscender > m_maxLineAscender ? baseAscender : m_maxLineAscender;
+
+ float baseDescender = (elementDescender - m_baselineOffset) / m_currentFontAsset.faceInfo.subscriptSize;
+ elementDescender = m_maxLineDescender;
+ m_maxLineDescender = baseDescender < m_maxLineDescender ? baseDescender : m_maxLineDescender;
+ }
+
+ if (m_lineNumber == 0 || m_isNewPage)
+ {
+ m_maxAscender = m_maxAscender > elementAscender ? m_maxAscender : elementAscender;
+ m_maxCapHeight = Mathf.Max(m_maxCapHeight, m_currentFontAsset.faceInfo.capLine * currentElementScale / smallCapsMultiplier);
+ }
+ if (m_lineOffset == 0) pageAscender = pageAscender > elementAscender ? pageAscender : elementAscender;
+
+
+ // Set Characters to not visible by default.
+ m_textInfo.characterInfo[m_characterCount].isVisible = false;
+
+ // Setup Mesh for visible text elements. ie. not a SPACE / LINEFEED / CARRIAGE RETURN.
+ #region Handle Visible Characters
+ //#if TMP_PROFILE_ON
+ //Profiler.BeginSample("Handle Visible Characters");
+ //#endif
+ if (charCode == 9 || charCode == 0xA0 || charCode == 0x2007 || (!char.IsWhiteSpace((char)charCode) && charCode != 0x200B) || m_textElementType == TMP_TextElementType.Sprite)
+ {
+ m_textInfo.characterInfo[m_characterCount].isVisible = true;
+
+ #region Experimental Margin Shaper
+ //Vector2 shapedMargins;
+ //if (marginShaper)
+ //{
+ // shapedMargins = m_marginShaper.GetShapedMargins(m_textInfo.characterInfo[m_characterCount].baseLine);
+ // if (shapedMargins.x < margins.x)
+ // {
+ // shapedMargins.x = m_marginLeft;
+ // }
+ // else
+ // {
+ // shapedMargins.x += m_marginLeft - margins.x;
+ // }
+ // if (shapedMargins.y < margins.z)
+ // {
+ // shapedMargins.y = m_marginRight;
+ // }
+ // else
+ // {
+ // shapedMargins.y += m_marginRight - margins.z;
+ // }
+ //}
+ //else
+ //{
+ // shapedMargins.x = m_marginLeft;
+ // shapedMargins.y = m_marginRight;
+ //}
+ //width = marginWidth + 0.0001f - shapedMargins.x - shapedMargins.y;
+ //if (m_width != -1 && m_width < width)
+ //{
+ // width = m_width;
+ //}
+ //m_textInfo.lineInfo[m_lineNumber].marginLeft = shapedMargins.x;
+ #endregion
+
+ width = m_width != -1 ? Mathf.Min(marginWidth + 0.0001f - m_marginLeft - m_marginRight, m_width) : marginWidth + 0.0001f - m_marginLeft - m_marginRight;
+ m_textInfo.lineInfo[m_lineNumber].marginLeft = m_marginLeft;
+
+ bool isJustifiedOrFlush = ((_HorizontalAlignmentOptions)m_lineJustification & _HorizontalAlignmentOptions.Flush) == _HorizontalAlignmentOptions.Flush || ((_HorizontalAlignmentOptions)m_lineJustification & _HorizontalAlignmentOptions.Justified) == _HorizontalAlignmentOptions.Justified;
+
+ // Calculate the line breaking width of the text.
+ linebreakingWidth = Mathf.Abs(m_xAdvance) + (!m_isRightToLeft ? m_cached_TextElement.glyph.metrics.horizontalAdvance : 0) * (1 - m_charWidthAdjDelta) * (charCode != 0xAD ? currentElementScale : old_scale);
+
+ // Check if Character exceeds the width of the Text Container
+ #region Handle Line Breaking, Text Auto-Sizing and Horizontal Overflow
+ if (linebreakingWidth > width * (isJustifiedOrFlush ? 1.05f : 1.0f))
+ {
+ ellipsisIndex = m_characterCount - 1; // Last safely rendered character
+
+ // Word Wrapping
+ #region Handle Word Wrapping
+ if (enableWordWrapping && m_characterCount != m_firstCharacterOfLine)
+ {
+ // Check if word wrapping is still possible
+ #region Line Breaking Check
+ if (wrappingIndex == m_SavedWordWrapState.previous_WordBreak || isFirstWord)
+ {
+ // Word wrapping is no longer possible. Shrink size of text if auto-sizing is enabled.
+ if (m_enableAutoSizing && m_fontSize > m_fontSizeMin)
+ {
+ // Handle Character Width Adjustments
+ #region Character Width Adjustments
+ if (m_charWidthAdjDelta < m_charWidthMaxAdj / 100)
+ {
+ loopCountA = 0;
+ m_charWidthAdjDelta += 0.01f;
+ GenerateTextMesh();
+ return;
+ }
+ #endregion
+
+ // Adjust Point Size
+ m_maxFontSize = m_fontSize;
+
+ m_fontSize -= Mathf.Max((m_fontSize - m_minFontSize) / 2, 0.05f);
+ m_fontSize = (int)(Mathf.Max(m_fontSize, m_fontSizeMin) * 20 + 0.5f) / 20f;
+
+ if (loopCountA > 20) return; // Added to debug
+ GenerateTextMesh();
+ return;
+ }
+
+ // Word wrapping is no longer possible, now breaking up individual words.
+ if (m_isCharacterWrappingEnabled == false)
+ {
+ if (ignoreNonBreakingSpace == false)
+ ignoreNonBreakingSpace = true;
+ else
+ m_isCharacterWrappingEnabled = true;
+ }
+ else
+ isLastBreakingChar = true;
+
+ //m_recursiveCount += 1;
+ //if (m_recursiveCount > 20)
+ //{
+ // Debug.Log("Recursive count exceeded!");
+ // continue;
+ //}
+ }
+ #endregion
+
+ // Restore to previously stored state of last valid (space character or linefeed)
+ i = RestoreWordWrappingState(ref m_SavedWordWrapState);
+ wrappingIndex = i; // Used to detect when line length can no longer be reduced.
+
+ // Handling for Soft Hyphen
+ if (m_TextParsingBuffer[i].unicode == 0xAD) // && !m_isCharacterWrappingEnabled) // && ellipsisIndex != i && !m_isCharacterWrappingEnabled)
+ {
+ m_isTextTruncated = true;
+ m_TextParsingBuffer[i].unicode = 0x2D;
+ GenerateTextMesh();
+ return;
+ }
+
+ //Debug.Log("Last Visible Character of line # " + m_lineNumber + " is [" + m_textInfo.characterInfo[m_lastVisibleCharacterOfLine].character + " Character Count: " + m_characterCount + " Last visible: " + m_lastVisibleCharacterOfLine);
+
+ // Check if Line Spacing of previous line needs to be adjusted.
+ if (m_lineNumber > 0 && !TMP_Math.Approximately(m_maxLineAscender, m_startOfLineAscender) && m_lineHeight == TMP_Math.FLOAT_UNSET && !m_isNewPage)
+ {
+ //Debug.Log("(Line Break - Adjusting Line Spacing on line #" + m_lineNumber);
+ float offsetDelta = m_maxLineAscender - m_startOfLineAscender;
+ AdjustLineOffset(m_firstCharacterOfLine, m_characterCount, offsetDelta);
+ m_lineOffset += offsetDelta;
+ m_SavedWordWrapState.lineOffset = m_lineOffset;
+ m_SavedWordWrapState.previousLineAscender = m_maxLineAscender;
+
+ // TODO - Add check for character exceeding vertical bounds
+ }
+ m_isNewPage = false;
+
+ // Calculate lineAscender & make sure if last character is superscript or subscript that we check that as well.
+ float lineAscender = m_maxLineAscender - m_lineOffset;
+ float lineDescender = m_maxLineDescender - m_lineOffset;
+
+
+ // Update maxDescender and maxVisibleDescender
+ m_maxDescender = m_maxDescender < lineDescender ? m_maxDescender : lineDescender;
+ if (!isMaxVisibleDescenderSet)
+ maxVisibleDescender = m_maxDescender;
+
+ if (m_useMaxVisibleDescender && (m_characterCount >= m_maxVisibleCharacters || m_lineNumber >= m_maxVisibleLines))
+ isMaxVisibleDescenderSet = true;
+
+ // Track & Store lineInfo for the new line
+ m_textInfo.lineInfo[m_lineNumber].firstCharacterIndex = m_firstCharacterOfLine;
+ m_textInfo.lineInfo[m_lineNumber].firstVisibleCharacterIndex = m_firstVisibleCharacterOfLine = m_firstCharacterOfLine > m_firstVisibleCharacterOfLine ? m_firstCharacterOfLine : m_firstVisibleCharacterOfLine;
+ m_textInfo.lineInfo[m_lineNumber].lastCharacterIndex = m_lastCharacterOfLine = m_characterCount - 1 > 0 ? m_characterCount - 1 : 0;
+ m_textInfo.lineInfo[m_lineNumber].lastVisibleCharacterIndex = m_lastVisibleCharacterOfLine = m_lastVisibleCharacterOfLine < m_firstVisibleCharacterOfLine ? m_firstVisibleCharacterOfLine : m_lastVisibleCharacterOfLine;
+
+ m_textInfo.lineInfo[m_lineNumber].characterCount = m_textInfo.lineInfo[m_lineNumber].lastCharacterIndex - m_textInfo.lineInfo[m_lineNumber].firstCharacterIndex + 1;
+ m_textInfo.lineInfo[m_lineNumber].visibleCharacterCount = m_lineVisibleCharacterCount;
+ m_textInfo.lineInfo[m_lineNumber].lineExtents.min = new Vector2(m_textInfo.characterInfo[m_firstVisibleCharacterOfLine].bottomLeft.x, lineDescender);
+ m_textInfo.lineInfo[m_lineNumber].lineExtents.max = new Vector2(m_textInfo.characterInfo[m_lastVisibleCharacterOfLine].topRight.x, lineAscender);
+ m_textInfo.lineInfo[m_lineNumber].length = m_textInfo.lineInfo[m_lineNumber].lineExtents.max.x;
+ m_textInfo.lineInfo[m_lineNumber].width = width;
+
+ //m_textInfo.lineInfo[m_lineNumber].alignment = m_lineJustification;
+
+ m_textInfo.lineInfo[m_lineNumber].maxAdvance = m_textInfo.characterInfo[m_lastVisibleCharacterOfLine].xAdvance - (characterSpacingAdjustment + m_currentFontAsset.normalSpacingOffset) * currentElementScale - m_cSpacing;
+
+ m_textInfo.lineInfo[m_lineNumber].baseline = 0 - m_lineOffset;
+ m_textInfo.lineInfo[m_lineNumber].ascender = lineAscender;
+ m_textInfo.lineInfo[m_lineNumber].descender = lineDescender;
+ m_textInfo.lineInfo[m_lineNumber].lineHeight = lineAscender - lineDescender + lineGap * baseScale;
+
+ m_firstCharacterOfLine = m_characterCount; // Store first character of the next line.
+ m_lineVisibleCharacterCount = 0;
+
+ // Store the state of the line before starting on the new line.
+ SaveWordWrappingState(ref m_SavedLineState, i, m_characterCount - 1);
+
+ m_lineNumber += 1;
+ isStartOfNewLine = true;
+ isFirstWord = true;
+
+ // Check to make sure Array is large enough to hold a new line.
+ if (m_lineNumber >= m_textInfo.lineInfo.Length)
+ ResizeLineExtents(m_lineNumber);
+
+ // Apply Line Spacing based on scale of the last character of the line.
+ if (m_lineHeight == TMP_Math.FLOAT_UNSET)
+ {
+ float ascender = m_textInfo.characterInfo[m_characterCount].ascender - m_textInfo.characterInfo[m_characterCount].baseLine;
+ lineOffsetDelta = 0 - m_maxLineDescender + ascender + (lineGap + m_lineSpacing + m_lineSpacingDelta) * baseScale;
+ m_lineOffset += lineOffsetDelta;
+
+ m_startOfLineAscender = ascender;
+ }
+ else
+ m_lineOffset += m_lineHeight + m_lineSpacing * baseScale;
+
+ m_maxLineAscender = k_LargeNegativeFloat;
+ m_maxLineDescender = k_LargePositiveFloat;
+
+ m_xAdvance = 0 + tag_Indent;
+
+ continue;
+ }
+ #endregion End Word Wrapping
+
+
+ // Text Auto-Sizing (text exceeding Width of container.
+ #region Handle Text Auto-Sizing
+ if (m_enableAutoSizing && m_fontSize > m_fontSizeMin)
+ {
+ // Handle Character Width Adjustments
+ #region Character Width Adjustments
+ if (m_charWidthAdjDelta < m_charWidthMaxAdj / 100)
+ {
+ loopCountA = 0;
+ m_charWidthAdjDelta += 0.01f;
+ GenerateTextMesh();
+ return;
+ }
+ #endregion
+
+ // Adjust Point Size
+ m_maxFontSize = m_fontSize;
+
+ m_fontSize -= Mathf.Max((m_fontSize - m_minFontSize) / 2, 0.05f);
+ m_fontSize = (int)(Mathf.Max(m_fontSize, m_fontSizeMin) * 20 + 0.5f) / 20f;
+
+ //m_recursiveCount = 0;
+ if (loopCountA > 20) return; // Added to debug
+ GenerateTextMesh();
+ return;
+ }
+ #endregion End Text Auto-Sizing
+
+
+ // Handle Text Overflow
+ #region Handle Text Overflow
+ switch (m_overflowMode)
+ {
+ case TextOverflowModes.Overflow:
+ if (m_isMaskingEnabled)
+ DisableMasking();
+
+ break;
+ case TextOverflowModes.Ellipsis:
+ if (m_isMaskingEnabled)
+ DisableMasking();
+
+ m_isTextTruncated = true;
+
+ if (m_characterCount < 1)
+ {
+ m_textInfo.characterInfo[m_characterCount].isVisible = false;
+ //m_visibleCharacterCount = 0;
+ break;
+ }
+
+ m_TextParsingBuffer[i - 1].unicode = 8230;
+ m_TextParsingBuffer[i].unicode = (char)0;
+
+ if (m_cached_Ellipsis_Character != null)
+ {
+ m_textInfo.characterInfo[ellipsisIndex].character = (char)8230;
+ m_textInfo.characterInfo[ellipsisIndex].textElement = m_cached_Ellipsis_Character;
+ m_textInfo.characterInfo[ellipsisIndex].fontAsset = m_materialReferences[0].fontAsset;
+ m_textInfo.characterInfo[ellipsisIndex].material = m_materialReferences[0].material;
+ m_textInfo.characterInfo[ellipsisIndex].materialReferenceIndex = 0;
+ }
+ else
+ {
+ Debug.LogWarning("Unable to use Ellipsis character since it wasn't found in the current Font Asset [" + m_fontAsset.name + "]. Consider regenerating this font asset to include the Ellipsis character (u+2026).\nNote: Warnings can be disabled in the TMP Settings file.", this);
+ }
+
+ m_totalCharacterCount = ellipsisIndex + 1;
+
+ GenerateTextMesh();
+ return;
+ //case TextOverflowModes.Masking:
+ // if (!m_isMaskingEnabled)
+ // EnableMasking();
+ // break;
+ //case TextOverflowModes.ScrollRect:
+ // if (!m_isMaskingEnabled)
+ // EnableMasking();
+ // break;
+ case TextOverflowModes.Truncate:
+ if (m_isMaskingEnabled)
+ DisableMasking();
+
+ m_textInfo.characterInfo[m_characterCount].isVisible = false;
+ break;
+ case TextOverflowModes.Linked:
+ //m_textInfo.characterInfo[m_characterCount].isVisible = false;
+
+ //if (m_linkedTextComponent != null)
+ //{
+ // m_linkedTextComponent.text = text;
+ // m_linkedTextComponent.firstVisibleCharacter = m_characterCount;
+ // m_linkedTextComponent.ForceMeshUpdate();
+ //}
+ break;
+ }
+ #endregion End Text Overflow
+
+ }
+ #endregion End Check for Characters Exceeding Width of Text Container
+
+
+ // Special handling of characters that are not ignored at the end of a line.
+ if (charCode == 9 || charCode == 0xA0 || charCode == 0x2007)
+ {
+ m_textInfo.characterInfo[m_characterCount].isVisible = false;
+ m_lastVisibleCharacterOfLine = m_characterCount;
+ m_textInfo.lineInfo[m_lineNumber].spaceCount += 1;
+ m_textInfo.spaceCount += 1;
+
+ if (charCode == 0xA0)
+ m_textInfo.lineInfo[m_lineNumber].controlCharacterCount += 1;
+ }
+ else
+ {
+ // Determine Vertex Color
+ if (m_overrideHtmlColors)
+ vertexColor = m_fontColor32;
+ else
+ vertexColor = m_htmlColor;
+
+ // Store Character & Sprite Vertex Information
+ if (m_textElementType == TMP_TextElementType.Character)
+ {
+ // Save Character Vertex Data
+ SaveGlyphVertexInfo(padding, style_padding, vertexColor);
+ }
+ else if (m_textElementType == TMP_TextElementType.Sprite)
+ {
+ SaveSpriteVertexInfo(vertexColor);
+ }
+ }
+
+
+ // Increase visible count for Characters.
+ if (m_textInfo.characterInfo[m_characterCount].isVisible && charCode != 0xAD)
+ {
+ if (isStartOfNewLine) { isStartOfNewLine = false; m_firstVisibleCharacterOfLine = m_characterCount; }
+
+ m_lineVisibleCharacterCount += 1;
+ m_lastVisibleCharacterOfLine = m_characterCount;
+ }
+ }
+ else
+ { // This is a Space, Tab, LineFeed or Carriage Return
+
+ // Track # of spaces per line which is used for line justification.
+ if ((charCode == 10 || char.IsSeparator((char)charCode)) && charCode != 0xAD && charCode != 0x200B && charCode != 0x2060)
+ {
+ m_textInfo.lineInfo[m_lineNumber].spaceCount += 1;
+ m_textInfo.spaceCount += 1;
+ }
+ }
+ //#if TMP_PROFILE_ON
+ //Profiler.EndSample();
+ //#endif
+ #endregion Handle Visible Characters
+
+
+ // Check if Line Spacing of previous line needs to be adjusted.
+ #region Adjust Line Spacing
+ #if TMP_PROFILE_ON
+ Profiler.BeginSample("Adjust Line Spacing");
+ #endif
+ if (m_lineNumber > 0 && !TMP_Math.Approximately(m_maxLineAscender, m_startOfLineAscender) && m_lineHeight == TMP_Math.FLOAT_UNSET && !m_isNewPage)
+ {
+ //Debug.Log("Inline - Adjusting Line Spacing on line #" + m_lineNumber);
+ //float gap = 0; // Compute gap.
+
+ float offsetDelta = m_maxLineAscender - m_startOfLineAscender;
+ AdjustLineOffset(m_firstCharacterOfLine, m_characterCount, offsetDelta);
+ elementDescenderII -= offsetDelta;
+ m_lineOffset += offsetDelta;
+
+ m_startOfLineAscender += offsetDelta;
+ m_SavedWordWrapState.lineOffset = m_lineOffset;
+ m_SavedWordWrapState.previousLineAscender = m_startOfLineAscender;
+ }
+ #if TMP_PROFILE_ON
+ Profiler.EndSample();
+ #endif
+ #endregion
+
+
+ // Store Rectangle positions for each Character.
+ #region Store Character Data
+ m_textInfo.characterInfo[m_characterCount].lineNumber = m_lineNumber;
+ m_textInfo.characterInfo[m_characterCount].pageNumber = m_pageNumber;
+
+ if (charCode != 10 && charCode != 13 && charCode != 8230 || m_textInfo.lineInfo[m_lineNumber].characterCount == 1)
+ m_textInfo.lineInfo[m_lineNumber].alignment = m_lineJustification;
+ #endregion Store Character Data
+
+
+ // Check if text Exceeds the vertical bounds of the margin area.
+ #region Check Vertical Bounds & Auto-Sizing
+ #if TMP_PROFILE_ON
+ Profiler.BeginSample("Check Vertical Bounds");
+ #endif
+ if (m_maxAscender - elementDescenderII > marginHeight + 0.0001f)
+ {
+ // Handle Line spacing adjustments
+ #region Line Spacing Adjustments
+ if (m_enableAutoSizing && m_lineSpacingDelta > m_lineSpacingMax && m_lineNumber > 0)
+ {
+ loopCountA = 0;
+
+ m_lineSpacingDelta -= 1;
+ GenerateTextMesh();
+ return;
+ }
+ #endregion
+
+
+ // Handle Text Auto-sizing resulting from text exceeding vertical bounds.
+ #region Text Auto-Sizing (Text greater than vertical bounds)
+ if (m_enableAutoSizing && m_fontSize > m_fontSizeMin)
+ {
+ m_maxFontSize = m_fontSize;
+
+ m_fontSize -= Mathf.Max((m_fontSize - m_minFontSize) / 2, 0.05f);
+ m_fontSize = (int)(Mathf.Max(m_fontSize, m_fontSizeMin) * 20 + 0.5f) / 20f;
+
+ //m_recursiveCount = 0;
+ if (loopCountA > 20) return; // Added to debug
+ GenerateTextMesh();
+ return;
+ }
+ #endregion Text Auto-Sizing
+
+ // Set isTextOverflowing and firstOverflowCharacterIndex
+ if (m_firstOverflowCharacterIndex == -1)
+ m_firstOverflowCharacterIndex = m_characterCount;
+
+ // Handle Text Overflow
+ #region Text Overflow
+ switch (m_overflowMode)
+ {
+ case TextOverflowModes.Overflow:
+ if (m_isMaskingEnabled)
+ DisableMasking();
+
+ break;
+ case TextOverflowModes.Ellipsis:
+ if (m_isMaskingEnabled)
+ DisableMasking();
+
+ if (m_lineNumber > 0)
+ {
+ m_TextParsingBuffer[m_textInfo.characterInfo[ellipsisIndex].index].unicode = 8230;
+ m_TextParsingBuffer[m_textInfo.characterInfo[ellipsisIndex].index + 1].unicode = (char)0;
+
+ if (m_cached_Ellipsis_Character != null)
+ {
+ m_textInfo.characterInfo[ellipsisIndex].character = (char)8230;
+ m_textInfo.characterInfo[ellipsisIndex].textElement = m_cached_Ellipsis_Character;
+ m_textInfo.characterInfo[ellipsisIndex].fontAsset = m_materialReferences[0].fontAsset;
+ m_textInfo.characterInfo[ellipsisIndex].material = m_materialReferences[0].material;
+ m_textInfo.characterInfo[ellipsisIndex].materialReferenceIndex = 0;
+ }
+ else
+ {
+ Debug.LogWarning("Unable to use Ellipsis character since it wasn't found in the current Font Asset [" + m_fontAsset.name + "]. Consider regenerating this font asset to include the Ellipsis character (u+2026).\nNote: Warnings can be disabled in the TMP Settings file.", this);
+ }
+
+ m_totalCharacterCount = ellipsisIndex + 1;
+
+ GenerateTextMesh();
+ m_isTextTruncated = true;
+ return;
+ }
+ else
+ {
+ ClearMesh();
+ return;
+ }
+ //case TextOverflowModes.Masking:
+ // if (!m_isMaskingEnabled)
+ // EnableMasking();
+ // break;
+ //case TextOverflowModes.ScrollRect:
+ // if (!m_isMaskingEnabled)
+ // EnableMasking();
+ // break;
+ case TextOverflowModes.Truncate:
+ if (m_isMaskingEnabled)
+ DisableMasking();
+
+ // TODO : Optimize
+ if (m_lineNumber > 0)
+ {
+ m_TextParsingBuffer[m_textInfo.characterInfo[ellipsisIndex].index + 1].unicode = (char)0;
+
+ m_totalCharacterCount = ellipsisIndex + 1;
+
+ GenerateTextMesh();
+ m_isTextTruncated = true;
+ return;
+ }
+ else
+ {
+ ClearMesh();
+ return;
+ }
+ case TextOverflowModes.Page:
+ if (m_isMaskingEnabled)
+ DisableMasking();
+
+ // Ignore Page Break, Linefeed or carriage return
+ if (charCode == 13 || charCode == 10)
+ break;
+
+ // Return if the first character doesn't fit.
+ if (i == 0)
+ {
+ ClearMesh();
+ return;
+ }
+ else if (previousPageOverflowChar == i)
+ {
+ m_TextParsingBuffer[i].unicode = 0;
+ m_isTextTruncated = true;
+ }
+
+ previousPageOverflowChar = i;
+
+ // Go back to previous line and re-layout
+ i = RestoreWordWrappingState(ref m_SavedLineState);
+
+ m_isNewPage = true;
+ m_xAdvance = 0 + tag_Indent;
+ m_lineOffset = 0;
+ m_maxAscender = 0;
+ pageAscender = 0;
+ m_lineNumber += 1;
+ m_pageNumber += 1;
+ continue;
+ case TextOverflowModes.Linked:
+ if (m_linkedTextComponent != null)
+ {
+ m_linkedTextComponent.text = text;
+ m_linkedTextComponent.firstVisibleCharacter = m_characterCount;
+ m_linkedTextComponent.ForceMeshUpdate();
+ }
+
+ // Truncate remaining text
+ if (m_lineNumber > 0)
+ {
+ m_TextParsingBuffer[i].unicode = (char)0;
+
+ m_totalCharacterCount = m_characterCount;
+
+ // TODO : Optimize as we should be able to end the layout phase here without having to do another pass.
+ GenerateTextMesh();
+ m_isTextTruncated = true;
+ return;
+ }
+ else
+ {
+ ClearMesh();
+ return;
+ }
+ }
+ #endregion End Text Overflow
+
+ }
+ #if TMP_PROFILE_ON
+ Profiler.EndSample();
+ #endif
+ #endregion Check Vertical Bounds
+
+
+ // Handle xAdvance & Tabulation Stops. Tab stops at every 25% of Font Size.
+ #region XAdvance, Tabulation & Stops
+ if (charCode == 9)
+ {
+ float tabSize = m_currentFontAsset.faceInfo.tabWidth * m_currentFontAsset.tabSize * currentElementScale;
+ float tabs = Mathf.Ceil(m_xAdvance / tabSize) * tabSize;
+ m_xAdvance = tabs > m_xAdvance ? tabs : m_xAdvance + tabSize;
+ }
+ else if (m_monoSpacing != 0)
+ {
+ m_xAdvance += (m_monoSpacing - monoAdvance + ((characterSpacingAdjustment + m_currentFontAsset.normalSpacingOffset) * currentElementScale) + m_cSpacing) * (1 - m_charWidthAdjDelta);
+
+ if (char.IsWhiteSpace((char)charCode) || charCode == 0x200B)
+ m_xAdvance += m_wordSpacing * currentElementScale;
+ }
+ else if (!m_isRightToLeft)
+ {
+ float scaleFXMultiplier = 1;
+ if (m_isFXMatrixSet) scaleFXMultiplier = m_FXMatrix.lossyScale.x;
+
+ m_xAdvance += ((m_cached_TextElement.glyph.metrics.horizontalAdvance * scaleFXMultiplier * bold_xAdvance_multiplier + characterSpacingAdjustment + m_currentFontAsset.normalSpacingOffset + glyphAdjustments.xAdvance) * currentElementScale + m_cSpacing) * (1 - m_charWidthAdjDelta);
+
+ if (char.IsWhiteSpace((char)charCode) || charCode == 0x200B)
+ m_xAdvance += m_wordSpacing * currentElementScale;
+ }
+ else
+ {
+ m_xAdvance -= glyphAdjustments.xAdvance * currentElementScale;
+ }
+
+
+ // Store xAdvance information
+ m_textInfo.characterInfo[m_characterCount].xAdvance = m_xAdvance;
+
+ #endregion Tabulation & Stops
+
+
+ // Handle Carriage Return
+ #region Carriage Return
+ if (charCode == 13)
+ {
+ m_xAdvance = 0 + tag_Indent;
+ }
+ #endregion Carriage Return
+
+
+ // Handle Line Spacing Adjustments + Word Wrapping & special case for last line.
+ #region Check for Line Feed and Last Character
+ #if TMP_PROFILE_ON
+ Profiler.BeginSample("Process Linefeed");
+ #endif
+ if (charCode == 10 || m_characterCount == totalCharacterCount - 1)
+ {
+ // Check if Line Spacing of previous line needs to be adjusted.
+ if (m_lineNumber > 0 && !TMP_Math.Approximately(m_maxLineAscender, m_startOfLineAscender) && m_lineHeight == TMP_Math.FLOAT_UNSET && !m_isNewPage)
+ {
+ //Debug.Log("Line Feed - Adjusting Line Spacing on line #" + m_lineNumber);
+ float offsetDelta = m_maxLineAscender - m_startOfLineAscender;
+ AdjustLineOffset(m_firstCharacterOfLine, m_characterCount, offsetDelta);
+ elementDescenderII -= offsetDelta;
+ m_lineOffset += offsetDelta;
+ }
+ m_isNewPage = false;
+
+ // Calculate lineAscender & make sure if last character is superscript or subscript that we check that as well.
+ float lineAscender = m_maxLineAscender - m_lineOffset;
+ float lineDescender = m_maxLineDescender - m_lineOffset;
+
+ // Update maxDescender and maxVisibleDescender
+ m_maxDescender = m_maxDescender < lineDescender ? m_maxDescender : lineDescender;
+ if (!isMaxVisibleDescenderSet)
+ maxVisibleDescender = m_maxDescender;
+
+ if (m_useMaxVisibleDescender && (m_characterCount >= m_maxVisibleCharacters || m_lineNumber >= m_maxVisibleLines))
+ isMaxVisibleDescenderSet = true;
+
+ // Save Line Information
+ m_textInfo.lineInfo[m_lineNumber].firstCharacterIndex = m_firstCharacterOfLine;
+ m_textInfo.lineInfo[m_lineNumber].firstVisibleCharacterIndex = m_firstVisibleCharacterOfLine = m_firstCharacterOfLine > m_firstVisibleCharacterOfLine ? m_firstCharacterOfLine : m_firstVisibleCharacterOfLine;
+ m_textInfo.lineInfo[m_lineNumber].lastCharacterIndex = m_lastCharacterOfLine = m_characterCount;
+ m_textInfo.lineInfo[m_lineNumber].lastVisibleCharacterIndex = m_lastVisibleCharacterOfLine = m_lastVisibleCharacterOfLine < m_firstVisibleCharacterOfLine ? m_firstVisibleCharacterOfLine : m_lastVisibleCharacterOfLine;
+
+ m_textInfo.lineInfo[m_lineNumber].characterCount = m_textInfo.lineInfo[m_lineNumber].lastCharacterIndex - m_textInfo.lineInfo[m_lineNumber].firstCharacterIndex + 1;
+ m_textInfo.lineInfo[m_lineNumber].visibleCharacterCount = m_lineVisibleCharacterCount;
+ m_textInfo.lineInfo[m_lineNumber].lineExtents.min = new Vector2(m_textInfo.characterInfo[m_firstVisibleCharacterOfLine].bottomLeft.x, lineDescender);
+ m_textInfo.lineInfo[m_lineNumber].lineExtents.max = new Vector2(m_textInfo.characterInfo[m_lastVisibleCharacterOfLine].topRight.x, lineAscender);
+ m_textInfo.lineInfo[m_lineNumber].length = m_textInfo.lineInfo[m_lineNumber].lineExtents.max.x - (padding * currentElementScale);
+ m_textInfo.lineInfo[m_lineNumber].width = width;
+
+ if (m_textInfo.lineInfo[m_lineNumber].characterCount == 1)
+ m_textInfo.lineInfo[m_lineNumber].alignment = m_lineJustification;
+
+ if (m_textInfo.characterInfo[m_lastVisibleCharacterOfLine].isVisible)
+ m_textInfo.lineInfo[m_lineNumber].maxAdvance = m_textInfo.characterInfo[m_lastVisibleCharacterOfLine].xAdvance - (characterSpacingAdjustment + m_currentFontAsset.normalSpacingOffset) * currentElementScale - m_cSpacing;
+ else
+ m_textInfo.lineInfo[m_lineNumber].maxAdvance = m_textInfo.characterInfo[m_lastCharacterOfLine].xAdvance - (characterSpacingAdjustment + m_currentFontAsset.normalSpacingOffset) * currentElementScale - m_cSpacing;
+
+ m_textInfo.lineInfo[m_lineNumber].baseline = 0 - m_lineOffset;
+ m_textInfo.lineInfo[m_lineNumber].ascender = lineAscender;
+ m_textInfo.lineInfo[m_lineNumber].descender = lineDescender;
+ m_textInfo.lineInfo[m_lineNumber].lineHeight = lineAscender - lineDescender + lineGap * baseScale;
+
+ m_firstCharacterOfLine = m_characterCount + 1;
+ m_lineVisibleCharacterCount = 0;
+
+ // Add new line if not last line or character.
+ if (charCode == 10)
+ {
+ // Store the state of the line before starting on the new line.
+ SaveWordWrappingState(ref m_SavedLineState, i, m_characterCount);
+ // Store the state of the last Character before the new line.
+ SaveWordWrappingState(ref m_SavedWordWrapState, i, m_characterCount);
+
+ m_lineNumber += 1;
+ isStartOfNewLine = true;
+ ignoreNonBreakingSpace = false;
+ isFirstWord = true;
+
+ // Check to make sure Array is large enough to hold a new line.
+ if (m_lineNumber >= m_textInfo.lineInfo.Length)
+ ResizeLineExtents(m_lineNumber);
+
+ // Apply Line Spacing
+ if (m_lineHeight == TMP_Math.FLOAT_UNSET)
+ {
+ lineOffsetDelta = 0 - m_maxLineDescender + elementAscender + (lineGap + m_lineSpacing + m_paragraphSpacing + m_lineSpacingDelta) * baseScale;
+ m_lineOffset += lineOffsetDelta;
+ }
+ else
+ m_lineOffset += m_lineHeight + (m_lineSpacing + m_paragraphSpacing) * baseScale;
+
+ m_maxLineAscender = k_LargeNegativeFloat;
+ m_maxLineDescender = k_LargePositiveFloat;
+ m_startOfLineAscender = elementAscender;
+
+ m_xAdvance = 0 + tag_LineIndent + tag_Indent;
+
+ ellipsisIndex = m_characterCount - 1;
+
+ m_characterCount += 1;
+ continue;
+ }
+ }
+ #if TMP_PROFILE_ON
+ Profiler.EndSample();
+ #endif
+ #endregion Check for Linefeed or Last Character
+
+
+ // Store Rectangle positions for each Character.
+ #region Save CharacterInfo for the current character.
+ #if TMP_PROFILE_ON
+ Profiler.BeginSample("Save CharacterInfo & Extents");
+ #endif
+ // Determine the bounds of the Mesh.
+ if (m_textInfo.characterInfo[m_characterCount].isVisible)
+ {
+ m_meshExtents.min.x = Mathf.Min(m_meshExtents.min.x, m_textInfo.characterInfo[m_characterCount].bottomLeft.x);
+ m_meshExtents.min.y = Mathf.Min(m_meshExtents.min.y, m_textInfo.characterInfo[m_characterCount].bottomLeft.y);
+
+ m_meshExtents.max.x = Mathf.Max(m_meshExtents.max.x, m_textInfo.characterInfo[m_characterCount].topRight.x);
+ m_meshExtents.max.y = Mathf.Max(m_meshExtents.max.y, m_textInfo.characterInfo[m_characterCount].topRight.y);
+
+ //m_meshExtents.min = new Vector2(Mathf.Min(m_meshExtents.min.x, m_textInfo.characterInfo[m_characterCount].bottomLeft.x), Mathf.Min(m_meshExtents.min.y, m_textInfo.characterInfo[m_characterCount].bottomLeft.y));
+ //m_meshExtents.max = new Vector2(Mathf.Max(m_meshExtents.max.x, m_textInfo.characterInfo[m_characterCount].topRight.x), Mathf.Max(m_meshExtents.max.y, m_textInfo.characterInfo[m_characterCount].topRight.y));
+ }
+
+
+ // Save pageInfo Data
+ if (m_overflowMode == TextOverflowModes.Page && charCode != 13 && charCode != 10) // && m_pageNumber < 16)
+ {
+ // Check if we need to increase allocations for the pageInfo array.
+ if (m_pageNumber + 1 > m_textInfo.pageInfo.Length)
+ TMP_TextInfo.Resize(ref m_textInfo.pageInfo, m_pageNumber + 1, true);
+
+ m_textInfo.pageInfo[m_pageNumber].ascender = pageAscender;
+ m_textInfo.pageInfo[m_pageNumber].descender = elementDescender < m_textInfo.pageInfo[m_pageNumber].descender ? elementDescender : m_textInfo.pageInfo[m_pageNumber].descender;
+
+ if (m_pageNumber == 0 && m_characterCount == 0)
+ m_textInfo.pageInfo[m_pageNumber].firstCharacterIndex = m_characterCount;
+ else if (m_characterCount > 0 && m_pageNumber != m_textInfo.characterInfo[m_characterCount - 1].pageNumber)
+ {
+ m_textInfo.pageInfo[m_pageNumber - 1].lastCharacterIndex = m_characterCount - 1;
+ m_textInfo.pageInfo[m_pageNumber].firstCharacterIndex = m_characterCount;
+ }
+ else if (m_characterCount == totalCharacterCount - 1)
+ m_textInfo.pageInfo[m_pageNumber].lastCharacterIndex = m_characterCount;
+ }
+ #if TMP_PROFILE_ON
+ Profiler.EndSample();
+ #endif
+ #endregion Saving CharacterInfo
+
+
+ // Save State of Mesh Creation for handling of Word Wrapping
+ #region Save Word Wrapping State
+ #if TMP_PROFILE_ON
+ Profiler.BeginSample("Save Word Wrapping State");
+ #endif
+ if (m_enableWordWrapping || m_overflowMode == TextOverflowModes.Truncate || m_overflowMode == TextOverflowModes.Ellipsis)
+ {
+ if ((char.IsWhiteSpace((char)charCode) || charCode == 0x200B || charCode == 0x2D || charCode == 0xAD) && (!m_isNonBreakingSpace || ignoreNonBreakingSpace) && charCode != 0xA0 && charCode != 0x2007 && charCode != 0x2011 && charCode != 0x202F && charCode != 0x2060)
+ {
+ // We store the state of numerous variables for the most recent Space, LineFeed or Carriage Return to enable them to be restored
+ // for Word Wrapping.
+ SaveWordWrappingState(ref m_SavedWordWrapState, i, m_characterCount);
+ m_isCharacterWrappingEnabled = false;
+ isFirstWord = false;
+ }
+ // Handling for East Asian languages
+ else if (( charCode > 0x1100 && charCode < 0x11ff || /* Hangul Jamo */
+ charCode > 0x2E80 && charCode < 0x9FFF || /* CJK */
+ charCode > 0xA960 && charCode < 0xA97F || /* Hangul Jame Extended-A */
+ charCode > 0xAC00 && charCode < 0xD7FF || /* Hangul Syllables */
+ charCode > 0xF900 && charCode < 0xFAFF || /* CJK Compatibility Ideographs */
+ charCode > 0xFE30 && charCode < 0xFE4F || /* CJK Compatibility Forms */
+ charCode > 0xFF00 && charCode < 0xFFEF) /* CJK Halfwidth */
+ && !m_isNonBreakingSpace)
+ {
+ if (isFirstWord || isLastBreakingChar || TMP_Settings.linebreakingRules.leadingCharacters.ContainsKey(charCode) == false &&
+ (m_characterCount < totalCharacterCount - 1 &&
+ TMP_Settings.linebreakingRules.followingCharacters.ContainsKey(m_textInfo.characterInfo[m_characterCount + 1].character) == false))
+ {
+ SaveWordWrappingState(ref m_SavedWordWrapState, i, m_characterCount);
+ m_isCharacterWrappingEnabled = false;
+ isFirstWord = false;
+ }
+ }
+ else if ((isFirstWord || m_isCharacterWrappingEnabled == true || isLastBreakingChar))
+ SaveWordWrappingState(ref m_SavedWordWrapState, i, m_characterCount);
+
+ }
+ #if TMP_PROFILE_ON
+ Profiler.EndSample();
+ #endif
+ #endregion Save Word Wrapping State
+
+ m_characterCount += 1;
+ }
+
+ // Check Auto Sizing and increase font size to fill text container.
+ #region Check Auto-Sizing (Upper Font Size Bounds)
+ fontSizeDelta = m_maxFontSize - m_minFontSize;
+ if (!m_isCharacterWrappingEnabled && m_enableAutoSizing && fontSizeDelta > 0.051f && m_fontSize < m_fontSizeMax)
+ {
+ m_minFontSize = m_fontSize;
+ m_fontSize += Mathf.Max((m_maxFontSize - m_fontSize) / 2, 0.05f);
+ m_fontSize = (int)(Mathf.Min(m_fontSize, m_fontSizeMax) * 20 + 0.5f) / 20f;
+
+ //Debug.Log(m_fontSize);
+
+ if (loopCountA > 20) return; // Added to debug
+ GenerateTextMesh();
+ return;
+ }
+ #endregion End Auto-sizing Check
+
+
+ m_isCharacterWrappingEnabled = false;
+
+ #if TMP_PROFILE_PHASES_ON
+ Profiler.EndSample();
+ #endif
+
+ //Debug.Log("Iteration Count: " + loopCountA + ". Final Point Size: " + m_fontSize); // + " B: " + loopCountB + " C: " + loopCountC + " D: " + loopCountD);
+
+ // *** PHASE II of Text Generation ***
+ #if TMP_PROFILE_PHASES_ON
+ Profiler.BeginSample("TMP Generate Text - Phase II");
+ #endif
+
+ // If there are no visible characters... no need to continue
+ if (m_characterCount == 0) // && m_visibleSpriteCount == 0)
+ {
+ ClearMesh();
+
+ // Event indicating the text has been regenerated.
+ TMPro_EventManager.ON_TEXT_CHANGED(this);
+ return;
+ }
+
+
+ // *** PHASE II of Text Generation ***
+ int last_vert_index = m_materialReferences[0].referenceCount * 4;
+
+ // Partial clear of the vertices array to mark unused vertices as degenerate.
+ m_textInfo.meshInfo[0].Clear(false);
+
+ // Handle Text Alignment
+ #region Text Vertical Alignment
+ #if TMP_PROFILE_ON
+ Profiler.BeginSample("Vertical Text Alignment");
+ #endif
+ Vector3 anchorOffset = Vector3.zero;
+ Vector3[] corners = m_RectTransformCorners; // GetTextContainerLocalCorners();
+
+ switch (m_textAlignment)
+ {
+ // Top Vertically
+ case TextAlignmentOptions.Top:
+ case TextAlignmentOptions.TopLeft:
+ case TextAlignmentOptions.TopRight:
+ case TextAlignmentOptions.TopJustified:
+ case TextAlignmentOptions.TopFlush:
+ case TextAlignmentOptions.TopGeoAligned:
+ if (m_overflowMode != TextOverflowModes.Page)
+ anchorOffset = corners[1] + new Vector3(0 + margins.x, 0 - m_maxAscender - margins.y, 0);
+ else
+ anchorOffset = corners[1] + new Vector3(0 + margins.x, 0 - m_textInfo.pageInfo[pageToDisplay].ascender - margins.y, 0);
+ break;
+
+ // Middle Vertically
+ case TextAlignmentOptions.Left:
+ case TextAlignmentOptions.Right:
+ case TextAlignmentOptions.Center:
+ case TextAlignmentOptions.Justified:
+ case TextAlignmentOptions.Flush:
+ case TextAlignmentOptions.CenterGeoAligned:
+ if (m_overflowMode != TextOverflowModes.Page)
+ anchorOffset = (corners[0] + corners[1]) / 2 + new Vector3(0 + margins.x, 0 - (m_maxAscender + margins.y + maxVisibleDescender - margins.w) / 2, 0);
+ else
+ anchorOffset = (corners[0] + corners[1]) / 2 + new Vector3(0 + margins.x, 0 - (m_textInfo.pageInfo[pageToDisplay].ascender + margins.y + m_textInfo.pageInfo[pageToDisplay].descender - margins.w) / 2, 0);
+ break;
+
+ // Bottom Vertically
+ case TextAlignmentOptions.Bottom:
+ case TextAlignmentOptions.BottomLeft:
+ case TextAlignmentOptions.BottomRight:
+ case TextAlignmentOptions.BottomJustified:
+ case TextAlignmentOptions.BottomFlush:
+ case TextAlignmentOptions.BottomGeoAligned:
+ if (m_overflowMode != TextOverflowModes.Page)
+ anchorOffset = corners[0] + new Vector3(0 + margins.x, 0 - maxVisibleDescender + margins.w, 0);
+ else
+ anchorOffset = corners[0] + new Vector3(0 + margins.x, 0 - m_textInfo.pageInfo[pageToDisplay].descender + margins.w, 0);
+ break;
+
+ // Baseline Vertically
+ case TextAlignmentOptions.Baseline:
+ case TextAlignmentOptions.BaselineLeft:
+ case TextAlignmentOptions.BaselineRight:
+ case TextAlignmentOptions.BaselineJustified:
+ case TextAlignmentOptions.BaselineFlush:
+ case TextAlignmentOptions.BaselineGeoAligned:
+ anchorOffset = (corners[0] + corners[1]) / 2 + new Vector3(0 + margins.x, 0, 0);
+ break;
+
+ // Midline Vertically
+ case TextAlignmentOptions.MidlineLeft:
+ case TextAlignmentOptions.Midline:
+ case TextAlignmentOptions.MidlineRight:
+ case TextAlignmentOptions.MidlineJustified:
+ case TextAlignmentOptions.MidlineFlush:
+ case TextAlignmentOptions.MidlineGeoAligned:
+ anchorOffset = (corners[0] + corners[1]) / 2 + new Vector3(0 + margins.x, 0 - (m_meshExtents.max.y + margins.y + m_meshExtents.min.y - margins.w) / 2, 0);
+ break;
+
+ // Capline Vertically
+ case TextAlignmentOptions.CaplineLeft:
+ case TextAlignmentOptions.Capline:
+ case TextAlignmentOptions.CaplineRight:
+ case TextAlignmentOptions.CaplineJustified:
+ case TextAlignmentOptions.CaplineFlush:
+ case TextAlignmentOptions.CaplineGeoAligned:
+ anchorOffset = (corners[0] + corners[1]) / 2 + new Vector3(0 + margins.x, 0 - (m_maxCapHeight - margins.y - margins.w) / 2, 0);
+ break;
+ }
+ #if TMP_PROFILE_ON
+ Profiler.EndSample();
+ #endif
+ #endregion
+
+
+ // Initialization for Second Pass
+ Vector3 justificationOffset = Vector3.zero;
+ Vector3 offset = Vector3.zero;
+ int vert_index_X4 = 0;
+ int sprite_index_X4 = 0;
+
+ int wordCount = 0;
+ int lineCount = 0;
+ int lastLine = 0;
+ bool isFirstSeperator = false;
+
+ bool isStartOfWord = false;
+ int wordFirstChar = 0;
+ int wordLastChar = 0;
+
+ // Second Pass : Line Justification, UV Mapping, Character & Line Visibility & more.
+ #region Handle Line Justification & UV Mapping & Character Visibility & More
+
+ // Variables used to handle Canvas Render Modes and SDF Scaling
+ bool isCameraAssigned = m_canvas.worldCamera == null ? false : true;
+ float lossyScale = m_previousLossyScaleY = this.transform.lossyScale.y;
+ RenderMode canvasRenderMode = m_canvas.renderMode;
+ float canvasScaleFactor = m_canvas.scaleFactor;
+
+ Color32 underlineColor = Color.white;
+ Color32 strikethroughColor = Color.white;
+ Color32 highlightColor = new Color32(255, 255, 0, 64);
+ float xScale = 0;
+ float xScaleMax = 0;
+ float underlineStartScale = 0;
+ float underlineEndScale = 0;
+ float underlineMaxScale = 0;
+ float underlineBaseLine = k_LargePositiveFloat;
+ int lastPage = 0;
+
+ float strikethroughPointSize = 0;
+ float strikethroughScale = 0;
+ float strikethroughBaseline = 0;
+
+ TMP_CharacterInfo[] characterInfos = m_textInfo.characterInfo;
+ #region Handle Line Justification & UV Mapping & Character Visibility & More
+ for (int i = 0; i < m_characterCount; i++)
+ {
+ TMP_FontAsset currentFontAsset = characterInfos[i].fontAsset;
+
+ char currentCharacter = characterInfos[i].character;
+
+ int currentLine = characterInfos[i].lineNumber;
+ TMP_LineInfo lineInfo = m_textInfo.lineInfo[currentLine];
+ lineCount = currentLine + 1;
+
+ TextAlignmentOptions lineAlignment = lineInfo.alignment;
+
+ // Process Line Justification
+ #region Handle Line Justification
+ #if TMP_PROFILE_ON
+ Profiler.BeginSample("Horizontal Text Alignment");
+ #endif
+ //if (!characterInfos[i].isIgnoringAlignment)
+ //{
+ switch (lineAlignment)
+ {
+ case TextAlignmentOptions.TopLeft:
+ case TextAlignmentOptions.Left:
+ case TextAlignmentOptions.BottomLeft:
+ case TextAlignmentOptions.BaselineLeft:
+ case TextAlignmentOptions.MidlineLeft:
+ case TextAlignmentOptions.CaplineLeft:
+ if (!m_isRightToLeft)
+ justificationOffset = new Vector3(0 + lineInfo.marginLeft, 0, 0);
+ else
+ justificationOffset = new Vector3(0 - lineInfo.maxAdvance, 0, 0);
+ break;
+
+ case TextAlignmentOptions.Top:
+ case TextAlignmentOptions.Center:
+ case TextAlignmentOptions.Bottom:
+ case TextAlignmentOptions.Baseline:
+ case TextAlignmentOptions.Midline:
+ case TextAlignmentOptions.Capline:
+ justificationOffset = new Vector3(lineInfo.marginLeft + lineInfo.width / 2 - lineInfo.maxAdvance / 2, 0, 0);
+ break;
+
+ case TextAlignmentOptions.TopGeoAligned:
+ case TextAlignmentOptions.CenterGeoAligned:
+ case TextAlignmentOptions.BottomGeoAligned:
+ case TextAlignmentOptions.BaselineGeoAligned:
+ case TextAlignmentOptions.MidlineGeoAligned:
+ case TextAlignmentOptions.CaplineGeoAligned:
+ justificationOffset = new Vector3(lineInfo.marginLeft + lineInfo.width / 2 - (lineInfo.lineExtents.min.x + lineInfo.lineExtents.max.x) / 2, 0, 0);
+ break;
+
+ case TextAlignmentOptions.TopRight:
+ case TextAlignmentOptions.Right:
+ case TextAlignmentOptions.BottomRight:
+ case TextAlignmentOptions.BaselineRight:
+ case TextAlignmentOptions.MidlineRight:
+ case TextAlignmentOptions.CaplineRight:
+ if (!m_isRightToLeft)
+ justificationOffset = new Vector3(lineInfo.marginLeft + lineInfo.width - lineInfo.maxAdvance, 0, 0);
+ else
+ justificationOffset = new Vector3(lineInfo.marginLeft + lineInfo.width, 0, 0);
+ break;
+
+ case TextAlignmentOptions.TopJustified:
+ case TextAlignmentOptions.Justified:
+ case TextAlignmentOptions.BottomJustified:
+ case TextAlignmentOptions.BaselineJustified:
+ case TextAlignmentOptions.MidlineJustified:
+ case TextAlignmentOptions.CaplineJustified:
+ case TextAlignmentOptions.TopFlush:
+ case TextAlignmentOptions.Flush:
+ case TextAlignmentOptions.BottomFlush:
+ case TextAlignmentOptions.BaselineFlush:
+ case TextAlignmentOptions.MidlineFlush:
+ case TextAlignmentOptions.CaplineFlush:
+ // Skip Zero Width Characters
+ if (currentCharacter == 0xAD || currentCharacter == 0x200B || currentCharacter == 0x2060) break;
+
+ char lastCharOfCurrentLine = characterInfos[lineInfo.lastCharacterIndex].character;
+ bool isFlush = ((_HorizontalAlignmentOptions)lineAlignment & _HorizontalAlignmentOptions.Flush) == _HorizontalAlignmentOptions.Flush;
+
+ // In Justified mode, all lines are justified except the last one.
+ // In Flush mode, all lines are justified.
+ if (char.IsControl(lastCharOfCurrentLine) == false && currentLine < m_lineNumber || isFlush || lineInfo.maxAdvance > lineInfo.width)
+ {
+ // First character of each line.
+ if (currentLine != lastLine || i == 0 || i == m_firstVisibleCharacter)
+ {
+ if (!m_isRightToLeft)
+ justificationOffset = new Vector3(lineInfo.marginLeft, 0, 0);
+ else
+ justificationOffset = new Vector3(lineInfo.marginLeft + lineInfo.width, 0, 0);
+
+ if (char.IsSeparator(currentCharacter))
+ isFirstSeperator = true;
+ else
+ isFirstSeperator = false;
+ }
+ else
+ {
+ float gap = !m_isRightToLeft ? lineInfo.width - lineInfo.maxAdvance : lineInfo.width + lineInfo.maxAdvance;
+
+ int visibleCount = lineInfo.visibleCharacterCount - 1 + lineInfo.controlCharacterCount;
+
+ // Get the number of spaces for each line ignoring the last character if it is not visible (ie. a space or linefeed).
+ int spaces = (characterInfos[lineInfo.lastCharacterIndex].isVisible ? lineInfo.spaceCount : lineInfo.spaceCount - 1) - lineInfo.controlCharacterCount;
+
+ if (isFirstSeperator) { spaces -= 1; visibleCount += 1; }
+
+ float ratio = spaces > 0 ? m_wordWrappingRatios : 1;
+
+ if (spaces < 1) spaces = 1;
+
+ if (currentCharacter != 0xA0 && (currentCharacter == 9 || char.IsSeparator((char)currentCharacter)))
+ {
+ if (!m_isRightToLeft)
+ justificationOffset += new Vector3(gap * (1 - ratio) / spaces, 0, 0);
+ else
+ justificationOffset -= new Vector3(gap * (1 - ratio) / spaces, 0, 0);
+ }
+ else
+ {
+ if (!m_isRightToLeft)
+ justificationOffset += new Vector3(gap * ratio / visibleCount, 0, 0);
+ else
+ justificationOffset -= new Vector3(gap * ratio / visibleCount, 0, 0);
+ }
+ }
+ }
+ else
+ {
+ if (!m_isRightToLeft)
+ justificationOffset = new Vector3(lineInfo.marginLeft, 0, 0); // Keep last line left justified.
+ else
+ justificationOffset = new Vector3(lineInfo.marginLeft + lineInfo.width, 0, 0); // Keep last line right justified.
+ }
+ //Debug.Log("Char [" + (char)charCode + "] Code:" + charCode + " Line # " + currentLine + " Offset:" + justificationOffset + " # Spaces:" + lineInfo.spaceCount + " # Characters:" + lineInfo.characterCount);
+ break;
+ }
+ //}
+ #if TMP_PROFILE_ON
+ Profiler.EndSample();
+ #endif
+ #endregion End Text Justification
+
+ offset = anchorOffset + justificationOffset;
+
+ // Handle UV2 mapping options and packing of scale information into UV2.
+ #region Handling of UV2 mapping & Scale packing
+ bool isCharacterVisible = characterInfos[i].isVisible;
+ if (isCharacterVisible)
+ {
+ TMP_TextElementType elementType = characterInfos[i].elementType;
+ switch (elementType)
+ {
+ // CHARACTERS
+ case TMP_TextElementType.Character:
+ Extents lineExtents = lineInfo.lineExtents;
+ float uvOffset = (m_uvLineOffset * currentLine) % 1; // + m_uvOffset.x;
+
+ // Setup UV2 based on Character Mapping Options Selected
+ #region Handle UV Mapping Options
+ #if TMP_PROFILE_ON
+ Profiler.BeginSample("UV MAPPING");
+ #endif
+ switch (m_horizontalMapping)
+ {
+ case TextureMappingOptions.Character:
+ characterInfos[i].vertex_BL.uv2.x = 0; //+ m_uvOffset.x;
+ characterInfos[i].vertex_TL.uv2.x = 0; //+ m_uvOffset.x;
+ characterInfos[i].vertex_TR.uv2.x = 1; //+ m_uvOffset.x;
+ characterInfos[i].vertex_BR.uv2.x = 1; //+ m_uvOffset.x;
+ break;
+
+ case TextureMappingOptions.Line:
+ if (m_textAlignment != TextAlignmentOptions.Justified)
+ {
+ characterInfos[i].vertex_BL.uv2.x = (characterInfos[i].vertex_BL.position.x - lineExtents.min.x) / (lineExtents.max.x - lineExtents.min.x) + uvOffset;
+ characterInfos[i].vertex_TL.uv2.x = (characterInfos[i].vertex_TL.position.x - lineExtents.min.x) / (lineExtents.max.x - lineExtents.min.x) + uvOffset;
+ characterInfos[i].vertex_TR.uv2.x = (characterInfos[i].vertex_TR.position.x - lineExtents.min.x) / (lineExtents.max.x - lineExtents.min.x) + uvOffset;
+ characterInfos[i].vertex_BR.uv2.x = (characterInfos[i].vertex_BR.position.x - lineExtents.min.x) / (lineExtents.max.x - lineExtents.min.x) + uvOffset;
+ break;
+ }
+ else // Special Case if Justified is used in Line Mode.
+ {
+ characterInfos[i].vertex_BL.uv2.x = (characterInfos[i].vertex_BL.position.x + justificationOffset.x - m_meshExtents.min.x) / (m_meshExtents.max.x - m_meshExtents.min.x) + uvOffset;
+ characterInfos[i].vertex_TL.uv2.x = (characterInfos[i].vertex_TL.position.x + justificationOffset.x - m_meshExtents.min.x) / (m_meshExtents.max.x - m_meshExtents.min.x) + uvOffset;
+ characterInfos[i].vertex_TR.uv2.x = (characterInfos[i].vertex_TR.position.x + justificationOffset.x - m_meshExtents.min.x) / (m_meshExtents.max.x - m_meshExtents.min.x) + uvOffset;
+ characterInfos[i].vertex_BR.uv2.x = (characterInfos[i].vertex_BR.position.x + justificationOffset.x - m_meshExtents.min.x) / (m_meshExtents.max.x - m_meshExtents.min.x) + uvOffset;
+ break;
+ }
+
+ case TextureMappingOptions.Paragraph:
+ characterInfos[i].vertex_BL.uv2.x = (characterInfos[i].vertex_BL.position.x + justificationOffset.x - m_meshExtents.min.x) / (m_meshExtents.max.x - m_meshExtents.min.x) + uvOffset;
+ characterInfos[i].vertex_TL.uv2.x = (characterInfos[i].vertex_TL.position.x + justificationOffset.x - m_meshExtents.min.x) / (m_meshExtents.max.x - m_meshExtents.min.x) + uvOffset;
+ characterInfos[i].vertex_TR.uv2.x = (characterInfos[i].vertex_TR.position.x + justificationOffset.x - m_meshExtents.min.x) / (m_meshExtents.max.x - m_meshExtents.min.x) + uvOffset;
+ characterInfos[i].vertex_BR.uv2.x = (characterInfos[i].vertex_BR.position.x + justificationOffset.x - m_meshExtents.min.x) / (m_meshExtents.max.x - m_meshExtents.min.x) + uvOffset;
+ break;
+
+ case TextureMappingOptions.MatchAspect:
+
+ switch (m_verticalMapping)
+ {
+ case TextureMappingOptions.Character:
+ characterInfos[i].vertex_BL.uv2.y = 0; // + m_uvOffset.y;
+ characterInfos[i].vertex_TL.uv2.y = 1; // + m_uvOffset.y;
+ characterInfos[i].vertex_TR.uv2.y = 0; // + m_uvOffset.y;
+ characterInfos[i].vertex_BR.uv2.y = 1; // + m_uvOffset.y;
+ break;
+
+ case TextureMappingOptions.Line:
+ characterInfos[i].vertex_BL.uv2.y = (characterInfos[i].vertex_BL.position.y - lineExtents.min.y) / (lineExtents.max.y - lineExtents.min.y) + uvOffset;
+ characterInfos[i].vertex_TL.uv2.y = (characterInfos[i].vertex_TL.position.y - lineExtents.min.y) / (lineExtents.max.y - lineExtents.min.y) + uvOffset;
+ characterInfos[i].vertex_TR.uv2.y = characterInfos[i].vertex_BL.uv2.y;
+ characterInfos[i].vertex_BR.uv2.y = characterInfos[i].vertex_TL.uv2.y;
+ break;
+
+ case TextureMappingOptions.Paragraph:
+ characterInfos[i].vertex_BL.uv2.y = (characterInfos[i].vertex_BL.position.y - m_meshExtents.min.y) / (m_meshExtents.max.y - m_meshExtents.min.y) + uvOffset;
+ characterInfos[i].vertex_TL.uv2.y = (characterInfos[i].vertex_TL.position.y - m_meshExtents.min.y) / (m_meshExtents.max.y - m_meshExtents.min.y) + uvOffset;
+ characterInfos[i].vertex_TR.uv2.y = characterInfos[i].vertex_BL.uv2.y;
+ characterInfos[i].vertex_BR.uv2.y = characterInfos[i].vertex_TL.uv2.y;
+ break;
+
+ case TextureMappingOptions.MatchAspect:
+ Debug.Log("ERROR: Cannot Match both Vertical & Horizontal.");
+ break;
+ }
+
+ //float xDelta = 1 - (_uv2s[vert_index + 0].y * textMeshCharacterInfo[i].AspectRatio); // Left aligned
+ float xDelta = (1 - ((characterInfos[i].vertex_BL.uv2.y + characterInfos[i].vertex_TL.uv2.y) * characterInfos[i].aspectRatio)) / 2; // Center of Rectangle
+
+ characterInfos[i].vertex_BL.uv2.x = (characterInfos[i].vertex_BL.uv2.y * characterInfos[i].aspectRatio) + xDelta + uvOffset;
+ characterInfos[i].vertex_TL.uv2.x = characterInfos[i].vertex_BL.uv2.x;
+ characterInfos[i].vertex_TR.uv2.x = (characterInfos[i].vertex_TL.uv2.y * characterInfos[i].aspectRatio) + xDelta + uvOffset;
+ characterInfos[i].vertex_BR.uv2.x = characterInfos[i].vertex_TR.uv2.x;
+ break;
+ }
+
+ switch (m_verticalMapping)
+ {
+ case TextureMappingOptions.Character:
+ characterInfos[i].vertex_BL.uv2.y = 0; // + m_uvOffset.y;
+ characterInfos[i].vertex_TL.uv2.y = 1; // + m_uvOffset.y;
+ characterInfos[i].vertex_TR.uv2.y = 1; // + m_uvOffset.y;
+ characterInfos[i].vertex_BR.uv2.y = 0; // + m_uvOffset.y;
+ break;
+
+ case TextureMappingOptions.Line:
+ characterInfos[i].vertex_BL.uv2.y = (characterInfos[i].vertex_BL.position.y - lineInfo.descender) / (lineInfo.ascender - lineInfo.descender); // + m_uvOffset.y;
+ characterInfos[i].vertex_TL.uv2.y = (characterInfos[i].vertex_TL.position.y - lineInfo.descender) / (lineInfo.ascender - lineInfo.descender); // + m_uvOffset.y;
+ characterInfos[i].vertex_TR.uv2.y = characterInfos[i].vertex_TL.uv2.y;
+ characterInfos[i].vertex_BR.uv2.y = characterInfos[i].vertex_BL.uv2.y;
+ break;
+
+ case TextureMappingOptions.Paragraph:
+ characterInfos[i].vertex_BL.uv2.y = (characterInfos[i].vertex_BL.position.y - m_meshExtents.min.y) / (m_meshExtents.max.y - m_meshExtents.min.y); // + m_uvOffset.y;
+ characterInfos[i].vertex_TL.uv2.y = (characterInfos[i].vertex_TL.position.y - m_meshExtents.min.y) / (m_meshExtents.max.y - m_meshExtents.min.y); // + m_uvOffset.y;
+ characterInfos[i].vertex_TR.uv2.y = characterInfos[i].vertex_TL.uv2.y;
+ characterInfos[i].vertex_BR.uv2.y = characterInfos[i].vertex_BL.uv2.y;
+ break;
+
+ case TextureMappingOptions.MatchAspect:
+ float yDelta = (1 - ((characterInfos[i].vertex_BL.uv2.x + characterInfos[i].vertex_TR.uv2.x) / characterInfos[i].aspectRatio)) / 2; // Center of Rectangle
+
+ characterInfos[i].vertex_BL.uv2.y = yDelta + (characterInfos[i].vertex_BL.uv2.x / characterInfos[i].aspectRatio); // + m_uvOffset.y;
+ characterInfos[i].vertex_TL.uv2.y = yDelta + (characterInfos[i].vertex_TR.uv2.x / characterInfos[i].aspectRatio); // + m_uvOffset.y;
+ characterInfos[i].vertex_BR.uv2.y = characterInfos[i].vertex_BL.uv2.y;
+ characterInfos[i].vertex_TR.uv2.y = characterInfos[i].vertex_TL.uv2.y;
+ break;
+ }
+ #if TMP_PROFILE_ON
+ Profiler.EndSample();
+ #endif
+ #endregion End UV Mapping Options
+
+ // Pack UV's so that we can pass Xscale needed for Shader to maintain 1:1 ratio.
+ #region Pack Scale into UV2
+ #if TMP_PROFILE_ON
+ Profiler.BeginSample("Pack UV");
+ #endif
+ xScale = characterInfos[i].scale * (1 - m_charWidthAdjDelta);
+ if (!characterInfos[i].isUsingAlternateTypeface && (characterInfos[i].style & FontStyles.Bold) == FontStyles.Bold) xScale *= -1;
+
+ switch (canvasRenderMode)
+ {
+ case RenderMode.ScreenSpaceOverlay:
+ xScale *= Mathf.Abs(lossyScale) / canvasScaleFactor;
+ break;
+ case RenderMode.ScreenSpaceCamera:
+ xScale *= isCameraAssigned ? Mathf.Abs(lossyScale) : 1;
+ break;
+ case RenderMode.WorldSpace:
+ xScale *= Mathf.Abs(lossyScale);
+ break;
+ }
+
+ // isBold is encoded in the X value and SDF Scale in Y.
+ //Vector2 vertexData = new Vector2((characterInfos[i].style & FontStyles.Bold) == FontStyles.Bold ? 1 : 0, xScale);
+ //characterInfos[i].vertex_BL.uv2 = vertexData;
+ //characterInfos[i].vertex_TL.uv2 = vertexData;
+ //characterInfos[i].vertex_TR.uv2 = vertexData;
+ //characterInfos[i].vertex_BR.uv2 = vertexData;
+
+ float x0 = characterInfos[i].vertex_BL.uv2.x;
+ float y0 = characterInfos[i].vertex_BL.uv2.y;
+ float x1 = characterInfos[i].vertex_TR.uv2.x;
+ float y1 = characterInfos[i].vertex_TR.uv2.y;
+
+ float dx = (int)x0;
+ float dy = (int)y0;
+
+ x0 = x0 - dx;
+ x1 = x1 - dx;
+ y0 = y0 - dy;
+ y1 = y1 - dy;
+
+ // Optimization to avoid having a vector2 returned from the Pack UV function.
+ characterInfos[i].vertex_BL.uv2.x = PackUV(x0, y0); characterInfos[i].vertex_BL.uv2.y = xScale;
+ characterInfos[i].vertex_TL.uv2.x = PackUV(x0, y1); characterInfos[i].vertex_TL.uv2.y = xScale;
+ characterInfos[i].vertex_TR.uv2.x = PackUV(x1, y1); characterInfos[i].vertex_TR.uv2.y = xScale;
+ characterInfos[i].vertex_BR.uv2.x = PackUV(x1, y0); characterInfos[i].vertex_BR.uv2.y = xScale;
+ #if TMP_PROFILE_ON
+ Profiler.EndSample();
+ #endif
+ #endregion
+ break;
+
+ // SPRITES
+ case TMP_TextElementType.Sprite:
+ // Nothing right now
+ break;
+ }
+
+ // Handle maxVisibleCharacters, maxVisibleLines and Overflow Page Mode.
+ #region Handle maxVisibleCharacters / maxVisibleLines / Page Mode
+ #if TMP_PROFILE_ON
+ Profiler.BeginSample("Process MaxVisible Characters & Lines");
+ #endif
+ if (i < m_maxVisibleCharacters && wordCount < m_maxVisibleWords && currentLine < m_maxVisibleLines && m_overflowMode != TextOverflowModes.Page)
+ {
+ characterInfos[i].vertex_BL.position += offset;
+ characterInfos[i].vertex_TL.position += offset;
+ characterInfos[i].vertex_TR.position += offset;
+ characterInfos[i].vertex_BR.position += offset;
+ }
+ else if (i < m_maxVisibleCharacters && wordCount < m_maxVisibleWords && currentLine < m_maxVisibleLines && m_overflowMode == TextOverflowModes.Page && characterInfos[i].pageNumber == pageToDisplay)
+ {
+ characterInfos[i].vertex_BL.position += offset;
+ characterInfos[i].vertex_TL.position += offset;
+ characterInfos[i].vertex_TR.position += offset;
+ characterInfos[i].vertex_BR.position += offset;
+ }
+ else
+ {
+ characterInfos[i].vertex_BL.position = Vector3.zero;
+ characterInfos[i].vertex_TL.position = Vector3.zero;
+ characterInfos[i].vertex_TR.position = Vector3.zero;
+ characterInfos[i].vertex_BR.position = Vector3.zero;
+ characterInfos[i].isVisible = false;
+ }
+ #if TMP_PROFILE_ON
+ Profiler.EndSample();
+ #endif
+ #endregion
+
+
+ // Fill Vertex Buffers for the various types of element
+ if (elementType == TMP_TextElementType.Character)
+ {
+ FillCharacterVertexBuffers(i, vert_index_X4);
+ }
+ else if (elementType == TMP_TextElementType.Sprite)
+ {
+ FillSpriteVertexBuffers(i, sprite_index_X4);
+ }
+ }
+ #endregion
+
+ // Apply Alignment and Justification Offset
+ m_textInfo.characterInfo[i].bottomLeft += offset;
+ m_textInfo.characterInfo[i].topLeft += offset;
+ m_textInfo.characterInfo[i].topRight += offset;
+ m_textInfo.characterInfo[i].bottomRight += offset;
+
+ m_textInfo.characterInfo[i].origin += offset.x;
+ m_textInfo.characterInfo[i].xAdvance += offset.x;
+
+ m_textInfo.characterInfo[i].ascender += offset.y;
+ m_textInfo.characterInfo[i].descender += offset.y;
+ m_textInfo.characterInfo[i].baseLine += offset.y;
+
+ // Update MeshExtents
+ if (isCharacterVisible)
+ {
+ //m_meshExtents.min = new Vector2(Mathf.Min(m_meshExtents.min.x, m_textInfo.characterInfo[i].bottomLeft.x), Mathf.Min(m_meshExtents.min.y, m_textInfo.characterInfo[i].bottomLeft.y));
+ //m_meshExtents.max = new Vector2(Mathf.Max(m_meshExtents.max.x, m_textInfo.characterInfo[i].topRight.x), Mathf.Max(m_meshExtents.max.y, m_textInfo.characterInfo[i].topLeft.y));
+ }
+
+ // Need to recompute lineExtent to account for the offset from justification.
+ #region Adjust lineExtents resulting from alignment offset
+ #if TMP_PROFILE_ON
+ Profiler.BeginSample("Adjust LineExtents");
+ #endif
+ if (currentLine != lastLine || i == m_characterCount - 1)
+ {
+ // Update the previous line's extents
+ if (currentLine != lastLine)
+ {
+ m_textInfo.lineInfo[lastLine].baseline += offset.y;
+ m_textInfo.lineInfo[lastLine].ascender += offset.y;
+ m_textInfo.lineInfo[lastLine].descender += offset.y;
+
+ m_textInfo.lineInfo[lastLine].lineExtents.min = new Vector2(m_textInfo.characterInfo[m_textInfo.lineInfo[lastLine].firstCharacterIndex].bottomLeft.x, m_textInfo.lineInfo[lastLine].descender);
+ m_textInfo.lineInfo[lastLine].lineExtents.max = new Vector2(m_textInfo.characterInfo[m_textInfo.lineInfo[lastLine].lastVisibleCharacterIndex].topRight.x, m_textInfo.lineInfo[lastLine].ascender);
+ }
+
+ // Update the current line's extents
+ if (i == m_characterCount - 1)
+ {
+ m_textInfo.lineInfo[currentLine].baseline += offset.y;
+ m_textInfo.lineInfo[currentLine].ascender += offset.y;
+ m_textInfo.lineInfo[currentLine].descender += offset.y;
+
+ m_textInfo.lineInfo[currentLine].lineExtents.min = new Vector2(m_textInfo.characterInfo[m_textInfo.lineInfo[currentLine].firstCharacterIndex].bottomLeft.x, m_textInfo.lineInfo[currentLine].descender);
+ m_textInfo.lineInfo[currentLine].lineExtents.max = new Vector2(m_textInfo.characterInfo[m_textInfo.lineInfo[currentLine].lastVisibleCharacterIndex].topRight.x, m_textInfo.lineInfo[currentLine].ascender);
+ }
+ }
+ #if TMP_PROFILE_ON
+ Profiler.EndSample();
+ #endif
+ #endregion
+
+
+ // Track Word Count per line and for the object
+ #region Track Word Count
+ #if TMP_PROFILE_ON
+ Profiler.BeginSample("Track Word Count");
+ #endif
+ if (char.IsLetterOrDigit(currentCharacter) || currentCharacter == 0x2D || currentCharacter == 0xAD || currentCharacter == 0x2010 || currentCharacter == 0x2011)
+ {
+ if (isStartOfWord == false)
+ {
+ isStartOfWord = true;
+ wordFirstChar = i;
+ }
+
+ // If last character is a word
+ if (isStartOfWord && i == m_characterCount - 1)
+ {
+ int size = m_textInfo.wordInfo.Length;
+ int index = m_textInfo.wordCount;
+
+ if (m_textInfo.wordCount + 1 > size)
+ TMP_TextInfo.Resize(ref m_textInfo.wordInfo, size + 1);
+
+ wordLastChar = i;
+
+ m_textInfo.wordInfo[index].firstCharacterIndex = wordFirstChar;
+ m_textInfo.wordInfo[index].lastCharacterIndex = wordLastChar;
+ m_textInfo.wordInfo[index].characterCount = wordLastChar - wordFirstChar + 1;
+ m_textInfo.wordInfo[index].textComponent = this;
+
+ wordCount += 1;
+ m_textInfo.wordCount += 1;
+ m_textInfo.lineInfo[currentLine].wordCount += 1;
+ }
+ }
+ else if (isStartOfWord || i == 0 && (!char.IsPunctuation(currentCharacter) || char.IsWhiteSpace(currentCharacter) || currentCharacter == 0x200B || i == m_characterCount - 1))
+ {
+ if (i > 0 && i < characterInfos.Length - 1 && i < m_characterCount && (currentCharacter == 39 || currentCharacter == 8217) && char.IsLetterOrDigit(characterInfos[i - 1].character) && char.IsLetterOrDigit(characterInfos[i + 1].character))
+ {
+
+ }
+ else
+ {
+ wordLastChar = i == m_characterCount - 1 && char.IsLetterOrDigit(currentCharacter) ? i : i - 1;
+ isStartOfWord = false;
+
+ int size = m_textInfo.wordInfo.Length;
+ int index = m_textInfo.wordCount;
+
+ if (m_textInfo.wordCount + 1 > size)
+ TMP_TextInfo.Resize(ref m_textInfo.wordInfo, size + 1);
+
+ m_textInfo.wordInfo[index].firstCharacterIndex = wordFirstChar;
+ m_textInfo.wordInfo[index].lastCharacterIndex = wordLastChar;
+ m_textInfo.wordInfo[index].characterCount = wordLastChar - wordFirstChar + 1;
+ m_textInfo.wordInfo[index].textComponent = this;
+
+ wordCount += 1;
+ m_textInfo.wordCount += 1;
+ m_textInfo.lineInfo[currentLine].wordCount += 1;
+ }
+ }
+ #if TMP_PROFILE_ON
+ Profiler.EndSample();
+ #endif
+ #endregion
+
+
+ // Setup & Handle Underline
+ #region Underline
+ #if TMP_PROFILE_ON
+ Profiler.BeginSample("Process Underline & Strikethrough");
+ #endif
+ // NOTE: Need to figure out how underline will be handled with multiple fonts and which font will be used for the underline.
+ bool isUnderline = (m_textInfo.characterInfo[i].style & FontStyles.Underline) == FontStyles.Underline;
+ if (isUnderline)
+ {
+ bool isUnderlineVisible = true;
+ int currentPage = m_textInfo.characterInfo[i].pageNumber;
+
+ if (i > m_maxVisibleCharacters || currentLine > m_maxVisibleLines || (m_overflowMode == TextOverflowModes.Page && currentPage + 1 != m_pageToDisplay))
+ isUnderlineVisible = false;
+
+ // We only use the scale of visible characters.
+ if (!char.IsWhiteSpace(currentCharacter) && currentCharacter != 0x200B)
+ {
+ underlineMaxScale = Mathf.Max(underlineMaxScale, m_textInfo.characterInfo[i].scale);
+ xScaleMax = Mathf.Max(xScaleMax, Mathf.Abs(xScale));
+ underlineBaseLine = Mathf.Min(currentPage == lastPage ? underlineBaseLine : k_LargePositiveFloat, m_textInfo.characterInfo[i].baseLine + font.faceInfo.underlineOffset * underlineMaxScale);
+ lastPage = currentPage; // Need to track pages to ensure we reset baseline for the new pages.
+ }
+
+ if (beginUnderline == false && isUnderlineVisible == true && i <= lineInfo.lastVisibleCharacterIndex && currentCharacter != 10 && currentCharacter != 13)
+ {
+ if (i == lineInfo.lastVisibleCharacterIndex && char.IsSeparator(currentCharacter))
+ { }
+ else
+ {
+ beginUnderline = true;
+ underlineStartScale = m_textInfo.characterInfo[i].scale;
+ if (underlineMaxScale == 0)
+ {
+ underlineMaxScale = underlineStartScale;
+ xScaleMax = xScale;
+ }
+ underline_start = new Vector3(m_textInfo.characterInfo[i].bottomLeft.x, underlineBaseLine, 0);
+ underlineColor = m_textInfo.characterInfo[i].underlineColor;
+ }
+ }
+
+ // End Underline if text only contains one character.
+ if (beginUnderline && m_characterCount == 1)
+ {
+ beginUnderline = false;
+ underline_end = new Vector3(m_textInfo.characterInfo[i].topRight.x, underlineBaseLine, 0);
+ underlineEndScale = m_textInfo.characterInfo[i].scale;
+
+ DrawUnderlineMesh(underline_start, underline_end, ref last_vert_index, underlineStartScale, underlineEndScale, underlineMaxScale, xScaleMax, underlineColor);
+ underlineMaxScale = 0;
+ xScaleMax = 0;
+ underlineBaseLine = k_LargePositiveFloat;
+ }
+ else if (beginUnderline && (i == lineInfo.lastCharacterIndex || i >= lineInfo.lastVisibleCharacterIndex))
+ {
+ // Terminate underline at previous visible character if space or carriage return.
+ if (char.IsWhiteSpace(currentCharacter) || currentCharacter == 0x200B)
+ {
+ int lastVisibleCharacterIndex = lineInfo.lastVisibleCharacterIndex;
+ underline_end = new Vector3(m_textInfo.characterInfo[lastVisibleCharacterIndex].topRight.x, underlineBaseLine, 0);
+ underlineEndScale = m_textInfo.characterInfo[lastVisibleCharacterIndex].scale;
+ }
+ else
+ { // End underline if last character of the line.
+ underline_end = new Vector3(m_textInfo.characterInfo[i].topRight.x, underlineBaseLine, 0);
+ underlineEndScale = m_textInfo.characterInfo[i].scale;
+ }
+
+ beginUnderline = false;
+ DrawUnderlineMesh(underline_start, underline_end, ref last_vert_index, underlineStartScale, underlineEndScale, underlineMaxScale, xScaleMax, underlineColor);
+ underlineMaxScale = 0;
+ xScaleMax = 0;
+ underlineBaseLine = k_LargePositiveFloat;
+ }
+ else if (beginUnderline && !isUnderlineVisible)
+ {
+ beginUnderline = false;
+ underline_end = new Vector3(m_textInfo.characterInfo[i - 1].topRight.x, underlineBaseLine, 0);
+ underlineEndScale = m_textInfo.characterInfo[i - 1].scale;
+
+ DrawUnderlineMesh(underline_start, underline_end, ref last_vert_index, underlineStartScale, underlineEndScale, underlineMaxScale, xScaleMax, underlineColor);
+ underlineMaxScale = 0;
+ xScaleMax = 0;
+ underlineBaseLine = k_LargePositiveFloat;
+ }
+ else if (beginUnderline && i < m_characterCount - 1 && !underlineColor.Compare(m_textInfo.characterInfo[i + 1].underlineColor))
+ {
+ // End underline if underline color has changed.
+ beginUnderline = false;
+ underline_end = new Vector3(m_textInfo.characterInfo[i].topRight.x, underlineBaseLine, 0);
+ underlineEndScale = m_textInfo.characterInfo[i].scale;
+
+ DrawUnderlineMesh(underline_start, underline_end, ref last_vert_index, underlineStartScale, underlineEndScale, underlineMaxScale, xScaleMax, underlineColor);
+ underlineMaxScale = 0;
+ xScaleMax = 0;
+ underlineBaseLine = k_LargePositiveFloat;
+ }
+ }
+ else
+ {
+ // End Underline
+ if (beginUnderline == true)
+ {
+ beginUnderline = false;
+ underline_end = new Vector3(m_textInfo.characterInfo[i - 1].topRight.x, underlineBaseLine, 0);
+ underlineEndScale = m_textInfo.characterInfo[i - 1].scale;
+
+ DrawUnderlineMesh(underline_start, underline_end, ref last_vert_index, underlineStartScale, underlineEndScale, underlineMaxScale, xScaleMax, underlineColor);
+ underlineMaxScale = 0;
+ xScaleMax = 0;
+ underlineBaseLine = k_LargePositiveFloat;
+ }
+ }
+ #endregion
+
+
+ // Setup & Handle Strikethrough
+ #region Strikethrough
+ // NOTE: Need to figure out how underline will be handled with multiple fonts and which font will be used for the underline.
+ bool isStrikethrough = (m_textInfo.characterInfo[i].style & FontStyles.Strikethrough) == FontStyles.Strikethrough;
+ float strikethroughOffset = currentFontAsset.faceInfo.strikethroughOffset;
+
+ if (isStrikethrough)
+ {
+ bool isStrikeThroughVisible = true;
+
+ if (i > m_maxVisibleCharacters || currentLine > m_maxVisibleLines || (m_overflowMode == TextOverflowModes.Page && m_textInfo.characterInfo[i].pageNumber + 1 != m_pageToDisplay))
+ isStrikeThroughVisible = false;
+
+ if (beginStrikethrough == false && isStrikeThroughVisible && i <= lineInfo.lastVisibleCharacterIndex && currentCharacter != 10 && currentCharacter != 13)
+ {
+ if (i == lineInfo.lastVisibleCharacterIndex && char.IsSeparator(currentCharacter))
+ { }
+ else
+ {
+ beginStrikethrough = true;
+ strikethroughPointSize = m_textInfo.characterInfo[i].pointSize;
+ strikethroughScale = m_textInfo.characterInfo[i].scale;
+ strikethrough_start = new Vector3(m_textInfo.characterInfo[i].bottomLeft.x, m_textInfo.characterInfo[i].baseLine + strikethroughOffset * strikethroughScale, 0);
+ strikethroughColor = m_textInfo.characterInfo[i].strikethroughColor;
+ strikethroughBaseline = m_textInfo.characterInfo[i].baseLine;
+ //Debug.Log("Char [" + currentCharacter + "] Start Strikethrough POS: " + strikethrough_start);
+ }
+ }
+
+ // End Strikethrough if text only contains one character.
+ if (beginStrikethrough && m_characterCount == 1)
+ {
+ beginStrikethrough = false;
+ strikethrough_end = new Vector3(m_textInfo.characterInfo[i].topRight.x, m_textInfo.characterInfo[i].baseLine + strikethroughOffset * strikethroughScale, 0);
+
+ DrawUnderlineMesh(strikethrough_start, strikethrough_end, ref last_vert_index, strikethroughScale, strikethroughScale, strikethroughScale, xScale, strikethroughColor);
+ }
+ else if (beginStrikethrough && i == lineInfo.lastCharacterIndex)
+ {
+ // Terminate Strikethrough at previous visible character if space or carriage return.
+ if (char.IsWhiteSpace(currentCharacter) || currentCharacter == 0x200B)
+ {
+ int lastVisibleCharacterIndex = lineInfo.lastVisibleCharacterIndex;
+ strikethrough_end = new Vector3(m_textInfo.characterInfo[lastVisibleCharacterIndex].topRight.x, m_textInfo.characterInfo[lastVisibleCharacterIndex].baseLine + strikethroughOffset * strikethroughScale, 0);
+ }
+ else
+ {
+ // Terminate Strikethrough at last character of line.
+ strikethrough_end = new Vector3(m_textInfo.characterInfo[i].topRight.x, m_textInfo.characterInfo[i].baseLine + strikethroughOffset * strikethroughScale, 0);
+ }
+
+ beginStrikethrough = false;
+ DrawUnderlineMesh(strikethrough_start, strikethrough_end, ref last_vert_index, strikethroughScale, strikethroughScale, strikethroughScale, xScale, strikethroughColor);
+ }
+ else if (beginStrikethrough && i < m_characterCount && (m_textInfo.characterInfo[i + 1].pointSize != strikethroughPointSize || !TMP_Math.Approximately(m_textInfo.characterInfo[i + 1].baseLine + offset.y, strikethroughBaseline)))
+ {
+ // Terminate Strikethrough if scale changes.
+ beginStrikethrough = false;
+
+ int lastVisibleCharacterIndex = lineInfo.lastVisibleCharacterIndex;
+ if (i > lastVisibleCharacterIndex)
+ strikethrough_end = new Vector3(m_textInfo.characterInfo[lastVisibleCharacterIndex].topRight.x, m_textInfo.characterInfo[lastVisibleCharacterIndex].baseLine + strikethroughOffset * strikethroughScale, 0);
+ else
+ strikethrough_end = new Vector3(m_textInfo.characterInfo[i].topRight.x, m_textInfo.characterInfo[i].baseLine + strikethroughOffset * strikethroughScale, 0);
+
+ DrawUnderlineMesh(strikethrough_start, strikethrough_end, ref last_vert_index, strikethroughScale, strikethroughScale, strikethroughScale, xScale, strikethroughColor);
+ //Debug.Log("Char [" + currentCharacter + "] at Index: " + i + " End Strikethrough POS: " + strikethrough_end + " Baseline: " + m_textInfo.characterInfo[i].baseLine.ToString("f3"));
+ }
+ else if (beginStrikethrough && i < m_characterCount && currentFontAsset.GetInstanceID() != characterInfos[i + 1].fontAsset.GetInstanceID())
+ {
+ // Terminate Strikethrough if font asset changes.
+ beginStrikethrough = false;
+ strikethrough_end = new Vector3(m_textInfo.characterInfo[i].topRight.x, m_textInfo.characterInfo[i].baseLine + strikethroughOffset * strikethroughScale, 0);
+
+ DrawUnderlineMesh(strikethrough_start, strikethrough_end, ref last_vert_index, strikethroughScale, strikethroughScale, strikethroughScale, xScale, strikethroughColor);
+ }
+ else if (beginStrikethrough && !isStrikeThroughVisible)
+ {
+ // Terminate Strikethrough if character is not visible.
+ beginStrikethrough = false;
+ strikethrough_end = new Vector3(m_textInfo.characterInfo[i - 1].topRight.x, m_textInfo.characterInfo[i - 1].baseLine + strikethroughOffset * strikethroughScale, 0);
+
+ DrawUnderlineMesh(strikethrough_start, strikethrough_end, ref last_vert_index, strikethroughScale, strikethroughScale, strikethroughScale, xScale, strikethroughColor);
+ }
+ }
+ else
+ {
+ // End Strikethrough
+ if (beginStrikethrough == true)
+ {
+ beginStrikethrough = false;
+ strikethrough_end = new Vector3(m_textInfo.characterInfo[i - 1].topRight.x, m_textInfo.characterInfo[i - 1].baseLine + strikethroughOffset * strikethroughScale, 0);
+
+ DrawUnderlineMesh(strikethrough_start, strikethrough_end, ref last_vert_index, strikethroughScale, strikethroughScale, strikethroughScale, xScale, strikethroughColor);
+ }
+ }
+ #endregion
+
+
+ // HANDLE TEXT HIGHLIGHTING
+ #region Text Highlighting
+ bool isHighlight = (m_textInfo.characterInfo[i].style & FontStyles.Highlight) == FontStyles.Highlight;
+ if (isHighlight)
+ {
+ bool isHighlightVisible = true;
+ int currentPage = m_textInfo.characterInfo[i].pageNumber;
+
+ if (i > m_maxVisibleCharacters || currentLine > m_maxVisibleLines || (m_overflowMode == TextOverflowModes.Page && currentPage + 1 != m_pageToDisplay))
+ isHighlightVisible = false;
+
+ if (beginHighlight == false && isHighlightVisible == true && i <= lineInfo.lastVisibleCharacterIndex && currentCharacter != 10 && currentCharacter != 13)
+ {
+ if (i == lineInfo.lastVisibleCharacterIndex && char.IsSeparator(currentCharacter))
+ { }
+ else
+ {
+ beginHighlight = true;
+ highlight_start = k_LargePositiveVector2;
+ highlight_end = k_LargeNegativeVector2;
+ highlightColor = m_textInfo.characterInfo[i].highlightColor;
+ }
+ }
+
+ if (beginHighlight)
+ {
+ Color32 currentHighlightColor = m_textInfo.characterInfo[i].highlightColor;
+ bool isColorTransition = false;
+
+ // Handle Highlight color changes
+ if (!highlightColor.Compare(currentHighlightColor))
+ {
+ // End drawing at the start of new highlight color to prevent a gap between highlight sections.
+ highlight_end.x = (highlight_end.x + m_textInfo.characterInfo[i].bottomLeft.x) / 2;
+
+ highlight_start.y = Mathf.Min(highlight_start.y, m_textInfo.characterInfo[i].descender);
+ highlight_end.y = Mathf.Max(highlight_end.y, m_textInfo.characterInfo[i].ascender);
+
+ DrawTextHighlight(highlight_start, highlight_end, ref last_vert_index, highlightColor);
+
+ beginHighlight = true;
+ highlight_start = highlight_end;
+
+ highlight_end = new Vector3(m_textInfo.characterInfo[i].topRight.x, m_textInfo.characterInfo[i].descender, 0);
+ highlightColor = m_textInfo.characterInfo[i].highlightColor;
+
+ isColorTransition = true;
+ }
+
+ if (!isColorTransition)
+ {
+ // Use the Min / Max Extents of the Highlight area to handle different character sizes and fonts.
+ highlight_start.x = Mathf.Min(highlight_start.x, m_textInfo.characterInfo[i].bottomLeft.x);
+ highlight_start.y = Mathf.Min(highlight_start.y, m_textInfo.characterInfo[i].descender);
+
+ highlight_end.x = Mathf.Max(highlight_end.x, m_textInfo.characterInfo[i].topRight.x);
+ highlight_end.y = Mathf.Max(highlight_end.y, m_textInfo.characterInfo[i].ascender);
+ }
+ }
+
+ // End Highlight if text only contains one character.
+ if (beginHighlight && m_characterCount == 1)
+ {
+ beginHighlight = false;
+
+ DrawTextHighlight(highlight_start, highlight_end, ref last_vert_index, highlightColor);
+ }
+ else if (beginHighlight && (i == lineInfo.lastCharacterIndex || i >= lineInfo.lastVisibleCharacterIndex))
+ {
+ beginHighlight = false;
+ DrawTextHighlight(highlight_start, highlight_end, ref last_vert_index, highlightColor);
+ }
+ else if (beginHighlight && !isHighlightVisible)
+ {
+ beginHighlight = false;
+ DrawTextHighlight(highlight_start, highlight_end, ref last_vert_index, highlightColor);
+ }
+ }
+ else
+ {
+ // End Highlight
+ if (beginHighlight == true)
+ {
+ beginHighlight = false;
+ DrawTextHighlight(highlight_start, highlight_end, ref last_vert_index, highlightColor);
+ }
+ }
+ #endregion
+ #if TMP_PROFILE_ON
+ Profiler.EndSample();
+ #endif
+ #endregion
+
+ lastLine = currentLine;
+ }
+ #endregion
+
+
+ // METRICS ABOUT THE TEXT OBJECT
+ m_textInfo.characterCount = m_characterCount;
+ m_textInfo.spriteCount = m_spriteCount;
+ m_textInfo.lineCount = lineCount;
+ m_textInfo.wordCount = wordCount != 0 && m_characterCount > 0 ? wordCount : 1;
+ m_textInfo.pageCount = m_pageNumber + 1;
+
+ #if TMP_PROFILE_PHASES_ON
+ Profiler.EndSample();
+ #endif
+
+
+ // *** UPLOAD MESH DATA ***
+ #if TMP_PROFILE_PHASES_ON
+ Profiler.BeginSample("TMP Generate Text - Phase III");
+ #endif
+ if (m_renderMode == TextRenderFlags.Render && IsActive())
+ {
+ // Clear unused vertices
+ //m_textInfo.meshInfo[0].ClearUnusedVertices();
+
+ // Must ensure the Canvas support the additon vertex attributes used by TMP.
+ if (m_canvas.additionalShaderChannels != (AdditionalCanvasShaderChannels)25)
+ m_canvas.additionalShaderChannels |= (AdditionalCanvasShaderChannels)25;
+
+ // Sort the geometry of the text object if needed.
+ if (m_geometrySortingOrder != VertexSortingOrder.Normal)
+ m_textInfo.meshInfo[0].SortGeometry(VertexSortingOrder.Reverse);
+
+ // Upload Mesh Data
+ m_mesh.MarkDynamic();
+ m_mesh.vertices = m_textInfo.meshInfo[0].vertices;
+ m_mesh.uv = m_textInfo.meshInfo[0].uvs0;
+ m_mesh.uv2 = m_textInfo.meshInfo[0].uvs2;
+ //m_mesh.uv4 = m_textInfo.meshInfo[0].uvs4;
+ m_mesh.colors32 = m_textInfo.meshInfo[0].colors32;
+
+ // Compute Bounds for the mesh. Manual computation is more efficient then using Mesh.recalcualteBounds.
+ m_mesh.RecalculateBounds();
+ //m_mesh.bounds = new Bounds(new Vector3((m_meshExtents.max.x + m_meshExtents.min.x) / 2, (m_meshExtents.max.y + m_meshExtents.min.y) / 2, 0) + offset, new Vector3(m_meshExtents.max.x - m_meshExtents.min.x, m_meshExtents.max.y - m_meshExtents.min.y, 0));
+
+ m_canvasRenderer.SetMesh(m_mesh);
+
+ // Cache CanvasRenderer color of the parent text object.
+ Color parentBaseColor = m_canvasRenderer.GetColor();
+
+ for (int i = 1; i < m_textInfo.materialCount; i++)
+ {
+ // Clear unused vertices
+ m_textInfo.meshInfo[i].ClearUnusedVertices();
+
+ if (m_subTextObjects[i] == null) continue;
+
+ // Sort the geometry of the sub-text objects if needed.
+ if (m_geometrySortingOrder != VertexSortingOrder.Normal)
+ m_textInfo.meshInfo[i].SortGeometry(VertexSortingOrder.Reverse);
+
+ //m_subTextObjects[i].mesh.MarkDynamic();
+ m_subTextObjects[i].mesh.vertices = m_textInfo.meshInfo[i].vertices;
+ m_subTextObjects[i].mesh.uv = m_textInfo.meshInfo[i].uvs0;
+ m_subTextObjects[i].mesh.uv2 = m_textInfo.meshInfo[i].uvs2;
+ //m_subTextObjects[i].mesh.uv4 = m_textInfo.meshInfo[i].uvs4;
+ m_subTextObjects[i].mesh.colors32 = m_textInfo.meshInfo[i].colors32;
+
+ m_subTextObjects[i].mesh.RecalculateBounds();
+
+ m_subTextObjects[i].canvasRenderer.SetMesh(m_subTextObjects[i].mesh);
+
+ // Set CanvasRenderer color to match the parent text object.
+ m_subTextObjects[i].canvasRenderer.SetColor(parentBaseColor);
+ }
+ }
+
+ // Event indicating the text has been regenerated.
+ TMPro_EventManager.ON_TEXT_CHANGED(this);
+ //SendOnTextChanged();
+
+ #if TMP_PROFILE_PHASES_ON
+ Profiler.EndSample();
+ #endif
+
+ //Debug.Log("Done Rendering Text.");
+ }
+
+
+ /// <summary>
+ /// Method to return the local corners of the Text Container or RectTransform.
+ /// </summary>
+ /// <returns></returns>
+ protected override Vector3[] GetTextContainerLocalCorners()
+ {
+ if (m_rectTransform == null) m_rectTransform = this.rectTransform;
+
+ m_rectTransform.GetLocalCorners(m_RectTransformCorners);
+
+ return m_RectTransformCorners;
+ }
+
+
+ /// <summary>
+ /// Method to Enable or Disable child SubMesh objects.
+ /// </summary>
+ /// <param name="state"></param>
+ protected override void SetActiveSubMeshes(bool state)
+ {
+ for (int i = 1; i < m_subTextObjects.Length && m_subTextObjects[i] != null; i++)
+ {
+ if (m_subTextObjects[i].enabled != state)
+ m_subTextObjects[i].enabled = state;
+ }
+ }
+
+
+ /// <summary>
+ /// Method returning the compound bounds of the text object and child sub objects.
+ /// </summary>
+ /// <returns></returns>
+ protected override Bounds GetCompoundBounds()
+ {
+ Bounds mainBounds = m_mesh.bounds;
+ Vector3 min = mainBounds.min;
+ Vector3 max = mainBounds.max;
+
+ for (int i = 1; i < m_subTextObjects.Length && m_subTextObjects[i] != null; i++)
+ {
+ Bounds subBounds = m_subTextObjects[i].mesh.bounds;
+ min.x = min.x < subBounds.min.x ? min.x : subBounds.min.x;
+ min.y = min.y < subBounds.min.y ? min.y : subBounds.min.y;
+
+ max.x = max.x > subBounds.max.x ? max.x : subBounds.max.x;
+ max.y = max.y > subBounds.max.y ? max.y : subBounds.max.y;
+ }
+
+ Vector3 center = (min + max) / 2;
+ Vector2 size = max - min;
+ return new Bounds(center, size);
+ }
+
+
+ //public override void UpdateGeometry()
+ //{
+
+ //}
+
+
+ /// <summary>
+ /// Method to Update Scale in UV2
+ /// </summary>
+ //void UpdateSDFScale(float lossyScale)
+ //{
+ // // TODO: Resolve - Underline / Strikethrough segments not getting their SDF Scale adjusted.
+
+ // //Debug.Log("Updating SDF Scale.");
+
+ // // Return if we don't have a valid reference to a Canvas.
+ // if (m_canvas == null)
+ // {
+ // m_canvas = GetCanvas();
+ // if (m_canvas == null) return;
+ // }
+
+ // lossyScale = lossyScale == 0 ? 1 : lossyScale;
+
+ // float xScale = 0;
+ // float canvasScaleFactor = m_canvas.scaleFactor;
+
+ // if (m_canvas.renderMode == RenderMode.ScreenSpaceOverlay)
+ // xScale = lossyScale / canvasScaleFactor;
+ // else if (m_canvas.renderMode == RenderMode.ScreenSpaceCamera)
+ // xScale = m_canvas.worldCamera != null ? lossyScale : 1;
+ // else
+ // xScale = lossyScale;
+
+ // // Iterate through each of the characters.
+ // for (int i = 0; i < m_textInfo.characterCount; i++)
+ // {
+ // // Only update scale for visible characters.
+ // if (m_textInfo.characterInfo[i].isVisible && m_textInfo.characterInfo[i].elementType == TMP_TextElementType.Character)
+ // {
+ // float scale = xScale * m_textInfo.characterInfo[i].scale * (1 - m_charWidthAdjDelta);
+ // if (!m_textInfo.characterInfo[i].isUsingAlternateTypeface && (m_textInfo.characterInfo[i].style & FontStyles.Bold) == FontStyles.Bold) scale *= -1;
+
+ // int index = m_textInfo.characterInfo[i].materialReferenceIndex;
+ // int vertexIndex = m_textInfo.characterInfo[i].vertexIndex;
+
+ // m_textInfo.meshInfo[index].uvs2[vertexIndex + 0].y = scale;
+ // m_textInfo.meshInfo[index].uvs2[vertexIndex + 1].y = scale;
+ // m_textInfo.meshInfo[index].uvs2[vertexIndex + 2].y = scale;
+ // m_textInfo.meshInfo[index].uvs2[vertexIndex + 3].y = scale;
+ // }
+ // }
+
+ // // Push the updated uv2 scale information to the meshes.
+ // for (int i = 0; i < m_textInfo.materialCount; i++)
+ // {
+ // if (i == 0)
+ // {
+ // m_mesh.uv2 = m_textInfo.meshInfo[0].uvs2;
+ // m_canvasRenderer.SetMesh(m_mesh);
+ // }
+ // else
+ // {
+ // m_subTextObjects[i].mesh.uv2 = m_textInfo.meshInfo[i].uvs2;
+ // m_subTextObjects[i].canvasRenderer.SetMesh(m_subTextObjects[i].mesh);
+ // }
+ // }
+ //}
+
+ /// <summary>
+ /// Method to update the SDF Scale in UV2.
+ /// </summary>
+ /// <param name="scaleDelta"></param>
+ void UpdateSDFScale(float scaleDelta)
+ {
+ if (scaleDelta == 0 || scaleDelta == float.PositiveInfinity)
+ {
+ m_havePropertiesChanged = true;
+ OnPreRenderCanvas();
+ return;
+ }
+
+ for (int materialIndex = 0; materialIndex < m_textInfo.materialCount; materialIndex ++)
+ {
+ TMP_MeshInfo meshInfo = m_textInfo.meshInfo[materialIndex];
+
+ for (int i = 0; i < meshInfo.uvs2.Length; i++)
+ {
+ meshInfo.uvs2[i].y *= Mathf.Abs(scaleDelta);
+ }
+ }
+
+ // Push the updated uv2 scale information to the meshes.
+ for (int i = 0; i < m_textInfo.materialCount; i++)
+ {
+ if (i == 0)
+ {
+ m_mesh.uv2 = m_textInfo.meshInfo[0].uvs2;
+ m_canvasRenderer.SetMesh(m_mesh);
+ }
+ else
+ {
+ m_subTextObjects[i].mesh.uv2 = m_textInfo.meshInfo[i].uvs2;
+ m_subTextObjects[i].canvasRenderer.SetMesh(m_subTextObjects[i].mesh);
+ }
+ }
+ }
+
+
+ // Function to offset vertices position to account for line spacing changes.
+ protected override void AdjustLineOffset(int startIndex, int endIndex, float offset)
+ {
+ Vector3 vertexOffset = new Vector3(0, offset, 0);
+
+ for (int i = startIndex; i <= endIndex; i++)
+ {
+ m_textInfo.characterInfo[i].bottomLeft -= vertexOffset;
+ m_textInfo.characterInfo[i].topLeft -= vertexOffset;
+ m_textInfo.characterInfo[i].topRight -= vertexOffset;
+ m_textInfo.characterInfo[i].bottomRight -= vertexOffset;
+
+ m_textInfo.characterInfo[i].ascender -= vertexOffset.y;
+ m_textInfo.characterInfo[i].baseLine -= vertexOffset.y;
+ m_textInfo.characterInfo[i].descender -= vertexOffset.y;
+
+ if (m_textInfo.characterInfo[i].isVisible)
+ {
+ m_textInfo.characterInfo[i].vertex_BL.position -= vertexOffset;
+ m_textInfo.characterInfo[i].vertex_TL.position -= vertexOffset;
+ m_textInfo.characterInfo[i].vertex_TR.position -= vertexOffset;
+ m_textInfo.characterInfo[i].vertex_BR.position -= vertexOffset;
+ }
+ }
+ }
+
+ }
+} \ No newline at end of file
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMPro_UGUI_Private.cs.meta b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMPro_UGUI_Private.cs.meta
new file mode 100644
index 0000000..249e391
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TMPro_UGUI_Private.cs.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: 3069a00b8c364df395994d7d379e0a99
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {fileID: 2800000, guid: 8e098d8d28c5182419f7a1c8b91ca722, type: 3}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TextContainer.cs b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TextContainer.cs
new file mode 100644
index 0000000..4e7c2b1
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TextContainer.cs
@@ -0,0 +1,370 @@
+using UnityEngine;
+using UnityEngine.EventSystems;
+using System.Collections;
+
+
+namespace TMPro
+{
+
+ public enum TextContainerAnchors { TopLeft = 0, Top = 1, TopRight = 2, Left = 3, Middle = 4, Right = 5, BottomLeft = 6, Bottom = 7, BottomRight = 8, Custom = 9 };
+
+
+ [RequireComponent(typeof(RectTransform))]
+ [AddComponentMenu("Layout/Text Container")]
+ public class TextContainer : UIBehaviour
+ {
+
+ #pragma warning disable 0618 // Disabled warning related to deprecated properties. This is for backwards compatibility.
+
+ public bool hasChanged
+ {
+ get { return m_hasChanged; }
+ set { m_hasChanged = value; }
+ }
+ private bool m_hasChanged;
+
+
+ // Pivot / Transform Position
+ public Vector2 pivot
+ {
+ get { return m_pivot; }
+ set { /*Debug.Log("Pivot has changed.");*/ if (m_pivot != value) { m_pivot = value; m_anchorPosition = GetAnchorPosition(m_pivot); m_hasChanged = true; OnContainerChanged(); } }
+ }
+ [SerializeField]
+ private Vector2 m_pivot;
+
+
+ public TextContainerAnchors anchorPosition
+ {
+ get { return m_anchorPosition; }
+ set { /*Debug.Log("Anchor has changed.");*/ if (m_anchorPosition != value) { m_anchorPosition = value; m_pivot = GetPivot(m_anchorPosition); m_hasChanged = true; OnContainerChanged(); } }
+ }
+ [SerializeField]
+ private TextContainerAnchors m_anchorPosition = TextContainerAnchors.Middle;
+
+
+ // Rect which defines the Rectangle
+ public Rect rect
+ {
+ get { return m_rect; }
+ set { /*Debug.Log("Rectangle has changed.");*/ if (m_rect != value) { m_rect = value; /*m_size = new Vector2(m_rect.width, m_rect.height);*/ m_hasChanged = true; OnContainerChanged(); } }
+ }
+ [SerializeField]
+ private Rect m_rect;
+
+
+ public Vector2 size
+ {
+ get { return new Vector2(m_rect.width, m_rect.height); }
+ set { /*Debug.Log("Size has changed.");*/ if (new Vector2(m_rect.width, m_rect.height) != value) { SetRect(value); m_hasChanged = true; m_isDefaultWidth = false; m_isDefaultHeight = false; OnContainerChanged(); } }
+ }
+
+
+ // Sets the width of the Text Container.
+ public float width
+ {
+ get { return m_rect.width; }
+ set { /*Debug.Log("Width has changed.");*/ SetRect(new Vector2(value, m_rect.height)); m_hasChanged = true; m_isDefaultWidth = false; OnContainerChanged(); }
+ }
+
+
+ // Sets the height of the Text Container.
+ public float height
+ {
+ get { return m_rect.height; }
+ set { SetRect(new Vector2(m_rect.width, value)); m_hasChanged = true; m_isDefaultHeight = false; OnContainerChanged(); }
+ }
+
+
+ // Used to determine if the user has changed the width of the Text Container.
+ public bool isDefaultWidth
+ {
+ get { return m_isDefaultWidth; }
+ }
+ private bool m_isDefaultWidth;
+
+ // Used to determine if the user has changed the height of the Text Container.
+ public bool isDefaultHeight
+ {
+ get { return m_isDefaultHeight; }
+ }
+ private bool m_isDefaultHeight;
+
+
+ public bool isAutoFitting
+ {
+ get { return m_isAutoFitting; }
+ set { m_isAutoFitting = value; }
+ }
+ private bool m_isAutoFitting = false;
+
+
+ // Corners of the Text Container
+ public Vector3[] corners
+ {
+ get { return m_corners; }
+ }
+ private Vector3[] m_corners = new Vector3[4];
+
+
+ public Vector3[] worldCorners
+ {
+ get { return m_worldCorners; }
+ }
+ private Vector3[] m_worldCorners = new Vector3[4];
+
+
+ //public Vector3 normal
+ //{
+ // get { return m_normal; }
+ //}
+ //private Vector3 m_normal;
+
+
+ // The margin offset from the Rectangle Bounds
+ public Vector4 margins
+ {
+ get { return m_margins; }
+ set { if (m_margins != value) { /*Debug.Log("Margins have changed.");*/ m_margins = value; m_hasChanged = true; OnContainerChanged(); } }
+ }
+ [SerializeField]
+ private Vector4 m_margins;
+
+
+ /// <summary>
+ /// The RectTransform used by the object
+ /// </summary>
+ public RectTransform rectTransform
+ {
+ get
+ {
+ if (m_rectTransform == null) m_rectTransform = GetComponent<RectTransform>();
+
+ return m_rectTransform;
+ }
+ }
+ private RectTransform m_rectTransform;
+
+
+ //private Transform m_transform;
+ //private bool m_isAddingRectTransform;
+ private static Vector2 k_defaultSize = new Vector2(100, 100);
+
+
+ /// <summary>
+ ///
+ /// </summary>
+ public TextMeshPro textMeshPro
+ {
+ get
+ {
+ if (m_textMeshPro == null) m_textMeshPro = GetComponent<TextMeshPro>();
+ return m_textMeshPro;
+ }
+ }
+ private TextMeshPro m_textMeshPro;
+
+
+ protected override void Awake()
+ {
+ Debug.LogWarning("The Text Container component is now Obsolete and can safely be removed from [" + gameObject.name + "].", this);
+
+ return;
+ }
+
+
+ /// <summary>
+ ///
+ /// </summary>
+ protected override void OnEnable()
+ {
+ //Debug.Log("Text Container OnEnable() called.");
+
+ OnContainerChanged();
+ }
+
+
+ /// <summary>
+ ///
+ /// </summary>
+ protected override void OnDisable()
+ {
+ //Debug.Log("OnDisable() called.");
+ }
+
+
+ /// <summary>
+ ///
+ /// </summary>
+ void OnContainerChanged()
+ {
+ //Debug.Log("OnContainerChanged");
+
+ UpdateCorners();
+ //UpdateWorldCorners();
+
+ if (this.m_rectTransform != null)
+ {
+ m_rectTransform.sizeDelta = this.size;
+ m_rectTransform.hasChanged = true;
+ }
+
+ if (this.textMeshPro != null)
+ {
+ m_textMeshPro.SetVerticesDirty();
+ m_textMeshPro.margin = m_margins;
+ }
+ }
+
+
+#if UNITY_EDITOR
+ /// <summary>
+ ///
+ /// </summary>
+ protected override void OnValidate()
+ {
+ //Debug.Log("OnValidate() called.");
+ m_hasChanged = true;
+ OnContainerChanged();
+ }
+#endif
+
+
+ /*
+ void LateUpdate()
+ {
+ // Used by the Run Time Text Input Component ... This will have to be changed.
+ if (m_transform.hasChanged)
+ UpdateWorldCorners();
+ }
+ */
+
+
+
+ /// <summary>
+ /// Callback from Unity to handle RectTransform changes.
+ /// </summary>
+ protected override void OnRectTransformDimensionsChange()
+ {
+ // Required to add a RectTransform to objects created in previous releases.
+ if (this.rectTransform == null) m_rectTransform = gameObject.AddComponent<RectTransform>();
+
+ if (m_rectTransform.sizeDelta != k_defaultSize)
+ this.size = m_rectTransform.sizeDelta;
+
+ pivot = m_rectTransform.pivot;
+
+ m_hasChanged = true;
+ OnContainerChanged();
+ }
+
+
+ private void SetRect(Vector2 size)
+ {
+ m_rect = new Rect(m_rect.x, m_rect.y, size.x, size.y);
+ //UpdateCorners();
+ }
+
+ private void UpdateCorners()
+ {
+ m_corners[0] = new Vector3(-m_pivot.x * m_rect.width, (- m_pivot.y) * m_rect.height);
+ m_corners[1] = new Vector3(-m_pivot.x * m_rect.width, (1 - m_pivot.y) * m_rect.height);
+ m_corners[2] = new Vector3((1 - m_pivot.x) * m_rect.width, (1 - m_pivot.y) * m_rect.height);
+ m_corners[3] = new Vector3((1 - m_pivot.x) * m_rect.width, (- m_pivot.y) * m_rect.height);
+ //Debug.Log("Pivot " + m_pivot + " Corners 0: " + m_corners[0] + " 1: " + m_corners[1] + " 2: " + m_corners[2] + " 3: " + m_corners[3]);
+
+ if (m_rectTransform != null)
+ m_rectTransform.pivot = m_pivot;
+ }
+
+
+ //private void UpdateWorldCorners()
+ //{
+ // if (m_transform == null)
+ // return;
+
+ // Vector3 position = m_transform.position;
+ // m_worldCorners[0] = position + m_transform.TransformDirection(m_corners[0]);
+ // m_worldCorners[1] = position + m_transform.TransformDirection(m_corners[1]);
+ // m_worldCorners[2] = position + m_transform.TransformDirection(m_corners[2]);
+ // m_worldCorners[3] = position + m_transform.TransformDirection(m_corners[3]);
+
+ // m_normal = Vector3.Cross(worldCorners[1] - worldCorners[0], worldCorners[3] - worldCorners[0]);
+ //}
+
+
+ //public Vector3[] GetWorldCorners()
+ //{
+ // UpdateWorldCorners();
+
+ // return m_worldCorners;
+ //}
+
+
+ Vector2 GetPivot(TextContainerAnchors anchor)
+ {
+ Vector2 pivot = Vector2.zero;
+
+ switch (anchor)
+ {
+ case TextContainerAnchors.TopLeft:
+ pivot = new Vector2(0, 1);
+ break;
+ case TextContainerAnchors.Top:
+ pivot = new Vector2(0.5f, 1);
+ break;
+ case TextContainerAnchors.TopRight:
+ pivot = new Vector2(1, 1);
+ break;
+ case TextContainerAnchors.Left:
+ pivot = new Vector2(0, 0.5f);
+ break;
+ case TextContainerAnchors.Middle:
+ pivot = new Vector2(0.5f, 0.5f);
+ break;
+ case TextContainerAnchors.Right:
+ pivot = new Vector2(1, 0.5f);
+ break;
+ case TextContainerAnchors.BottomLeft:
+ pivot = new Vector2(0, 0);
+ break;
+ case TextContainerAnchors.Bottom:
+ pivot = new Vector2(0.5f, 0);
+ break;
+ case TextContainerAnchors.BottomRight:
+ pivot = new Vector2(1, 0);
+ break;
+ }
+
+ return pivot;
+ }
+
+
+ // Method which returns the Anchor position based on pivot value.
+ TextContainerAnchors GetAnchorPosition(Vector2 pivot)
+ {
+
+ if (pivot == new Vector2(0, 1))
+ return TextContainerAnchors.TopLeft;
+ else if (pivot == new Vector2(0.5f, 1))
+ return TextContainerAnchors.Top;
+ else if (pivot == new Vector2(1f, 1))
+ return TextContainerAnchors.TopRight;
+ else if (pivot == new Vector2(0, 0.5f))
+ return TextContainerAnchors.Left;
+ else if (pivot == new Vector2(0.5f, 0.5f))
+ return TextContainerAnchors.Middle;
+ else if (pivot == new Vector2(1, 0.5f))
+ return TextContainerAnchors.Right;
+ else if (pivot == new Vector2(0, 0))
+ return TextContainerAnchors.BottomLeft;
+ else if (pivot == new Vector2(0.5f, 0))
+ return TextContainerAnchors.Bottom;
+ else if (pivot == new Vector2(1, 0))
+ return TextContainerAnchors.BottomRight;
+ else
+ return TextContainerAnchors.Custom;
+
+ }
+ }
+}
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TextContainer.cs.meta b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TextContainer.cs.meta
new file mode 100644
index 0000000..9f0812a
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TextContainer.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 32d40088a6124c578ad6b428df586e2e
+timeCreated: 1448498693
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: -110
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TextMeshPro.cs b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TextMeshPro.cs
new file mode 100644
index 0000000..f92483b
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TextMeshPro.cs
@@ -0,0 +1,551 @@
+using UnityEngine;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine.UI;
+
+namespace TMPro
+{
+
+ [DisallowMultipleComponent]
+ [RequireComponent(typeof(MeshRenderer))]
+ [RequireComponent(typeof(MeshFilter))]
+ [AddComponentMenu("Mesh/TextMeshPro - Text")]
+ [ExecuteAlways]
+ public partial class TextMeshPro : TMP_Text, ILayoutElement
+ {
+ // Public Properties and Serializable Properties
+
+ /// <summary>
+ /// Sets the Renderer's sorting Layer ID
+ /// </summary>
+ public int sortingLayerID
+ {
+ get { return m_renderer.sortingLayerID; }
+ set { m_renderer.sortingLayerID = value; }
+ }
+
+ /// <summary>
+ /// Sets the Renderer's sorting order within the assigned layer.
+ /// </summary>
+ public int sortingOrder
+ {
+ get { return m_renderer.sortingOrder; }
+ set { m_renderer.sortingOrder = value; }
+ }
+
+ /// <summary>
+ /// Determines if the size of the text container will be adjusted to fit the text object when it is first created.
+ /// </summary>
+ public override bool autoSizeTextContainer
+ {
+ get { return m_autoSizeTextContainer; }
+
+ set { if (m_autoSizeTextContainer == value) return; m_autoSizeTextContainer = value; if (m_autoSizeTextContainer) { TMP_UpdateManager.RegisterTextElementForLayoutRebuild(this); SetLayoutDirty(); } }
+ }
+
+
+ /// <summary>
+ /// Returns a reference to the Text Container
+ /// </summary>
+ [Obsolete("The TextContainer is now obsolete. Use the RectTransform instead.")]
+ public TextContainer textContainer
+ {
+ get
+ {
+ return null;
+ }
+ }
+
+
+ /// <summary>
+ /// Returns a reference to the Transform
+ /// </summary>
+ public new Transform transform
+ {
+ get
+ {
+ if (m_transform == null)
+ m_transform = GetComponent<Transform>();
+
+ return m_transform;
+ }
+ }
+
+
+ #pragma warning disable 0108
+ /// <summary>
+ /// Returns the rendered assigned to the text object.
+ /// </summary>
+ public Renderer renderer
+ {
+ get
+ {
+ if (m_renderer == null)
+ m_renderer = GetComponent<Renderer>();
+
+ return m_renderer;
+ }
+ }
+
+
+ /// <summary>
+ /// Returns the mesh assigned to the text object.
+ /// </summary>
+ public override Mesh mesh
+ {
+ get
+ {
+ if (m_mesh == null)
+ {
+ m_mesh = new Mesh();
+ m_mesh.hideFlags = HideFlags.HideAndDontSave;
+ this.meshFilter.mesh = m_mesh;
+ }
+
+ return m_mesh;
+ }
+ }
+
+ /// <summary>
+ /// Returns the Mesh Filter of the text object.
+ /// </summary>
+ public MeshFilter meshFilter
+ {
+ get
+ {
+ if (m_meshFilter == null)
+ m_meshFilter = GetComponent<MeshFilter>();
+
+ return m_meshFilter;
+ }
+ }
+
+ // MASKING RELATED PROPERTIES
+ /// <summary>
+ /// Sets the mask type
+ /// </summary>
+ public MaskingTypes maskType
+ {
+ get { return m_maskType; }
+ set { m_maskType = value; SetMask(m_maskType); }
+ }
+
+
+ /// <summary>
+ /// Function used to set the mask type and coordinates in World Space
+ /// </summary>
+ /// <param name="type"></param>
+ /// <param name="maskCoords"></param>
+ public void SetMask(MaskingTypes type, Vector4 maskCoords)
+ {
+ SetMask(type);
+
+ SetMaskCoordinates(maskCoords);
+ }
+
+ /// <summary>
+ /// Function used to set the mask type, coordinates and softness
+ /// </summary>
+ /// <param name="type"></param>
+ /// <param name="maskCoords"></param>
+ /// <param name="softnessX"></param>
+ /// <param name="softnessY"></param>
+ public void SetMask(MaskingTypes type, Vector4 maskCoords, float softnessX, float softnessY)
+ {
+ SetMask(type);
+
+ SetMaskCoordinates(maskCoords, softnessX, softnessY);
+ }
+
+
+ /// <summary>
+ /// Schedule rebuilding of the text geometry.
+ /// </summary>
+ public override void SetVerticesDirty()
+ {
+ //Debug.Log("SetVerticesDirty()");
+
+ if (m_verticesAlreadyDirty || this == null || !this.IsActive())
+ return;
+
+ TMP_UpdateManager.RegisterTextElementForGraphicRebuild(this);
+ m_verticesAlreadyDirty = true;
+ }
+
+
+ /// <summary>
+ ///
+ /// </summary>
+ public override void SetLayoutDirty()
+ {
+ m_isPreferredWidthDirty = true;
+ m_isPreferredHeightDirty = true;
+
+ if (m_layoutAlreadyDirty || this == null || !this.IsActive())
+ return;
+
+ //TMP_UpdateManager.RegisterTextElementForLayoutRebuild(this);
+ m_layoutAlreadyDirty = true;
+ //LayoutRebuilder.MarkLayoutForRebuild(this.rectTransform);
+ m_isLayoutDirty = true;
+ }
+
+
+ /// <summary>
+ /// Schedule updating of the material used by the text object.
+ /// </summary>
+ public override void SetMaterialDirty()
+ {
+ //Debug.Log("SetMaterialDirty()");
+
+ //if (!this.IsActive())
+ // return;
+
+ //m_isMaterialDirty = true;
+ UpdateMaterial();
+ //TMP_UpdateManager.RegisterTextElementForGraphicRebuild(this);
+ }
+
+
+ /// <summary>
+ ///
+ /// </summary>
+ public override void SetAllDirty()
+ {
+ m_isInputParsingRequired = true;
+
+ SetLayoutDirty();
+ SetVerticesDirty();
+ SetMaterialDirty();
+ }
+
+
+ /// <summary>
+ ///
+ /// </summary>
+ /// <param name="update"></param>
+ public override void Rebuild(CanvasUpdate update)
+ {
+ if (this == null) return;
+
+ if (update == CanvasUpdate.Prelayout)
+ {
+ if (m_autoSizeTextContainer)
+ {
+ m_rectTransform.sizeDelta = GetPreferredValues(Mathf.Infinity, Mathf.Infinity);
+ }
+ }
+ else if (update == CanvasUpdate.PreRender)
+ {
+ this.OnPreRenderObject();
+ m_verticesAlreadyDirty = false;
+ m_layoutAlreadyDirty = false;
+
+ if (!m_isMaterialDirty) return;
+
+ UpdateMaterial();
+ m_isMaterialDirty = false;
+ }
+ }
+
+
+ /// <summary>
+ ///
+ /// </summary>
+ protected override void UpdateMaterial()
+ {
+ //Debug.Log("*** UpdateMaterial() ***");
+
+ //if (!this.IsActive())
+ // return;
+
+ if (m_sharedMaterial == null)
+ return;
+
+ if (m_renderer == null) m_renderer = this.renderer;
+
+ // Only update the material if it has changed.
+ if (m_renderer.sharedMaterial.GetInstanceID() != m_sharedMaterial.GetInstanceID())
+ m_renderer.sharedMaterial = m_sharedMaterial;
+ }
+
+
+ /// <summary>
+ /// Function to be used to force recomputing of character padding when Shader / Material properties have been changed via script.
+ /// </summary>
+ public override void UpdateMeshPadding()
+ {
+ m_padding = ShaderUtilities.GetPadding(m_sharedMaterial, m_enableExtraPadding, m_isUsingBold);
+ m_isMaskingEnabled = ShaderUtilities.IsMaskingEnabled(m_sharedMaterial);
+ m_havePropertiesChanged = true;
+ checkPaddingRequired = false;
+
+ // Return if text object is not awake yet.
+ if (m_textInfo == null) return;
+
+ // Update sub text objects
+ for (int i = 1; i < m_textInfo.materialCount; i++)
+ m_subTextObjects[i].UpdateMeshPadding(m_enableExtraPadding, m_isUsingBold);
+ }
+
+
+ /// <summary>
+ /// Function to force regeneration of the mesh before its normal process time. This is useful when changes to the text object properties need to be applied immediately.
+ /// </summary>
+ public override void ForceMeshUpdate()
+ {
+ //Debug.Log("ForceMeshUpdate() called.");
+ m_havePropertiesChanged = true;
+ OnPreRenderObject();
+ }
+
+
+ /// <summary>
+ /// Function to force regeneration of the mesh before its normal process time. This is useful when changes to the text object properties need to be applied immediately.
+ /// </summary>
+ /// <param name="ignoreInactive">If set to true, the text object will be regenerated regardless of is active state.</param>
+ public override void ForceMeshUpdate(bool ignoreInactive)
+ {
+ m_havePropertiesChanged = true;
+ m_ignoreActiveState = true;
+ OnPreRenderObject();
+ }
+
+
+ /// <summary>
+ /// Function used to evaluate the length of a text string.
+ /// </summary>
+ /// <param name="text"></param>
+ /// <returns></returns>
+ public override TMP_TextInfo GetTextInfo(string text)
+ {
+ StringToCharArray(text, ref m_TextParsingBuffer);
+ SetArraySizes(m_TextParsingBuffer);
+
+ m_renderMode = TextRenderFlags.DontRender;
+
+ ComputeMarginSize();
+
+ GenerateTextMesh();
+
+ m_renderMode = TextRenderFlags.Render;
+
+ return this.textInfo;
+ }
+
+
+ /// <summary>
+ /// Function to clear the geometry of the Primary and Sub Text objects.
+ /// </summary>
+ public override void ClearMesh(bool updateMesh)
+ {
+ if (m_textInfo.meshInfo[0].mesh == null) m_textInfo.meshInfo[0].mesh = m_mesh;
+
+ m_textInfo.ClearMeshInfo(updateMesh);
+ }
+
+
+ /// <summary>
+ /// Function to force the regeneration of the text object.
+ /// </summary>
+ /// <param name="flags"> Flags to control which portions of the geometry gets uploaded.</param>
+ //public override void ForceMeshUpdate(TMP_VertexDataUpdateFlags flags) { }
+
+
+ /// <summary>
+ /// Function to update the geometry of the main and sub text objects.
+ /// </summary>
+ /// <param name="mesh"></param>
+ /// <param name="index"></param>
+ public override void UpdateGeometry(Mesh mesh, int index)
+ {
+ mesh.RecalculateBounds();
+ }
+
+
+ /// <summary>
+ /// Function to upload the updated vertex data and renderer.
+ /// </summary>
+ public override void UpdateVertexData(TMP_VertexDataUpdateFlags flags)
+ {
+ int materialCount = m_textInfo.materialCount;
+
+ for (int i = 0; i < materialCount; i++)
+ {
+ Mesh mesh;
+
+ if (i == 0)
+ mesh = m_mesh;
+ else
+ {
+ // Clear unused vertices
+ // TODO: Causes issues when sorting geometry as last vertex data attribute get wiped out.
+ //m_textInfo.meshInfo[i].ClearUnusedVertices();
+
+ mesh = m_subTextObjects[i].mesh;
+ }
+
+ //mesh.MarkDynamic();
+
+ if ((flags & TMP_VertexDataUpdateFlags.Vertices) == TMP_VertexDataUpdateFlags.Vertices)
+ mesh.vertices = m_textInfo.meshInfo[i].vertices;
+
+ if ((flags & TMP_VertexDataUpdateFlags.Uv0) == TMP_VertexDataUpdateFlags.Uv0)
+ mesh.uv = m_textInfo.meshInfo[i].uvs0;
+
+ if ((flags & TMP_VertexDataUpdateFlags.Uv2) == TMP_VertexDataUpdateFlags.Uv2)
+ mesh.uv2 = m_textInfo.meshInfo[i].uvs2;
+
+ //if ((flags & TMP_VertexDataUpdateFlags.Uv4) == TMP_VertexDataUpdateFlags.Uv4)
+ // mesh.uv4 = m_textInfo.meshInfo[i].uvs4;
+
+ if ((flags & TMP_VertexDataUpdateFlags.Colors32) == TMP_VertexDataUpdateFlags.Colors32)
+ mesh.colors32 = m_textInfo.meshInfo[i].colors32;
+
+ mesh.RecalculateBounds();
+ }
+ }
+
+
+ /// <summary>
+ /// Function to upload the updated vertex data and renderer.
+ /// </summary>
+ public override void UpdateVertexData()
+ {
+ int materialCount = m_textInfo.materialCount;
+
+ for (int i = 0; i < materialCount; i++)
+ {
+ Mesh mesh;
+
+ if (i == 0)
+ mesh = m_mesh;
+ else
+ {
+ // Clear unused vertices
+ m_textInfo.meshInfo[i].ClearUnusedVertices();
+
+ mesh = m_subTextObjects[i].mesh;
+ }
+
+
+ //mesh.MarkDynamic();
+ mesh.vertices = m_textInfo.meshInfo[i].vertices;
+ mesh.uv = m_textInfo.meshInfo[i].uvs0;
+ mesh.uv2 = m_textInfo.meshInfo[i].uvs2;
+ //mesh.uv4 = m_textInfo.meshInfo[i].uvs4;
+ mesh.colors32 = m_textInfo.meshInfo[i].colors32;
+
+ mesh.RecalculateBounds();
+ }
+ }
+
+ public void UpdateFontAsset()
+ {
+ LoadFontAsset();
+ }
+
+
+ private bool m_currentAutoSizeMode;
+
+
+ public void CalculateLayoutInputHorizontal()
+ {
+ //Debug.Log("*** CalculateLayoutInputHorizontal() ***");
+
+ if (!this.gameObject.activeInHierarchy)
+ return;
+
+ //IsRectTransformDriven = true;
+
+ m_currentAutoSizeMode = m_enableAutoSizing;
+
+ if (m_isCalculateSizeRequired || m_rectTransform.hasChanged)
+ {
+ //Debug.Log("Calculating Layout Horizontal");
+
+ //m_LayoutPhase = AutoLayoutPhase.Horizontal;
+ //m_isRebuildingLayout = true;
+
+ m_minWidth = 0;
+ m_flexibleWidth = 0;
+
+ //m_renderMode = TextRenderFlags.GetPreferredSizes; // Set Text to not Render and exit early once we have new width values.
+
+ if (m_enableAutoSizing)
+ {
+ m_fontSize = m_fontSizeMax;
+ }
+
+ // Set Margins to Infinity
+ m_marginWidth = k_LargePositiveFloat;
+ m_marginHeight = k_LargePositiveFloat;
+
+ if (m_isInputParsingRequired || m_isTextTruncated)
+ ParseInputText();
+
+ GenerateTextMesh();
+
+ m_renderMode = TextRenderFlags.Render;
+
+ //m_preferredWidth = (int)m_preferredWidth + 1f;
+
+ ComputeMarginSize();
+
+ //Debug.Log("Preferred Width: " + m_preferredWidth + " Margin Width: " + m_marginWidth + " Preferred Height: " + m_preferredHeight + " Margin Height: " + m_marginHeight + " Rendered Width: " + m_renderedWidth + " Height: " + m_renderedHeight + " RectTransform Width: " + m_rectTransform.rect);
+
+ m_isLayoutDirty = true;
+ }
+ }
+
+
+ public void CalculateLayoutInputVertical()
+ {
+ //Debug.Log("*** CalculateLayoutInputVertical() ***");
+
+ // Check if object is active
+ if (!this.gameObject.activeInHierarchy) // || IsRectTransformDriven == false)
+ return;
+
+ //IsRectTransformDriven = true;
+
+ if (m_isCalculateSizeRequired || m_rectTransform.hasChanged)
+ {
+ //Debug.Log("Calculating Layout InputVertical");
+
+ //m_LayoutPhase = AutoLayoutPhase.Vertical;
+ //m_isRebuildingLayout = true;
+
+ m_minHeight = 0;
+ m_flexibleHeight = 0;
+
+ //m_renderMode = TextRenderFlags.GetPreferredSizes;
+
+ if (m_enableAutoSizing)
+ {
+ m_currentAutoSizeMode = true;
+ m_enableAutoSizing = false;
+ }
+
+ m_marginHeight = k_LargePositiveFloat;
+
+ GenerateTextMesh();
+
+ m_enableAutoSizing = m_currentAutoSizeMode;
+
+ m_renderMode = TextRenderFlags.Render;
+
+ //m_preferredHeight = (int)m_preferredHeight + 1f;
+
+ ComputeMarginSize();
+
+ //Debug.Log("Preferred Height: " + m_preferredHeight + " Margin Height: " + m_marginHeight + " Preferred Width: " + m_preferredWidth + " Margin Width: " + m_marginWidth + " Rendered Width: " + m_renderedWidth + " Height: " + m_renderedHeight + " RectTransform Width: " + m_rectTransform.rect);
+
+ m_isLayoutDirty = true;
+ }
+
+ m_isCalculateSizeRequired = false;
+ }
+ }
+} \ No newline at end of file
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TextMeshPro.cs.meta b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TextMeshPro.cs.meta
new file mode 100644
index 0000000..42be0b9
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TextMeshPro.cs.meta
@@ -0,0 +1,39 @@
+fileFormatVersion: 2
+guid: 9541d86e2fd84c1d9990edf0852d74ab
+labels:
+- Text
+- TextMesh
+- Mesh
+- Glow
+- Shadow
+- Outline
+- Bevel
+- Pro
+- TextMeshPro
+- Kerning
+- Distance
+- Field
+- Signed
+- Font
+- Atlas
+- Creator
+- Advanced
+- Rendering
+- TrueType
+- Dynamic
+- Layout
+- Style
+- Styles
+- Fonts
+- SDF
+- Scene
+- GUI
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: -105
+ icon: {fileID: 2800000, guid: 2fd6421f253b4ef1a19526541f9ffc0c, type: 3}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TextMeshProUGUI.cs b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TextMeshProUGUI.cs
new file mode 100644
index 0000000..be57eb1
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TextMeshProUGUI.cs
@@ -0,0 +1,656 @@
+using UnityEngine;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+
+using UnityEngine.UI;
+using UnityEngine.EventSystems;
+using UnityEngine.UI.CoroutineTween;
+
+
+#pragma warning disable 0414 // Disabled a few warnings related to serialized variables not used in this script but used in the editor.
+
+namespace TMPro
+{
+
+ [DisallowMultipleComponent]
+ [RequireComponent(typeof(RectTransform))]
+ [RequireComponent(typeof(CanvasRenderer))]
+ [AddComponentMenu("UI/TextMeshPro - Text (UI)", 11)]
+ [ExecuteAlways]
+ public partial class TextMeshProUGUI : TMP_Text, ILayoutElement
+ {
+ /// <summary>
+ /// Get the material that will be used for rendering.
+ /// </summary>
+ public override Material materialForRendering
+ {
+ get { return TMP_MaterialManager.GetMaterialForRendering(this, m_sharedMaterial); }
+ }
+
+ /// <summary>
+ /// Determines if the size of the text container will be adjusted to fit the text object when it is first created.
+ /// </summary>
+ public override bool autoSizeTextContainer
+ {
+ get { return m_autoSizeTextContainer; }
+
+ set { if (m_autoSizeTextContainer == value) return; m_autoSizeTextContainer = value; if (m_autoSizeTextContainer) { CanvasUpdateRegistry.RegisterCanvasElementForLayoutRebuild(this); SetLayoutDirty(); } }
+ }
+
+
+
+ /// <summary>
+ /// Reference to the Mesh used by the text object.
+ /// </summary>
+ public override Mesh mesh
+ {
+ get { return m_mesh; }
+ }
+
+
+ /// <summary>
+ /// Reference to the CanvasRenderer used by the text object.
+ /// </summary>
+ public new CanvasRenderer canvasRenderer
+ {
+ get
+ {
+ if (m_canvasRenderer == null) m_canvasRenderer = GetComponent<CanvasRenderer>();
+
+ return m_canvasRenderer;
+ }
+ }
+
+
+ /// <summary>
+ /// Anchor dampening prevents the anchor position from being adjusted unless the positional change exceeds about 40% of the width of the underline character. This essentially stabilizes the anchor position.
+ /// </summary>
+ //public bool anchorDampening
+ //{
+ // get { return m_anchorDampening; }
+ // set { if (m_anchorDampening != value) { havePropertiesChanged = true; m_anchorDampening = value; /* ScheduleUpdate(); */ } }
+ //}
+
+
+ private bool m_isRebuildingLayout = false;
+ //private bool m_isLayoutDirty = false;
+
+
+ /// <summary>
+ /// Function called by Unity when the horizontal layout needs to be recalculated.
+ /// </summary>
+ public void CalculateLayoutInputHorizontal()
+ {
+ //Debug.Log("*** CalculateLayoutHorizontal() ***"); // at Frame: " + Time.frameCount); // called on Object ID " + GetInstanceID());
+
+ //// Check if object is active
+ if (!this.gameObject.activeInHierarchy)
+ return;
+
+ if (m_isCalculateSizeRequired || m_rectTransform.hasChanged)
+ {
+ m_preferredWidth = GetPreferredWidth();
+
+ ComputeMarginSize();
+
+ m_isLayoutDirty = true;
+ }
+ }
+
+
+ /// <summary>
+ /// Function called by Unity when the vertical layout needs to be recalculated.
+ /// </summary>
+ public void CalculateLayoutInputVertical()
+ {
+ //Debug.Log("*** CalculateLayoutInputVertical() ***"); // at Frame: " + Time.frameCount); // called on Object ID " + GetInstanceID());
+
+ //// Check if object is active
+ if (!this.gameObject.activeInHierarchy) // || IsRectTransformDriven == false)
+ return;
+
+ if (m_isCalculateSizeRequired || m_rectTransform.hasChanged)
+ {
+ m_preferredHeight = GetPreferredHeight();
+
+ ComputeMarginSize();
+
+ m_isLayoutDirty = true;
+ }
+
+ m_isCalculateSizeRequired = false;
+ }
+
+
+ public override void SetVerticesDirty()
+ {
+ if (m_verticesAlreadyDirty || this == null || !this.IsActive() || CanvasUpdateRegistry.IsRebuildingGraphics())
+ return;
+
+ m_verticesAlreadyDirty = true;
+ CanvasUpdateRegistry.RegisterCanvasElementForGraphicRebuild((ICanvasElement)this);
+
+ if (m_OnDirtyVertsCallback != null)
+ m_OnDirtyVertsCallback();
+ }
+
+
+ /// <summary>
+ ///
+ /// </summary>
+ public override void SetLayoutDirty()
+ {
+ m_isPreferredWidthDirty = true;
+ m_isPreferredHeightDirty = true;
+
+ if ( m_layoutAlreadyDirty || this == null || !this.IsActive())
+ return;
+
+ m_layoutAlreadyDirty = true;
+ LayoutRebuilder.MarkLayoutForRebuild(this.rectTransform);
+
+ m_isLayoutDirty = true;
+
+ if (m_OnDirtyLayoutCallback != null)
+ m_OnDirtyLayoutCallback();
+ }
+
+
+ /// <summary>
+ ///
+ /// </summary>
+ public override void SetMaterialDirty()
+ {
+ //Debug.Log("SetMaterialDirty()");
+
+ if (this == null || !this.IsActive() || CanvasUpdateRegistry.IsRebuildingGraphics())
+ return;
+
+ m_isMaterialDirty = true;
+ CanvasUpdateRegistry.RegisterCanvasElementForGraphicRebuild((ICanvasElement)this);
+
+ if (m_OnDirtyMaterialCallback != null)
+ m_OnDirtyMaterialCallback();
+ }
+
+
+ /// <summary>
+ ///
+ /// </summary>
+ public override void SetAllDirty()
+ {
+ m_isInputParsingRequired = true;
+
+ SetLayoutDirty();
+ SetVerticesDirty();
+ SetMaterialDirty();
+ }
+
+
+
+ /// <summary>
+ ///
+ /// </summary>
+ /// <param name="update"></param>
+ public override void Rebuild(CanvasUpdate update)
+ {
+ if (this == null) return;
+
+ if (update == CanvasUpdate.Prelayout)
+ {
+ if (m_autoSizeTextContainer)
+ {
+ m_rectTransform.sizeDelta = GetPreferredValues(Mathf.Infinity, Mathf.Infinity);
+ }
+ }
+ else if (update == CanvasUpdate.PreRender)
+ {
+ OnPreRenderCanvas();
+
+ m_verticesAlreadyDirty = false;
+ m_layoutAlreadyDirty = false;
+
+ if (!m_isMaterialDirty) return;
+
+ UpdateMaterial();
+ m_isMaterialDirty = false;
+ }
+ }
+
+
+ /// <summary>
+ /// Method to keep the pivot of the sub text objects in sync with the parent pivot.
+ /// </summary>
+ private void UpdateSubObjectPivot()
+ {
+ if (m_textInfo == null) return;
+
+ for (int i = 1; i < m_subTextObjects.Length && m_subTextObjects[i] != null; i++)
+ {
+ m_subTextObjects[i].SetPivotDirty();
+ }
+ //m_isPivotDirty = false;
+ }
+
+
+ /// <summary>
+ ///
+ /// </summary>
+ /// <param name="baseMaterial"></param>
+ /// <returns></returns>
+ public override Material GetModifiedMaterial(Material baseMaterial)
+ {
+ Material mat = baseMaterial;
+
+ if (m_ShouldRecalculateStencil)
+ {
+ m_stencilID = TMP_MaterialManager.GetStencilID(gameObject);
+ m_ShouldRecalculateStencil = false;
+ }
+
+ // Release masking material
+ //if (m_MaskMaterial != null)
+ // MaterialManager.ReleaseStencilMaterial(m_MaskMaterial);
+
+ if (m_stencilID > 0)
+ {
+ mat = TMP_MaterialManager.GetStencilMaterial(baseMaterial, m_stencilID);
+ if (m_MaskMaterial != null)
+ TMP_MaterialManager.ReleaseStencilMaterial(m_MaskMaterial);
+
+ m_MaskMaterial = mat;
+ }
+
+ return mat;
+ }
+
+
+ /// <summary>
+ ///
+ /// </summary>
+ protected override void UpdateMaterial()
+ {
+ //Debug.Log("*** UpdateMaterial() ***");
+
+ //if (!this.IsActive())
+ // return;
+
+ if (m_sharedMaterial == null) return;
+
+ if (m_canvasRenderer == null) m_canvasRenderer = this.canvasRenderer;
+
+ m_canvasRenderer.materialCount = 1;
+ m_canvasRenderer.SetMaterial(materialForRendering, 0);
+ }
+
+
+ //public override void OnRebuildRequested()
+ //{
+ // //Debug.Log("OnRebuildRequested");
+
+ // base.OnRebuildRequested();
+ //}
+
+
+
+ //public override bool Raycast(Vector2 sp, Camera eventCamera)
+ //{
+ // //Debug.Log("Raycast Event. ScreenPoint: " + sp);
+ // return base.Raycast(sp, eventCamera);
+ //}
+
+
+ // MASKING RELATED PROPERTIES
+ /// <summary>
+ /// Sets the masking offset from the bounds of the object
+ /// </summary>
+ public Vector4 maskOffset
+ {
+ get { return m_maskOffset; }
+ set { m_maskOffset = value; UpdateMask(); m_havePropertiesChanged = true; }
+ }
+
+
+ //public override Material defaultMaterial
+ //{
+ // get { Debug.Log("Default Material called."); return m_sharedMaterial; }
+ //}
+
+
+
+ //protected override void OnCanvasHierarchyChanged()
+ //{
+ // //Debug.Log("OnCanvasHierarchyChanged...");
+ //}
+
+
+ // IClippable implementation
+ /// <summary>
+ /// Method called when the state of a parent changes.
+ /// </summary>
+ public override void RecalculateClipping()
+ {
+ //Debug.Log("***** RecalculateClipping() *****");
+
+ base.RecalculateClipping();
+ }
+
+ // IMaskable Implementation
+ /// <summary>
+ /// Method called when Stencil Mask needs to be updated on this element and parents.
+ /// </summary>
+ public override void RecalculateMasking()
+ {
+ //Debug.Log("***** RecalculateMasking() *****");
+
+ this.m_ShouldRecalculateStencil = true;
+ SetMaterialDirty();
+ }
+
+ /// <summary>
+ /// Override of the Cull function to provide for the ability to override the culling of the text object.
+ /// </summary>
+ /// <param name="clipRect"></param>
+ /// <param name="validRect"></param>
+ public override void Cull(Rect clipRect, bool validRect)
+ {
+ if (m_ignoreRectMaskCulling) return;
+
+ base.Cull(clipRect, validRect);
+ }
+
+
+ //protected override void UpdateGeometry()
+ //{
+ // //Debug.Log("UpdateGeometry");
+ // //base.UpdateGeometry();
+ //}
+
+
+ //protected override void UpdateMaterial()
+ //{
+ // //Debug.Log("UpdateMaterial called.");
+ //// base.UpdateMaterial();
+ //}
+
+
+ /*
+ /// <summary>
+ /// Sets the mask type
+ /// </summary>
+ public MaskingTypes mask
+ {
+ get { return m_mask; }
+ set { m_mask = value; havePropertiesChanged = true; isMaskUpdateRequired = true; }
+ }
+
+ /// <summary>
+ /// Set the masking offset mode (as percentage or pixels)
+ /// </summary>
+ public MaskingOffsetMode maskOffsetMode
+ {
+ get { return m_maskOffsetMode; }
+ set { m_maskOffsetMode = value; havePropertiesChanged = true; isMaskUpdateRequired = true; }
+ }
+ */
+
+
+
+ /*
+ /// <summary>
+ /// Sets the softness of the mask
+ /// </summary>
+ public Vector2 maskSoftness
+ {
+ get { return m_maskSoftness; }
+ set { m_maskSoftness = value; havePropertiesChanged = true; isMaskUpdateRequired = true; }
+ }
+
+ /// <summary>
+ /// Allows to move / offset the mesh vertices by a set amount
+ /// </summary>
+ public Vector2 vertexOffset
+ {
+ get { return m_vertexOffset; }
+ set { m_vertexOffset = value; havePropertiesChanged = true; isMaskUpdateRequired = true; }
+ }
+ */
+
+
+ /// <summary>
+ /// Function to be used to force recomputing of character padding when Shader / Material properties have been changed via script.
+ /// </summary>
+ public override void UpdateMeshPadding()
+ {
+ m_padding = ShaderUtilities.GetPadding(m_sharedMaterial, m_enableExtraPadding, m_isUsingBold);
+ m_isMaskingEnabled = ShaderUtilities.IsMaskingEnabled(m_sharedMaterial);
+ m_havePropertiesChanged = true;
+ checkPaddingRequired = false;
+
+ // Return if text object is not awake yet.
+ if (m_textInfo == null) return;
+
+ // Update sub text objects
+ for (int i = 1; i < m_textInfo.materialCount; i++)
+ m_subTextObjects[i].UpdateMeshPadding(m_enableExtraPadding, m_isUsingBold);
+ }
+
+
+ /// <summary>
+ /// Tweens the CanvasRenderer color associated with this Graphic.
+ /// </summary>
+ /// <param name="targetColor">Target color.</param>
+ /// <param name="duration">Tween duration.</param>
+ /// <param name="ignoreTimeScale">Should ignore Time.scale?</param>
+ /// <param name="useAlpha">Should also Tween the alpha channel?</param>
+ protected override void InternalCrossFadeColor(Color targetColor, float duration, bool ignoreTimeScale, bool useAlpha)
+ {
+ int materialCount = m_textInfo.materialCount;
+
+ for (int i = 1; i < materialCount; i++)
+ {
+ m_subTextObjects[i].CrossFadeColor(targetColor, duration, ignoreTimeScale, useAlpha);
+ }
+ }
+
+
+ /// <summary>
+ /// Tweens the alpha of the CanvasRenderer color associated with this Graphic.
+ /// </summary>
+ /// <param name="alpha">Target alpha.</param>
+ /// <param name="duration">Duration of the tween in seconds.</param>
+ /// <param name="ignoreTimeScale">Should ignore Time.scale?</param>
+ protected override void InternalCrossFadeAlpha(float alpha, float duration, bool ignoreTimeScale)
+ {
+ int materialCount = m_textInfo.materialCount;
+
+ for (int i = 1; i < materialCount; i++)
+ {
+ m_subTextObjects[i].CrossFadeAlpha(alpha, duration, ignoreTimeScale);
+ }
+ }
+
+
+ /// <summary>
+ /// Function to force regeneration of the mesh before its normal process time. This is useful when changes to the text object properties need to be applied immediately.
+ /// </summary>
+ public override void ForceMeshUpdate()
+ {
+ //if (m_isEnabled == false) this.OnEnable();
+
+ m_havePropertiesChanged = true;
+ OnPreRenderCanvas();
+ }
+
+
+ /// <summary>
+ /// Function to force regeneration of the mesh before its normal process time. This is useful when changes to the text object properties need to be applied immediately.
+ /// </summary>
+ /// <param name="ignoreInactive">If set to true, the text object will be regenerated regardless of is active state.</param>
+ public override void ForceMeshUpdate(bool ignoreInactive)
+ {
+ m_havePropertiesChanged = true;
+ m_ignoreActiveState = true;
+ OnPreRenderCanvas();
+ }
+
+
+ /// <summary>
+ /// Function used to evaluate the length of a text string.
+ /// </summary>
+ /// <param name="text"></param>
+ /// <returns></returns>
+ public override TMP_TextInfo GetTextInfo(string text)
+ {
+ StringToCharArray(text, ref m_TextParsingBuffer);
+ SetArraySizes(m_TextParsingBuffer);
+
+ m_renderMode = TextRenderFlags.DontRender;
+
+ ComputeMarginSize();
+
+ // Need to make sure we have a valid reference to a Canvas.
+ if (m_canvas == null) m_canvas = this.canvas;
+
+ GenerateTextMesh();
+
+ m_renderMode = TextRenderFlags.Render;
+
+ return this.textInfo;
+ }
+
+ /// <summary>
+ /// Function to clear the geometry of the Primary and Sub Text objects.
+ /// </summary>
+ public override void ClearMesh()
+ {
+ m_canvasRenderer.SetMesh(null);
+
+ for (int i = 1; i < m_subTextObjects.Length && m_subTextObjects[i] != null; i++)
+ m_subTextObjects[i].canvasRenderer.SetMesh(null);
+
+ //if (m_linkedTextComponent != null)
+ // m_linkedTextComponent.ClearMesh();
+ }
+
+
+ /// <summary>
+ /// Function to force the regeneration of the text object.
+ /// </summary>
+ /// <param name="flags"> Flags to control which portions of the geometry gets uploaded.</param>
+ //public override void ForceMeshUpdate(TMP_VertexDataUpdateFlags flags) { }
+
+
+ /// <summary>
+ /// Function to update the geometry of the main and sub text objects.
+ /// </summary>
+ /// <param name="mesh"></param>
+ /// <param name="index"></param>
+ public override void UpdateGeometry(Mesh mesh, int index)
+ {
+ mesh.RecalculateBounds();
+
+ if (index == 0)
+ {
+ m_canvasRenderer.SetMesh(mesh);
+ }
+ else
+ {
+ m_subTextObjects[index].canvasRenderer.SetMesh(mesh);
+ }
+ }
+
+
+ /// <summary>
+ /// Function to upload the updated vertex data and renderer.
+ /// </summary>
+ public override void UpdateVertexData(TMP_VertexDataUpdateFlags flags)
+ {
+ int materialCount = m_textInfo.materialCount;
+
+ for (int i = 0; i < materialCount; i++)
+ {
+ Mesh mesh;
+
+ if (i == 0)
+ mesh = m_mesh;
+ else
+ {
+ // Clear unused vertices
+ // TODO: Causes issues when sorting geometry as last vertex data attribute get wiped out.
+ //m_textInfo.meshInfo[i].ClearUnusedVertices();
+
+ mesh = m_subTextObjects[i].mesh;
+ }
+
+ if ((flags & TMP_VertexDataUpdateFlags.Vertices) == TMP_VertexDataUpdateFlags.Vertices)
+ mesh.vertices = m_textInfo.meshInfo[i].vertices;
+
+ if ((flags & TMP_VertexDataUpdateFlags.Uv0) == TMP_VertexDataUpdateFlags.Uv0)
+ mesh.uv = m_textInfo.meshInfo[i].uvs0;
+
+ if ((flags & TMP_VertexDataUpdateFlags.Uv2) == TMP_VertexDataUpdateFlags.Uv2)
+ mesh.uv2 = m_textInfo.meshInfo[i].uvs2;
+
+ //if ((flags & TMP_VertexDataUpdateFlags.Uv4) == TMP_VertexDataUpdateFlags.Uv4)
+ // mesh.uv4 = m_textInfo.meshInfo[i].uvs4;
+
+ if ((flags & TMP_VertexDataUpdateFlags.Colors32) == TMP_VertexDataUpdateFlags.Colors32)
+ mesh.colors32 = m_textInfo.meshInfo[i].colors32;
+
+ mesh.RecalculateBounds();
+
+ if (i == 0)
+ m_canvasRenderer.SetMesh(mesh);
+ else
+ m_subTextObjects[i].canvasRenderer.SetMesh(mesh);
+ }
+ }
+
+
+ /// <summary>
+ /// Function to upload the updated vertex data and renderer.
+ /// </summary>
+ public override void UpdateVertexData()
+ {
+ int materialCount = m_textInfo.materialCount;
+
+ for (int i = 0; i < materialCount; i++)
+ {
+ Mesh mesh;
+
+ if (i == 0)
+ mesh = m_mesh;
+ else
+ {
+ // Clear unused vertices
+ m_textInfo.meshInfo[i].ClearUnusedVertices();
+
+ mesh = m_subTextObjects[i].mesh;
+ }
+
+ //mesh.MarkDynamic();
+ mesh.vertices = m_textInfo.meshInfo[i].vertices;
+ mesh.uv = m_textInfo.meshInfo[i].uvs0;
+ mesh.uv2 = m_textInfo.meshInfo[i].uvs2;
+ //mesh.uv4 = m_textInfo.meshInfo[i].uvs4;
+ mesh.colors32 = m_textInfo.meshInfo[i].colors32;
+
+ mesh.RecalculateBounds();
+
+ if (i == 0)
+ m_canvasRenderer.SetMesh(mesh);
+ else
+ m_subTextObjects[i].canvasRenderer.SetMesh(mesh);
+ }
+ }
+
+
+ public void UpdateFontAsset()
+ {
+ LoadFontAsset();
+ }
+
+ }
+} \ No newline at end of file
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TextMeshProUGUI.cs.meta b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TextMeshProUGUI.cs.meta
new file mode 100644
index 0000000..13eea85
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/TextMeshProUGUI.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: f4688fdb7df04437aeb418b961361dc5
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: -100
+ icon: {fileID: 2800000, guid: 2fd6421f253b4ef1a19526541f9ffc0c, type: 3}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/Unity.TextMeshPro.asmdef b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/Unity.TextMeshPro.asmdef
new file mode 100644
index 0000000..6f5618c
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/Unity.TextMeshPro.asmdef
@@ -0,0 +1,13 @@
+{
+ "name": "Unity.TextMeshPro",
+ "references": [ "Unity.ugui" ],
+ "optionalUnityReferences": [],
+ "includePlatforms": [],
+ "excludePlatforms": [],
+ "allowUnsafeCode": false,
+ "overrideReferences": false,
+ "precompiledReferences": [],
+ "autoReferenced": true,
+ "defineConstraints": [],
+ "versionDefines": []
+} \ No newline at end of file
diff --git a/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/Unity.TextMeshPro.asmdef.meta b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/Unity.TextMeshPro.asmdef.meta
new file mode 100644
index 0000000..b0a09e5
--- /dev/null
+++ b/Library/PackageCache/com.unity.textmeshpro@2.0.1/Scripts/Runtime/Unity.TextMeshPro.asmdef.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 6055be8ebefd69e48b49212b09b47b2f
+AssemblyDefinitionImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant: