diff --git a/CHANGELOG.md b/CHANGELOG.md index c2c00e1..0fec6ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,34 @@ # Changelog These are the release notes for the TextMesh Pro UPM package which was first introduced with Unity 2018.1. Please see the following link for the Release Notes for prior versions of TextMesh Pro. http://digitalnativestudios.com/forum/index.php?topic=1363.0 +## [1.5.0-preview.10] - 2020-04-21 +## [2.1.0-preview.10] +## [3.0.0-preview.10] +### Changes +- Revised caching of Preferred Width and Height to further reduce the amount of time it has to be recomputed when using a complex structure of Layout components. +- Fixed potential issue when using Text Overflow Ellipsis and Truncate modes when the text contains characters using superscript, subscript or using the <voffset> tag. +- Revised culling of text objects when using a RectMask2D where the bounds of the text geometry instead of the RectTransform define the culling rect. +- Added HDR support to material preset colors. +- Fixed various formatting issues in this ChangeLog. +- Added the ability to define a unicode value for a missing sprite character in the TMP Settings. +- Added support for displaying a missing sprite character when the requested sprite character is not present in the sprite asset or potential fallback(s). This new functionality is only available when trying to reference a sprite by name. +- Sprite Characters will now have a default Unicode value of 0xFFFE (Private NonCharacter) instead of a Unicode value of 0x0 (default unicode value for missing character). +- Using the sprite asset context menu option "Update Sprite Asset" will now remap sprite characters with unicode value of 0x0 to 0xFFFE in addition to its currently functionality. +- Updating TMP Essential Resources via the "Window - TextMeshPro - Import TMP Essential Resources" menu option will no longer override existing TMP Settings. +- Minor optimization where SDF Scale on some text objects could be unnecessarily updated due to floating point rounding errors in their lossy scale. Case #1230799 +- Fixed minor issue where text objects created before importing the required TMP Essential Resources would have no default text. +- Improvements to line breaking for CJK and mixed Latin and CJK characters. See the following [forum post](https://forum.unity.com/threads/tmp-bug.852733/#post-5688274) for more details. +- Fixed potential NullReferenceException that could occur in the TMP InputField on some platforms if the InputSystem reference is null. Case #1232433 +- Added small padding to bitmap character geometry to prevent potential clipping. +- Added optimization to ignore very small RectTransform pivot changes that are usually the result of rounding errors when using Layout Components. Case #1237700 +- Sorting Layer ID and Sorting Order properties located in the Extra Settings of <TextMeshPro> text objects will now serialized when creating Editor Presets. Case #1215750 +- TextMeshProUGUI sub text objects will now be set as first sibling of their parent to prevent them from being rendered over other non text object child in the scene hierarchy. +- Fixed text objects becoming visible when set to empty or null as a result of a scale change. Case #1238408 +- Fixed useMaxVisibleDescender property now getting set properly via scripting. Case #1218526 +- Fixed SortingLayerID and SortingOrder not getting set correctly when multiple <TextMeshPro> objects are selected. Case #1171272 +- Fixed default settings getting applied to disabled text objects in the scene hierarchy whose text property was set to null. Case #1151621 +- Fixed mouse cursor flickering when hovering the Text Input Box of a text prefab with RTL enabled. Case #1206395 + ## [1.5.0-preview.8] - 2020-03-14 ## [2.1.0-preview.8] ## [3.0.0-preview.8] @@ -12,7 +40,7 @@ These are the release notes for the TextMesh Pro UPM package which was first int - Improved search for potential missing character to include the primary font asset and potential fallbacks when the current font asset is not the primary. - Ignorable / Synthesized characters in font assets will only be created if they do not exist in the source font file. - Trying to use Text Overflow Ellipsis mode when no Ellipsis character is available in the primary font asset or potential fallbacks will now issue a warning and switch Text Overflow mode to Truncate. -- Added <color=lightblue> and <color=grey> to pre-defined rich text tag colors. +- Added <color=lightblue> and <color=grey> to pre-defined rich text tag colors. - Fixed compatibility issue when using TexturePacker - JSON (Array) mode and the TMP Sprite Asset Importer to create SpriteAssets. - Simple fix to prevent the underline rich text tag becoming visible in the TMP Input Field when in IME composition mode with Rich Text disabled on the TMP Input Field. This is a temporary fix until a more robust and flexible solution is implemented. Case #1219969 - Sub Text Objects which are created when the text requires the use of a fallback font asset or sprite asset will now use HideFlags.DontSave to prevent them from being save with Prefabs as they are created on demand. @@ -24,10 +52,10 @@ These are the release notes for the TextMesh Pro UPM package which was first int ### Changes - Reverted recent change to the TMP_SubMeshUI OnDisable() function that could result in a Missing Reference Exception in the GraphicRaycaster.cs script. See the following [forum post](https://forum.unity.com/threads/version-1-5-0-2-1-0-preview-5-now-available-for-testing.753587/page-2#post-5523412). - Fixed glyph drawing issue in the Font Asset Inspector Glyph Adjustment Table when Multi Atlas Texture is enabled and the glyph is not located in the main atlas texture or at atlasTextures[0]. -- Added support for <ZWSP> tag which is internally replaced by a zero width space or \u200B. -- Improved line breaking handling when using <NBSP> and / or <NOBR> tags where instead of breaking these line segments per character, they will break at any possible soft breaking space when these segments exceed the width of the text container. +- Added support for <ZWSP> tag which is internally replaced by a zero width space or \u200B. +- Improved line breaking handling when using <NBSP> and / or <NOBR> tags where instead of breaking these line segments per character, they will break at any possible soft breaking space when these segments exceed the width of the text container. - Improved PreferredHeight calculations and handling when using Text Auto Size. -- Fixed incorrect color being applied to the underline or strikethrough line segments when using and / or tags along with a tag while at the same time applying an Underline or Strikethrough font style on the whole text object. +- Fixed incorrect color being applied to the underline or strikethrough line segments when using <u> and / or <s> tags along with a tag while at the same time applying an Underline or Strikethrough font style on the whole text object. - Fixed SDF Scale not getting updated when using SetText() with StringBuilder when the lossyScale of the text object changes. Case #1216007 - Added Non Breaking Space \u00A0 and Soft Hyphen \u00AD to list of synthesized characters in the event they are not present in the source font file. - Fixed stack overflow issue when using TMP_FontAsset.HasCharacter and TMP_FontAsset.HasCharacters function on font assets that have circular fallback references. Case #1222574 @@ -106,7 +134,7 @@ These are the release notes for the TextMesh Pro UPM package which was first int - Added the ability to control culling modes for the TMP Shaders. This new option is available in the Debug section of the Material Inspector. New feature requires updating the TMP Essential Resources. See the following post https://forum.unity.com/threads/not-see-textmeshpro-rendering-from-the-back.767510/#post-5112461. - Fixed Material Inspector issue when toggling the Record button in the Animation window. Case #1174960 - Improved Line Breaking handling for CJK. This also addresses a few reported issues. Case #1171603 -- Added support for <NBSP> tag which is internally replaced by a non-breaking space or \u00A0. +- Added support for <NBSP> tag which is internally replaced by a non-breaking space or \u00A0. - Improved performance when retrieving glyph adjustment records when using dynamic font assets. - Fixed potential Null Reference Exception in the Editor when assigning new font asset to disabled game object when no previous font asset was assigned. @@ -125,9 +153,9 @@ These are the release notes for the TextMesh Pro UPM package which was first int - Creating Material Presets via the Material Context menu with multi selection will now work as expected and assign the newly created material preset to all selected text objects. - Fixed minor issue when changing Material Preset in prefab isolation mode with multiple text objects selected where the new material preset would not be assigned to disabled text objects. - Revised Character, Word, Line and Paragraph spacing adjustments to be in font units (em) where a value of 1 represents 1/100 em. -- Added TMP_Text.onFontAssetRequest and TMP_Text.onSpriteAssetRequest events to allow users to implement custom asset loading when using the <font="Font Asset Name"> and <sprite="Sprite Asset Name"> tags. +- Added TMP_Text.onFontAssetRequest and TMP_Text.onSpriteAssetRequest events to allow users to implement custom asset loading when using the <font="Font Asset Name"> and <sprite="Sprite Asset Name"> tags. - Additional Shader Channels on the Canvas will be set to TexCoord1, Normal and Tangents or Mixed when using TMP Surface Shaders. Otherwise it will be set to TexCoord1 only. Case #1100696 -- Added new attribute to the <mark> tag to allow users to define a padding value for the mark / highlight region. Example: <mark color=#FFFF0080 padding="1.0,1.0,0.0,0.0"> where padding="Left, Right, Top, Bottom". +- Added new attribute to the <mark> tag to allow users to define a padding value for the mark / highlight region. Example: <mark color=#FFFF0080 padding="1.0,1.0,0.0,0.0"> where padding="Left, Right, Top, Bottom". - Fixed an issue which could result in out of range exception when referencing sprites contained in fallback sprite assets using unicode values. - Fixed an issue in the Font Asset Creator where the source font file property of the newly created font asset was not getting set. - Added .blend files to exclusion asset scan list of the Project GUID Remapping tool. @@ -147,11 +175,11 @@ These are the release notes for the TextMesh Pro UPM package which was first int = Fixed text alignment issue with TMP Input Field when using Center alignment on the underlying text component. - Setting ContentType.Custom on the TMP Input Field will no longer hide the Soft Keyboard. The Soft Keyboard can now be control independently via the shouldHideSoftKeyboard property. - Added new Font Asset Context Menu option "Force Upgrade To Version 1.1.0" for convenience purposes in case a font asset didn't get upgraded automatically when migrating from version 1.3.0 to 1.4.x or 2.0.x. -- The <gradient> tag now as an optional attribute "tint=0" or "tint=1" controlling whether or not the gradient will be affect by vertex color. The alpha of the gradient will continue to be affected by the vertex color alpha. -- Added new angle=x attribute to the <i> tag where the value of x define the italic slant angle. +- The <gradient> tag now as an optional attribute "tint=0" or "tint=1" controlling whether or not the gradient will be affect by vertex color. The alpha of the gradient will continue to be affected by the vertex color alpha. +- Added new angle=x attribute to the <i> tag where the value of x define the italic slant angle. - Since the legacy TextContainer used by TMP has been deprecated, it was removed from the Layout Context Menu options. - Improved character positioning when using italic text where large angle / slant would potentially result in uneven spacing between normal and italic blocks of text. -- Fixed an issue where <mspace> and <cspace> tags would not be handled correctly in conjunction with word wrapping. +- Fixed an issue where <mspace> and <cspace> tags would not be handled correctly in conjunction with word wrapping. - Fixed issue in the TMP_Dropdown.cs that was affecting navigation. Case 1162600. See https://forum.unity.com/threads/huge-bug-missing-a-code-line-since-1-4-0.693421/ - Fixed an issue related to kerning where the glyph adjustment values did not account for the upsampling of the legacy SDF modes like SDF8 / SDF16 and SDF32. - Made the TMP_Text.text property virtual. @@ -205,9 +233,9 @@ These are the release notes for the TextMesh Pro UPM package which was first int - Added new property in the Dynamic Font System section to determine if OpenType Font Features will be retrieved from source font files at runtime as new characters are added to font assets. Glyph Adjustment Data (Kerning) is the only feature currently supported. - Fix an issue where font assets created at runtime were not getting their asset version number set to "1.1.0". - Improved parsing of the text file used in the Font Asset Creator and "Characters from File" option to handle UTF16 "\u" and UTF32 "\U" escape character sequences. -- Fixed a Null Reference Error (NRE) that could occur when using the <font> tag with an invalid font name followed by the <sprite> tag. +- Fixed a Null Reference Error (NRE) that could occur when using the <font> tag with an invalid font name followed by the <sprite> tag. - The Glyph Adjustment Table presentation and internal data structure has been changed to facilitate the future addition of OpenType font features. See https://forum.unity.com/threads/version-1-4-0-preview-with-dynamic-sdf-for-unity-2018-3-now-available.622420/#post-4206595 for more details. -- Fixed an issue with the <rotate> tag incorrectly affecting character spacing. +- Fixed an issue with the <rotate> tag incorrectly affecting character spacing. ## [1.4.0-preview.1] - 2019-01-30 ### Changes @@ -217,7 +245,7 @@ These are the release notes for the TextMesh Pro UPM package which was first int - Added public setter to the TMP Settings' missingGlyphCharacter to allow changing which character will be used for missing characters via scripting. - Fixed a potential Null Reference Exception related to loading the Default Style Sheet. - Added compiler conditional to TMP_UpdateManager.cs to address changes to SRP. -- Improved the <margin> tag to make it possible to define both left and right margin values. Example: <margin left=10% right=10px>. +- Improved the <margin> tag to make it possible to define both left and right margin values. Example: <margin left=10% right=10px>. - Added new menu option to allow the quick creation of a UI Button using TMP. New menu option is located in Create - UI - Button (TextMeshPro). - Renamed TMP related create menu options. - Fixed TMP object creation handling when using Prefab isolation mode. Case #1077392 @@ -289,11 +317,11 @@ These are the release notes for the TextMesh Pro UPM package which was first int - Non-Breaking Space character (0xA0) will now be excluded from word spacing adjustments when using Justified or Flush text alignment. - Improved handling of Underline, Strikethrough and Mark tag with regards to vertex color and Color tag alpha. - Improved TMP_FontAsset.HasCharacter(char character, bool searchFallbacks) to include a recursive search of fallbacks as well as TMP Settings fallback list and default font asset. -- The <gradient> tag will now also apply to sprites provided the sprite tint attribute is set to a value of 1. Ex. <sprite="Sprite Asset" index=0 tint=1>. +- The <gradient> tag will now also apply to sprites provided the sprite tint attribute is set to a value of 1. Ex. <sprite="Sprite Asset" index=0 tint=1>. - Updated Font Asset Creator Plugin to allow for cancellation of the font asset generation process. - Added callback to support the Scriptable Render Pipeline (SRP) with the normal TextMeshPro component. - Improved handling of some non-breaking space characters which should not be ignored at the end of a line. -- Sprite Asset fallbacks will now be searched when using the <sprite> tag and referencing a sprite by Unicode or by Name. +- Sprite Asset fallbacks will now be searched when using the <sprite> tag and referencing a sprite by Unicode or by Name. - Updated EmojiOne samples from https://www.emojione.com/ and added attribution. - Removed the 32bit versions of the TMP Plugins used by the Font Asset Creator since the Unity Editor is now only available as 64bit. - The isTextTruncated property is now serialized. diff --git a/Package Resources/TMP Essential Resources.unitypackage b/Package Resources/TMP Essential Resources.unitypackage index 018af48..aa27eb5 100644 Binary files a/Package Resources/TMP Essential Resources.unitypackage and b/Package Resources/TMP Essential Resources.unitypackage differ diff --git a/Package Resources/TMP Examples & Extras.unitypackage b/Package Resources/TMP Examples & Extras.unitypackage index ea4bda2..ea3a62b 100644 Binary files a/Package Resources/TMP Examples & Extras.unitypackage and b/Package Resources/TMP Examples & Extras.unitypackage differ diff --git a/Scripts/Editor/GlyphMetricsPropertyDrawer.cs b/Scripts/Editor/GlyphMetricsPropertyDrawer.cs index 1e1f4d1..bcd25a6 100644 --- a/Scripts/Editor/GlyphMetricsPropertyDrawer.cs +++ b/Scripts/Editor/GlyphMetricsPropertyDrawer.cs @@ -21,10 +21,10 @@ public override void OnGUI(Rect position, SerializedProperty property, GUIConten // We get Rect since a valid position may not be provided by the caller. Rect rect = new Rect(position.x, position.y, position.width, 49); - EditorGUI.LabelField(rect, new GUIContent("Glyph Metrics")); + EditorGUI.LabelField(new Rect(rect.x, rect.y - 2.5f, rect.width, 18), new GUIContent("Glyph Metrics")); - EditorGUIUtility.labelWidth = 30f; - EditorGUIUtility.fieldWidth = 10f; + EditorGUIUtility.labelWidth = 50f; + EditorGUIUtility.fieldWidth = 15f; //GUI.enabled = false; float width = (rect.width - 75f) / 2; diff --git a/Scripts/Editor/GlyphRectPropertyDrawer.cs b/Scripts/Editor/GlyphRectPropertyDrawer.cs index 87ecf0c..c9be74e 100644 --- a/Scripts/Editor/GlyphRectPropertyDrawer.cs +++ b/Scripts/Editor/GlyphRectPropertyDrawer.cs @@ -21,10 +21,10 @@ public override void OnGUI(Rect position, SerializedProperty property, GUIConten // We get Rect since a valid position may not be provided by the caller. Rect rect = new Rect(position.x, position.y, position.width, 49); - EditorGUI.LabelField(rect, new GUIContent("Glyph Rect")); + EditorGUI.LabelField(new Rect(rect.x, rect.y - 2.5f, rect.width, 18), new GUIContent("Glyph Rect")); - EditorGUIUtility.labelWidth = 30f; - EditorGUIUtility.fieldWidth = 10f; + EditorGUIUtility.labelWidth = 50f; + EditorGUIUtility.fieldWidth = 20f; //GUI.enabled = false; float width = (rect.width - 75f) / 4; diff --git a/Scripts/Editor/TMP_BaseEditorPanel.cs b/Scripts/Editor/TMP_BaseEditorPanel.cs index 0c420c7..9987c5d 100644 --- a/Scripts/Editor/TMP_BaseEditorPanel.cs +++ b/Scripts/Editor/TMP_BaseEditorPanel.cs @@ -30,20 +30,20 @@ public abstract class TMP_BaseEditorPanel : Editor static readonly GUIContent k_LowercaseLabel = new GUIContent("ab", "Lowercase"); static readonly GUIContent k_UppercaseLabel = new GUIContent("AB", "Uppercase"); static readonly GUIContent k_SmallcapsLabel = new GUIContent("SC", "Smallcaps"); - + static readonly GUIContent k_ColorModeLabel = new GUIContent("Color Mode", "The type of gradient to use."); static readonly GUIContent k_BaseColorLabel = new GUIContent("Vertex Color", "The base color of the text vertices."); static readonly GUIContent k_ColorPresetLabel = new GUIContent("Color Preset", "A Color Preset which override the local color settings."); static readonly GUIContent k_ColorGradientLabel = new GUIContent("Color Gradient", "The gradient color applied over the Vertex Color. Can be locally set or driven by a Gradient Asset."); static readonly GUIContent k_CorenerColorsLabel = new GUIContent("Colors", "The color composition of the gradient."); static readonly GUIContent k_OverrideTagsLabel = new GUIContent("Override Tags", "Whether the color settings override the tag."); - + static readonly GUIContent k_SpacingOptionsLabel = new GUIContent("Spacing Options (em)", "Spacing adjustments between different elements of the text. Values are in font units where a value of 1 equals 1/100em."); static readonly GUIContent k_CharacterSpacingLabel = new GUIContent("Character"); static readonly GUIContent k_WordSpacingLabel = new GUIContent("Word"); static readonly GUIContent k_LineSpacingLabel = new GUIContent("Line"); static readonly GUIContent k_ParagraphSpacingLabel = new GUIContent("Paragraph"); - + static readonly GUIContent k_AlignmentLabel = new GUIContent("Alignment", "Horizontal and vertical aligment of the text within its container."); static readonly GUIContent k_WrapMixLabel = new GUIContent("Wrap Mix (W <-> C)", "How much to favor words versus characters when distributing the text."); @@ -66,7 +66,7 @@ public abstract class TMP_BaseEditorPanel : Editor static readonly GUIContent k_KerningLabel = new GUIContent("Kerning", "Enables character specific spacing between pairs of characters."); static readonly GUIContent k_PaddingLabel = new GUIContent("Extra Padding", "Adds some padding between the characters and the edge of the text mesh. Can reduce graphical errors when displaying small text."); - + static readonly GUIContent k_LeftLabel = new GUIContent("Left"); static readonly GUIContent k_TopLabel = new GUIContent("Top"); static readonly GUIContent k_RightLabel = new GUIContent("Right"); @@ -84,14 +84,14 @@ protected struct Foldout public static bool extraSettings = false; public static bool materialInspector = true; } - + protected static int s_EventId; public int selAlignGridA; public int selAlignGridB; - + protected SerializedProperty m_TextProp; - + protected SerializedProperty m_IsRightToLeftProp; protected string m_RtlText; @@ -109,7 +109,7 @@ protected struct Foldout protected int m_StyleSelectionIndex; protected SerializedProperty m_FontStyleProp; - + protected SerializedProperty m_FontColorProp; protected SerializedProperty m_EnableVertexGradientProp; protected SerializedProperty m_FontColorGradientProp; @@ -122,7 +122,7 @@ protected struct Foldout protected SerializedProperty m_AutoSizingProp; protected SerializedProperty m_FontSizeMinProp; protected SerializedProperty m_FontSizeMaxProp; - + protected SerializedProperty m_LineSpacingMaxProp; protected SerializedProperty m_CharWidthMaxAdjProp; @@ -174,12 +174,12 @@ protected struct Foldout protected TMP_Text m_TextComponent; protected TMP_Text m_PreviousLinkedTextComponent; protected RectTransform m_RectTransform; - + protected Material m_TargetMaterial; - + protected Vector3[] m_RectCorners = new Vector3[4]; protected Vector3[] m_HandlePoints = new Vector3[4]; - + protected virtual void OnEnable() { m_TextProp = serializedObject.FindProperty("m_text"); @@ -195,7 +195,7 @@ protected virtual void OnEnable() m_AutoSizingProp = serializedObject.FindProperty("m_enableAutoSizing"); m_FontSizeMinProp = serializedObject.FindProperty("m_fontSizeMin"); m_FontSizeMaxProp = serializedObject.FindProperty("m_fontSizeMax"); - + m_LineSpacingMaxProp = serializedObject.FindProperty("m_lineSpacingMax"); m_CharWidthMaxAdjProp = serializedObject.FindProperty("m_charWidthMaxAdj"); @@ -227,13 +227,13 @@ protected virtual void OnEnable() m_ParentLinkedTextComponentProp = serializedObject.FindProperty("parentLinkedComponent"); m_EnableKerningProp = serializedObject.FindProperty("m_enableKerning"); - + m_EnableExtraPaddingProp = serializedObject.FindProperty("m_enableExtraPadding"); m_IsRichTextProp = serializedObject.FindProperty("m_isRichText"); m_CheckPaddingRequiredProp = serializedObject.FindProperty("checkPaddingRequired"); m_EnableEscapeCharacterParsingProp = serializedObject.FindProperty("m_parseCtrlCharacters"); m_UseMaxVisibleDescenderProp = serializedObject.FindProperty("m_useMaxVisibleDescender"); - + m_GeometrySortingOrderProp = serializedObject.FindProperty("m_geometrySortingOrder"); m_IsTextObjectScaleStaticProp = serializedObject.FindProperty("m_IsTextObjectScaleStatic"); @@ -325,14 +325,14 @@ public void OnSceneGUI() m_RectTransform.GetWorldCorners(m_RectCorners); Vector4 marginOffset = m_TextComponent.margin; Vector3 lossyScale = m_RectTransform.lossyScale; - + m_HandlePoints[0] = m_RectCorners[0] + m_RectTransform.TransformDirection(new Vector3(marginOffset.x * lossyScale.x, marginOffset.w * lossyScale.y, 0)); m_HandlePoints[1] = m_RectCorners[1] + m_RectTransform.TransformDirection(new Vector3(marginOffset.x * lossyScale.x, -marginOffset.y * lossyScale.y, 0)); m_HandlePoints[2] = m_RectCorners[2] + m_RectTransform.TransformDirection(new Vector3(-marginOffset.z * lossyScale.x, -marginOffset.y * lossyScale.y, 0)); m_HandlePoints[3] = m_RectCorners[3] + m_RectTransform.TransformDirection(new Vector3(-marginOffset.z * lossyScale.x, marginOffset.w * lossyScale.y, 0)); Handles.DrawSolidRectangleWithOutline(m_HandlePoints, new Color32(255, 255, 255, 0), new Color32(255, 255, 0, 255)); - + // Draw & process FreeMoveHandles // LEFT HANDLE @@ -354,7 +354,7 @@ public void OnSceneGUI() { float delta = oldTop.y - newTop.y; marginOffset.y += delta / lossyScale.y; - //Debug.Log("Top Margin H1:" + handlePoints[1] + " H2:" + handlePoints[2]); + //Debug.Log("Top Margin H1:" + handlePoints[1] + " H2:" + handlePoints[2]); hasChanged = true; } @@ -404,50 +404,47 @@ protected void DrawTextInput() } else { + // Display RTL Toggle + float labelWidth = EditorGUIUtility.labelWidth; + EditorGUIUtility.labelWidth = 110f; + + m_IsRightToLeftProp.boolValue = EditorGUI.Toggle(new Rect(rect.width - 120, rect.y + 3, 130, 20), k_RtlToggleLabel, m_IsRightToLeftProp.boolValue); + + EditorGUIUtility.labelWidth = labelWidth; + EditorGUI.BeginChangeCheck(); EditorGUILayout.PropertyField(m_TextProp, GUIContent.none); - if (EditorGUI.EndChangeCheck() || (m_IsRightToLeftProp.boolValue && string.IsNullOrEmpty(m_RtlText))) + if (EditorGUI.EndChangeCheck()) { m_TextComponent.m_inputSource = TMP_Text.TextInputSources.Text; m_TextComponent.m_isInputParsingRequired = true; m_HavePropertiesChanged = true; - - // Handle Left to Right or Right to Left Editor - if (m_IsRightToLeftProp.boolValue) - { - m_RtlText = string.Empty; - string sourceText = m_TextProp.stringValue; - - // Reverse Text displayed in Text Input Box - for (int i = 0; i < sourceText.Length; i++) - { - m_RtlText += sourceText[sourceText.Length - i - 1]; - } - } } - // Toggle showing RTL mode - float labelWidth = EditorGUIUtility.labelWidth; - EditorGUIUtility.labelWidth = 110f; - m_IsRightToLeftProp.boolValue = EditorGUI.Toggle(new Rect(rect.width - 120, rect.y + 3, 130, 20), k_RtlToggleLabel, m_IsRightToLeftProp.boolValue); - EditorGUIUtility.labelWidth = labelWidth; - if (m_IsRightToLeftProp.boolValue) { + // Copy source text to RTL string + m_RtlText = string.Empty; + string sourceText = m_TextProp.stringValue; + + // Reverse Text displayed in Text Input Box + for (int i = 0; i < sourceText.Length; i++) + m_RtlText += sourceText[sourceText.Length - i - 1]; + GUILayout.Label("RTL Text Input"); + EditorGUI.BeginChangeCheck(); m_RtlText = EditorGUILayout.TextArea(m_RtlText, TMP_UIStyleManager.wrappingTextArea, GUILayout.Height(EditorGUI.GetPropertyHeight(m_TextProp) - EditorGUIUtility.singleLineHeight), GUILayout.ExpandWidth(true)); + if (EditorGUI.EndChangeCheck()) { // Convert RTL input - string sourceText = string.Empty; + sourceText = string.Empty; // Reverse Text displayed in Text Input Box for (int i = 0; i < m_RtlText.Length; i++) - { sourceText += m_RtlText[m_RtlText.Length - i - 1]; - } m_TextProp.stringValue = sourceText; } @@ -490,7 +487,7 @@ protected void DrawMainSettings() DrawWrappingOverflow(); DrawTextureMapping(); - + //EditorGUI.indentLevel -= 1; } @@ -567,9 +564,9 @@ void DrawFont() rect.x += rect.width; v7 = TMP_EditorUtility.EditorToggle(rect, (styleValue & 64) == 64, k_StrikethroughLabel, TMP_UIStyleManager.alignmentButtonRight) ? 64 : 0; // Strikethrough rect.x += rect.width; - + int selected = 0; - + EditorGUI.BeginChangeCheck(); v4 = TMP_EditorUtility.EditorToggle(rect, (styleValue & 8) == 8, k_LowercaseLabel, TMP_UIStyleManager.alignmentButtonLeft) ? 8 : 0; // Lowercase if (EditorGUI.EndChangeCheck() && v4 > 0) @@ -619,14 +616,14 @@ void DrawFont() v7 = TMP_EditorUtility.EditorToggle(rect, (styleValue & 64) == 64, k_StrikethroughLabel, TMP_UIStyleManager.alignmentButtonRight) ? 64 : 0; // Strikethrough rect = EditorGUILayout.GetControlRect(true, EditorGUIUtility.singleLineHeight + 2f); - + rect.x += EditorGUIUtility.labelWidth; rect.width -= EditorGUIUtility.labelWidth; rect.width = Mathf.Max(25f, rect.width / 4f); int selected = 0; - + EditorGUI.BeginChangeCheck(); v4 = TMP_EditorUtility.EditorToggle(rect, (styleValue & 8) == 8, k_LowercaseLabel, TMP_UIStyleManager.alignmentButtonLeft) ? 8 : 0; // Lowercase if (EditorGUI.EndChangeCheck() && v4 > 0) @@ -664,7 +661,7 @@ void DrawFont() // FONT SIZE EditorGUI.BeginChangeCheck(); - + EditorGUI.BeginDisabledGroup(m_AutoSizingProp.boolValue); EditorGUILayout.PropertyField(m_FontSizeProp, k_FontSizeLabel, GUILayout.MaxWidth(EditorGUIUtility.labelWidth + 50f)); EditorGUI.EndDisabledGroup(); @@ -679,7 +676,7 @@ void DrawFont() } EditorGUI.indentLevel += 1; - + EditorGUI.BeginChangeCheck(); EditorGUILayout.PropertyField(m_AutoSizingProp, k_AutoSizeLabel); if (EditorGUI.EndChangeCheck()) @@ -745,13 +742,13 @@ void DrawFont() m_LineSpacingMaxProp.floatValue = Mathf.Min(0, m_LineSpacingMaxProp.floatValue); m_HavePropertiesChanged = true; } - + EditorGUI.indentLevel = previousIndent; } EditorGUI.indentLevel -= 1; - + EditorGUILayout.Space(); } @@ -760,24 +757,30 @@ void DrawColor() { // FACE VERTEX COLOR EditorGUI.BeginChangeCheck(); - EditorGUILayout.PropertyField(m_FontColorProp, k_BaseColorLabel); + Color vertexColor = EditorGUILayout.ColorField(k_BaseColorLabel, m_FontColorProp.colorValue, false, true, false); + if (EditorGUI.EndChangeCheck()) + { + m_FontColorProp.colorValue = vertexColor; + m_HavePropertiesChanged = true; + } + EditorGUI.BeginChangeCheck(); EditorGUILayout.PropertyField(m_EnableVertexGradientProp, k_ColorGradientLabel); if (EditorGUI.EndChangeCheck()) { m_HavePropertiesChanged = true; } - + EditorGUIUtility.fieldWidth = 0; if (m_EnableVertexGradientProp.boolValue) { EditorGUI.indentLevel += 1; - + EditorGUI.BeginChangeCheck(); - + EditorGUILayout.PropertyField(m_FontColorGradientPresetProp, k_ColorPresetLabel); - + SerializedObject obj = null; SerializedProperty colorMode; @@ -829,9 +832,9 @@ void DrawColor() TMP_EditorUtility.DrawColorProperty(rect, topLeft); rect.x += rect.width; - + TMP_EditorUtility.DrawColorProperty(rect, topRight); - + bottomLeft.colorValue = topLeft.colorValue; bottomRight.colorValue = topRight.colorValue; break; @@ -840,9 +843,9 @@ void DrawColor() rect = EditorGUILayout.GetControlRect(false, EditorGUIUtility.singleLineHeight * (EditorGUIUtility.wideMode ? 1 : 2)); rect.x += EditorGUIUtility.labelWidth; - + TMP_EditorUtility.DrawColorProperty(rect, bottomLeft); - + topRight.colorValue = topLeft.colorValue; bottomRight.colorValue = bottomLeft.colorValue; break; @@ -852,13 +855,13 @@ void DrawColor() TMP_EditorUtility.DrawColorProperty(rect, topLeft); rect.x += rect.width; - + TMP_EditorUtility.DrawColorProperty(rect, topRight); rect = EditorGUILayout.GetControlRect(false, EditorGUIUtility.singleLineHeight * (EditorGUIUtility.wideMode ? 1 : 2)); rect.x += EditorGUIUtility.labelWidth; rect.width = (rect.width - EditorGUIUtility.labelWidth) / 2f; - + TMP_EditorUtility.DrawColorProperty(rect, bottomLeft); rect.x += rect.width; @@ -866,7 +869,7 @@ void DrawColor() TMP_EditorUtility.DrawColorProperty(rect, bottomRight); break; } - + if (EditorGUI.EndChangeCheck()) { m_HavePropertiesChanged = true; @@ -879,9 +882,9 @@ void DrawColor() EditorGUI.indentLevel -= 1; } - + EditorGUILayout.PropertyField(m_OverrideHtmlColorProp, k_OverrideTagsLabel); - + EditorGUILayout.Space(); } @@ -889,9 +892,9 @@ void DrawSpacing() { // CHARACTER, LINE & PARAGRAPH SPACING EditorGUI.BeginChangeCheck(); - + Rect rect = EditorGUILayout.GetControlRect(true, EditorGUIUtility.singleLineHeight); - + EditorGUI.PrefixLabel(rect, k_SpacingOptionsLabel); int oldIndent = EditorGUI.indentLevel; @@ -899,9 +902,9 @@ void DrawSpacing() rect.x += EditorGUIUtility.labelWidth; rect.width = (rect.width - EditorGUIUtility.labelWidth - 3f) / 2f; - + EditorGUIUtility.labelWidth = Mathf.Min(rect.width * 0.55f, 80f); - + EditorGUI.PropertyField(rect, m_CharacterSpacingProp, k_CharacterSpacingLabel); rect.x += rect.width + 3f; EditorGUI.PropertyField(rect, m_WordSpacingProp, k_WordSpacingLabel); @@ -961,7 +964,7 @@ void DrawWrappingOverflow() m_HavePropertiesChanged = true; m_TextComponent.m_isInputParsingRequired = true; } - + // TEXT OVERFLOW EditorGUI.BeginChangeCheck(); @@ -975,7 +978,7 @@ void DrawWrappingOverflow() EditorGUIUtility.fieldWidth = fieldWidth; EditorGUILayout.PropertyField(m_LinkedTextComponentProp, GUIContent.none); - + EditorGUILayout.EndHorizontal(); if (GUI.changed) @@ -1084,7 +1087,7 @@ protected void DrawIsTextObjectScaleStatic() protected void DrawRichText() { EditorGUI.BeginChangeCheck(); - + EditorGUILayout.PropertyField(m_IsRichTextProp, k_RichTextLabel); if (EditorGUI.EndChangeCheck()) m_HavePropertiesChanged = true; @@ -1333,4 +1336,4 @@ protected void DrawPropertySlider(GUIContent label, SerializedProperty property) protected abstract void OnUndoRedo(); } -} \ No newline at end of file +} diff --git a/Scripts/Editor/TMP_BaseShaderGUI.cs b/Scripts/Editor/TMP_BaseShaderGUI.cs index a36a01c..5fd33ce 100644 --- a/Scripts/Editor/TMP_BaseShaderGUI.cs +++ b/Scripts/Editor/TMP_BaseShaderGUI.cs @@ -177,7 +177,7 @@ public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] pro protected bool BeginPanel(string panel, bool expanded) { EditorGUI.indentLevel = 0; - + EditorGUILayout.BeginVertical(EditorStyles.helpBox); Rect r = EditorGUI.IndentedRect(GUILayoutUtility.GetRect(20, 18)); r.x += 20; @@ -199,7 +199,7 @@ protected bool BeginPanel(string panel, bool expanded) protected bool BeginPanel(string panel, ShaderFeature feature, bool expanded, bool readState = true) { EditorGUI.indentLevel = 0; - + if (readState) { feature.ReadState(m_Material); @@ -286,7 +286,7 @@ void DoTexture(string name, string label, System.Type type, bool withTilingOffse { MaterialProperty property = FindProperty(name, m_Properties); m_Editor.BeginAnimatedCheck(Rect.zero, property); - + Rect rect = EditorGUILayout.GetControlRect(true, 60f); float totalWidth = rect.width; rect.width = EditorGUIUtility.labelWidth + 60f; @@ -414,7 +414,7 @@ protected void DoColor(string name, string label) { MaterialProperty property = BeginProperty(name); s_TempLabel.text = label; - Color value = EditorGUI.ColorField(EditorGUILayout.GetControlRect(), s_TempLabel, property.colorValue); + Color value = EditorGUI.ColorField(EditorGUILayout.GetControlRect(), s_TempLabel, property.colorValue, false, true, true); if (EndProperty()) { property.colorValue = value; diff --git a/Scripts/Editor/TMP_CharacterPropertyDrawer.cs b/Scripts/Editor/TMP_CharacterPropertyDrawer.cs index 46e94f0..98f32c7 100644 --- a/Scripts/Editor/TMP_CharacterPropertyDrawer.cs +++ b/Scripts/Editor/TMP_CharacterPropertyDrawer.cs @@ -81,7 +81,7 @@ public override void OnGUI(Rect position, SerializedProperty property, GUIConten if (elementIndex == -1) prop_GlyphIndex.intValue = currentGlyphIndex; else - fontAsset.m_IsFontAssetLookupTablesDirty = true; + fontAsset.IsFontAssetLookupTablesDirty = true; } int glyphIndex = prop_GlyphIndex.intValue; diff --git a/Scripts/Editor/TMP_EditorPanel.cs b/Scripts/Editor/TMP_EditorPanel.cs index f430407..a03494f 100644 --- a/Scripts/Editor/TMP_EditorPanel.cs +++ b/Scripts/Editor/TMP_EditorPanel.cs @@ -1,5 +1,6 @@ using UnityEngine; using UnityEditor; +using UnityEditor.Presets; namespace TMPro.EditorUtilities { @@ -11,22 +12,29 @@ public class TMP_EditorPanel : TMP_BaseEditorPanel static readonly GUIContent k_OrderInLayerLabel = new GUIContent("Order in Layer", "Renderer's order within a sorting layer."); static readonly GUIContent k_OrthographicLabel = new GUIContent("Orthographic Mode", "Should be enabled when using an orthographic camera. Instructs the shader to not perform any perspective correction."); static readonly GUIContent k_VolumetricLabel = new GUIContent("Volumetric Setup", "Use cubes rather than quads to render the text. Allows for volumetric rendering when combined with a compatible shader."); - + + private static string[] k_SortingLayerNames; + bool IsPreset; + SerializedProperty m_IsVolumetricTextProp; - SerializedProperty m_IsOrthographicProp; - Renderer m_Renderer; protected override void OnEnable() { base.OnEnable(); + // Determine if the inspected object is a Preset + IsPreset = (int)(target as Component).gameObject.hideFlags == 93; + m_IsOrthographicProp = serializedObject.FindProperty("m_isOrthographic"); - + m_IsVolumetricTextProp = serializedObject.FindProperty("m_isVolumetricText"); m_Renderer = m_TextComponent.GetComponent(); + + // Populate Sorting Layer Names + k_SortingLayerNames = SortingLayerHelper.sortingLayerNames; } protected override void DrawExtraSettings() @@ -51,7 +59,7 @@ protected override void DrawExtraSettings() DrawIsTextObjectScaleStatic(); DrawOrthographicMode(); - + DrawRichText(); DrawParsing(); @@ -76,33 +84,29 @@ protected void DrawSortingLayer() EditorGUI.BeginChangeCheck(); - // SORTING LAYERS - var sortingLayerNames = SortingLayerHelper.sortingLayerNames; - - var textComponent = (TextMeshPro)m_TextComponent; + TextMeshPro textComponent = (TextMeshPro)m_TextComponent; // Look up the layer name using the current layer ID - string oldName = SortingLayerHelper.GetSortingLayerNameFromID(textComponent.sortingLayerID); + string oldName = IsPreset ? SortingLayer.IDToName(textComponent._SortingLayerID) : SortingLayer.IDToName(textComponent.sortingLayerID); // Use the name to look up our array index into the names list - int oldLayerIndex = System.Array.IndexOf(sortingLayerNames, oldName); + int oldLayerIndex = System.Array.IndexOf(k_SortingLayerNames, oldName); // Show the pop-up for the names EditorGUIUtility.fieldWidth = 0f; - int newLayerIndex = EditorGUILayout.Popup(k_SortingLayerLabel, oldLayerIndex, sortingLayerNames); - + int newLayerIndex = EditorGUILayout.Popup(k_SortingLayerLabel, oldLayerIndex, k_SortingLayerNames); + // If the index changes, look up the ID for the new index to store as the new ID if (newLayerIndex != oldLayerIndex) - { - textComponent.sortingLayerID = SortingLayerHelper.GetSortingLayerIDForIndex(newLayerIndex); - } + UpdateTargetsSortingLayerID(SortingLayer.NameToID(k_SortingLayerNames[newLayerIndex])); + + // Get value from internal property if target is a Preset otherwise from the public property + int oldSortingOrder = IsPreset ? textComponent._SortingOrder : textComponent.sortingOrder; + + int newSortingLayerOrder = EditorGUILayout.IntField(k_OrderInLayerLabel, oldSortingOrder); - // Expose the manual sorting order - int newSortingLayerOrder = EditorGUILayout.IntField(k_OrderInLayerLabel, textComponent.sortingOrder); if (newSortingLayerOrder != textComponent.sortingOrder) - { - textComponent.sortingOrder = newSortingLayerOrder; - } + UpdateTargetsSortingOrder(newSortingLayerOrder); if (EditorGUI.EndChangeCheck()) m_HavePropertiesChanged = true; @@ -161,5 +165,27 @@ protected override void OnUndoRedo() } } } + + void UpdateTargetsSortingLayerID(int sortingLayerID) + { + for (int i = 0; i < targets.Length; i++) + { + var textComponent = (TextMeshPro)targets[i]; + + if (textComponent != null) + textComponent.sortingLayerID = sortingLayerID; + } + } + + void UpdateTargetsSortingOrder(int sortingOrder) + { + for (int i = 0; i < targets.Length; i++) + { + var textComponent = (TextMeshPro)targets[i]; + + if (textComponent != null) + textComponent.sortingOrder = sortingOrder; + } + } } -} \ No newline at end of file +} diff --git a/Scripts/Editor/TMP_FontAssetEditor.cs b/Scripts/Editor/TMP_FontAssetEditor.cs index fd1099b..60efbe5 100644 --- a/Scripts/Editor/TMP_FontAssetEditor.cs +++ b/Scripts/Editor/TMP_FontAssetEditor.cs @@ -1280,7 +1280,7 @@ public override void OnInspectorGUI() if (m_DisplayDestructiveChangeWarning == false) TMPro_EventManager.ON_FONT_PROPERTY_CHANGED(true, m_fontAsset); - if (m_fontAsset.m_IsFontAssetLookupTablesDirty || evt_cmd == k_UndoRedo) + if (m_fontAsset.IsFontAssetLookupTablesDirty || evt_cmd == k_UndoRedo) m_fontAsset.ReadFontAssetDefinition(); isAssetDirty = false; diff --git a/Scripts/Editor/TMP_PackageUtilities.cs b/Scripts/Editor/TMP_PackageUtilities.cs index d0bf164..e082954 100644 --- a/Scripts/Editor/TMP_PackageUtilities.cs +++ b/Scripts/Editor/TMP_PackageUtilities.cs @@ -81,7 +81,7 @@ static void ShowConverterWindow() }; /// - /// + /// /// struct AssetModificationRecord { @@ -234,7 +234,7 @@ void SetEditorWindowSize() /// - /// + /// /// /// /// @@ -402,7 +402,7 @@ static void ScanProjectFileAsync(AssetFileRecord fileRecord) /// - /// + /// /// private static void ResetScanProcess() { @@ -415,7 +415,7 @@ private static void ResetScanProcess() /// - /// + /// /// private static void UpdateProjectFiles() { @@ -488,7 +488,7 @@ void OnEnable() private void OnGUI() { - + } @@ -535,7 +535,7 @@ struct AssetModificationRecord } /// - /// + /// /// [MenuItem("Window/TextMeshPro/Import TMP Essential Resources", false, 2050)] public static void ImportProjectResourcesMenu() @@ -545,7 +545,7 @@ public static void ImportProjectResourcesMenu() /// - /// + /// /// [MenuItem("Window/TextMeshPro/Import TMP Examples and Extras", false, 2051)] public static void ImportExamplesContentMenu() @@ -562,24 +562,56 @@ private static void GetVersionInfo() /// - /// + /// /// private static void ImportExamplesAndExtras() { - string packageFullPath = EditorUtilities.TMP_EditorUtility.packageFullPath; + string packageFullPath = TMP_EditorUtility.packageFullPath; AssetDatabase.ImportPackage(packageFullPath + "/Package Resources/TMP Examples & Extras.unitypackage", true); } + private static string k_SettingsFilePath; + private static byte[] k_SettingsBackup; /// - /// + /// /// private static void ImportEssentialResources() { - string packageFullPath = EditorUtilities.TMP_EditorUtility.packageFullPath; + // Check if the TMP Settings asset is already present in the project. + string[] settings = AssetDatabase.FindAssets("t:TMP_Settings"); + + if (settings.Length > 0) + { + // Save assets just in case the TMP Setting were modified before import. + AssetDatabase.SaveAssets(); + + // Copy existing TMP Settings asset to a byte[] + k_SettingsFilePath = AssetDatabase.GUIDToAssetPath(settings[0]); + k_SettingsBackup = File.ReadAllBytes(k_SettingsFilePath); + + RegisterResourceImportCallback(); + } + + string packageFullPath = TMP_EditorUtility.packageFullPath; AssetDatabase.ImportPackage(packageFullPath + "/Package Resources/TMP Essential Resources.unitypackage", true); } + + private static void RegisterResourceImportCallback() + { + AssetDatabase.importPackageCompleted += ImportCallback; + } + + private static void ImportCallback(string packageName) + { + // Restore backup of TMP Settings from byte[] + File.WriteAllBytes(k_SettingsFilePath, k_SettingsBackup); + + AssetDatabase.Refresh(); + + AssetDatabase.importPackageCompleted -= ImportCallback; + } } } diff --git a/Scripts/Editor/TMP_SettingsEditor.cs b/Scripts/Editor/TMP_SettingsEditor.cs index f7bf4b7..e4b8d5c 100644 --- a/Scripts/Editor/TMP_SettingsEditor.cs +++ b/Scripts/Editor/TMP_SettingsEditor.cs @@ -44,10 +44,11 @@ internal class Styles public static readonly GUIContent getFontFeaturesAtRuntime = new GUIContent("Get Font Features at Runtime", "Determines if Glyph Adjustment Data will be retrieved from font files at runtime when new characters and glyphs are added to font assets."); public static readonly GUIContent dynamicAtlasTextureGroup = new GUIContent("Dynamic Atlas Texture Group"); - public static readonly GUIContent missingGlyphLabel = new GUIContent("Replacement Character", "The character to be displayed when the requested character is not found in any font asset or fallbacks."); + public static readonly GUIContent missingGlyphLabel = new GUIContent("Missing Character Unicode", "The character to be displayed when the requested character is not found in any font asset or fallbacks."); public static readonly GUIContent disableWarningsLabel = new GUIContent("Disable warnings", "Disable warning messages in the Console."); public static readonly GUIContent defaultSpriteAssetLabel = new GUIContent("Default Sprite Asset", "The Sprite Asset that will be assigned by default when using the tag when no Sprite Asset is specified."); + public static readonly GUIContent missingSpriteCharacterUnicodeLabel = new GUIContent("Missing Sprite Unicode", "The unicode value for the sprite character to be displayed when the requested sprite character is not found in any sprite assets or fallbacks."); public static readonly GUIContent enableEmojiSupportLabel = new GUIContent("iOS Emoji Support", "Enables Emoji support for Touch Screen Keyboards on target devices."); //public static readonly GUIContent spriteRelativeScale = new GUIContent("Relative Scaling", "Determines if the sprites will be scaled relative to the primary font asset assigned to the text object or relative to the current font asset."); @@ -74,6 +75,7 @@ internal class Styles SerializedProperty m_PropEnableRaycastTarget; SerializedProperty m_PropSpriteAsset; + SerializedProperty m_PropMissingSpriteCharacterUnicode; //SerializedProperty m_PropSpriteRelativeScaling; SerializedProperty m_PropEnableEmojiSupport; SerializedProperty m_PropSpriteAssetPath; @@ -102,6 +104,8 @@ internal class Styles SerializedProperty m_PropFollowingCharacters; SerializedProperty m_PropUseModernHangulLineBreakingRules; + private const string k_UndoRedo = "UndoRedoPerformed"; + public void OnEnable() { if (target == null) @@ -118,6 +122,7 @@ public void OnEnable() m_PropEnableRaycastTarget = serializedObject.FindProperty("m_EnableRaycastTarget"); m_PropSpriteAsset = serializedObject.FindProperty("m_defaultSpriteAsset"); + m_PropMissingSpriteCharacterUnicode = serializedObject.FindProperty("m_MissingCharacterSpriteUnicode"); //m_PropSpriteRelativeScaling = serializedObject.FindProperty("m_SpriteRelativeScaling"); m_PropEnableEmojiSupport = serializedObject.FindProperty("m_enableEmojiSupport"); m_PropSpriteAssetPath = serializedObject.FindProperty("m_defaultSpriteAssetPath"); @@ -164,6 +169,7 @@ public void OnEnable() public override void OnInspectorGUI() { serializedObject.Update(); + string evt_cmd = Event.current.commandName; float labelWidth = EditorGUIUtility.labelWidth; float fieldWidth = EditorGUIUtility.fieldWidth; @@ -259,6 +265,7 @@ public override void OnInspectorGUI() GUILayout.Label(Styles.defaultSpriteAssetLabel, EditorStyles.boldLabel); EditorGUI.indentLevel = 1; EditorGUILayout.PropertyField(m_PropSpriteAsset, Styles.defaultSpriteAssetLabel); + EditorGUILayout.PropertyField(m_PropMissingSpriteCharacterUnicode, Styles.missingSpriteCharacterUnicodeLabel); EditorGUILayout.PropertyField(m_PropEnableEmojiSupport, Styles.enableEmojiSupportLabel); //EditorGUILayout.PropertyField(m_PropSpriteRelativeScaling, Styles.spriteRelativeScale); EditorGUILayout.PropertyField(m_PropSpriteAssetPath, Styles.spriteAssetsPathLabel); @@ -313,7 +320,7 @@ public override void OnInspectorGUI() EditorGUILayout.Space(); EditorGUILayout.EndVertical(); - if (serializedObject.ApplyModifiedProperties()) + if (serializedObject.ApplyModifiedProperties() || evt_cmd == k_UndoRedo) { EditorUtility.SetDirty(target); TMPro_EventManager.ON_TMP_SETTINGS_CHANGED(); diff --git a/Scripts/Editor/TMP_SpriteAssetImporter.cs b/Scripts/Editor/TMP_SpriteAssetImporter.cs index 72d8277..5408c6f 100644 --- a/Scripts/Editor/TMP_SpriteAssetImporter.cs +++ b/Scripts/Editor/TMP_SpriteAssetImporter.cs @@ -168,6 +168,7 @@ private static void PopulateSpriteTables(TexturePacker_JsonArray.SpriteDataObjec TMP_SpriteCharacter spriteCharacter = new TMP_SpriteCharacter(0, spriteGlyph); spriteCharacter.name = spriteData.filename.Split('.')[0]; + spriteCharacter.unicode = 0xFFFE; spriteCharacter.scale = 1.0f; spriteCharacterTable.Add(spriteCharacter); diff --git a/Scripts/Editor/TMP_SpriteAssetMenu.cs b/Scripts/Editor/TMP_SpriteAssetMenu.cs index 8fe8c6f..0e46711 100644 --- a/Scripts/Editor/TMP_SpriteAssetMenu.cs +++ b/Scripts/Editor/TMP_SpriteAssetMenu.cs @@ -40,7 +40,7 @@ static void UpdateSpriteAsset(MenuCommand command) } internal static void UpdateSpriteAsset(TMP_SpriteAsset spriteAsset) - { + { // Get a list of all the sprites contained in the texture referenced by the sprite asset. // This only works if the texture is set to sprite mode. string filePath = AssetDatabase.GetAssetPath(spriteAsset.spriteSheet); @@ -98,7 +98,7 @@ internal static void UpdateSpriteAsset(TMP_SpriteAsset spriteAsset) spriteGlyphTable.Add(spriteGlyph); - TMP_SpriteCharacter spriteCharacter = new TMP_SpriteCharacter(0, spriteGlyph); + TMP_SpriteCharacter spriteCharacter = new TMP_SpriteCharacter(0xFFFE, spriteGlyph); spriteCharacter.name = sprite.name; spriteCharacter.scale = 1.0f; @@ -117,6 +117,14 @@ internal static void UpdateSpriteAsset(TMP_SpriteAsset spriteAsset) } } + // Update Sprite Character Table to replace unicode 0x0 by 0xFFFE + for (int i = 0; i < spriteAsset.spriteCharacterTable.Count; i++) + { + TMP_SpriteCharacter spriteCharacter = spriteAsset.spriteCharacterTable[i]; + if (spriteCharacter.unicode == 0) + spriteCharacter.unicode = 0xFFFE; + } + // Sort glyph table by glyph index spriteAsset.SortGlyphTable(); spriteAsset.UpdateLookupTables(); @@ -189,7 +197,7 @@ public static void CreateSpriteAsset() // Get the Sprites contained in the Sprite Sheet EditorUtility.SetDirty(spriteAsset); - + //spriteAsset.sprites = sprites; // Set source texture back to Not Readable. @@ -206,12 +214,12 @@ public static void CreateSpriteAsset() private static void PopulateSpriteTables(Texture source, ref List spriteCharacterTable, ref List spriteGlyphTable) { //Debug.Log("Creating new Sprite Asset."); - + string filePath = AssetDatabase.GetAssetPath(source); // Get all the Sprites sorted by Index Sprite[] sprites = AssetDatabase.LoadAllAssetsAtPath(filePath).Select(x => x as Sprite).Where(x => x != null).OrderByDescending(x => x.rect.y).ThenBy(x => x.rect.x).ToArray(); - + for (int i = 0; i < sprites.Length; i++) { Sprite sprite = sprites[i]; @@ -225,7 +233,7 @@ private static void PopulateSpriteTables(Texture source, ref List UpdateSpriteInfo(TMP_SpriteAsset spriteAsset) for (int j = 0; j < ids.Length; j++ ) { if (ids[0] != 0) break; - + if (j > 0 && (ids[j] - ids[j - 1]) > 1) { id = ids[j - 1] + 1; @@ -358,6 +366,6 @@ private static List UpdateSpriteInfo(TMP_SpriteAsset spriteAsset) return spriteAsset.spriteInfoList; } - + } -} \ No newline at end of file +} diff --git a/Scripts/Editor/TMP_SubMesh_Editor.cs b/Scripts/Editor/TMP_SubMesh_Editor.cs index b5a3cc7..f668af9 100644 --- a/Scripts/Editor/TMP_SubMesh_Editor.cs +++ b/Scripts/Editor/TMP_SubMesh_Editor.cs @@ -15,13 +15,15 @@ private struct m_foldout //public static bool shadowSetting = false; //public static bool materialEditor = true; } - + private SerializedProperty fontAsset_prop; private SerializedProperty spriteAsset_prop; private TMP_SubMesh m_SubMeshComponent; private Renderer m_Renderer; + private string[] m_SortingLayerNames; + public void OnEnable() { fontAsset_prop = serializedObject.FindProperty("m_fontAsset"); @@ -30,47 +32,40 @@ public void OnEnable() m_SubMeshComponent = target as TMP_SubMesh; m_Renderer = m_SubMeshComponent.renderer; + + m_SortingLayerNames = SortingLayerHelper.sortingLayerNames; } public override void OnInspectorGUI() { EditorGUI.indentLevel = 0; - + GUI.enabled = false; EditorGUILayout.PropertyField(fontAsset_prop); EditorGUILayout.PropertyField(spriteAsset_prop); GUI.enabled = true; - - EditorGUI.BeginChangeCheck(); - // SORTING LAYERS - var sortingLayerNames = SortingLayerHelper.sortingLayerNames; + EditorGUI.BeginChangeCheck(); // Look up the layer name using the current layer ID - string oldName = SortingLayerHelper.GetSortingLayerNameFromID(m_Renderer.sortingLayerID); + string oldName = SortingLayer.IDToName(m_Renderer.sortingLayerID); // Use the name to look up our array index into the names list - int oldLayerIndex = System.Array.IndexOf(sortingLayerNames, oldName); + int oldLayerIndex = System.Array.IndexOf(m_SortingLayerNames, oldName); // Show the pop-up for the names - int newLayerIndex = EditorGUILayout.Popup("Sorting Layer", oldLayerIndex, sortingLayerNames); + int newLayerIndex = EditorGUILayout.Popup("Sorting Layer", oldLayerIndex, m_SortingLayerNames); // If the index changes, look up the ID for the new index to store as the new ID if (newLayerIndex != oldLayerIndex) - { - //Undo.RecordObject(renderer, "Edit Sorting Layer"); - m_Renderer.sortingLayerID = SortingLayerHelper.GetSortingLayerIDForIndex(newLayerIndex); - //EditorUtility.SetDirty(renderer); - } + m_Renderer.sortingLayerID = SortingLayer.NameToID(m_SortingLayerNames[newLayerIndex]); // Expose the manual sorting order int newSortingLayerOrder = EditorGUILayout.IntField("Order in Layer", m_Renderer.sortingOrder); if (newSortingLayerOrder != m_Renderer.sortingOrder) - { - //Undo.RecordObject(renderer, "Edit Sorting Order"); m_Renderer.sortingOrder = newSortingLayerOrder; - } + } } } diff --git a/Scripts/Editor/TMPro_CreateObjectMenu.cs b/Scripts/Editor/TMPro_CreateObjectMenu.cs index e4a39b8..8742bf4 100644 --- a/Scripts/Editor/TMPro_CreateObjectMenu.cs +++ b/Scripts/Editor/TMPro_CreateObjectMenu.cs @@ -31,10 +31,15 @@ static void CreateTextMeshProObjectPerform(MenuCommand command) { // Apply TMP Settings Defaults if no Preset is defined if (Preset.GetDefaultForObject(textComponent) == null) - { + { textComponent.text = "Sample text"; textComponent.alignment = TextAlignmentOptions.TopLeft; } + else + { + textComponent.renderer.sortingLayerID = textComponent._SortingLayerID; + textComponent.renderer.sortingOrder = textComponent._SortingOrder; + } if (TMP_Settings.autoSizeTextContainer) { @@ -46,6 +51,11 @@ static void CreateTextMeshProObjectPerform(MenuCommand command) textComponent.rectTransform.sizeDelta = TMP_Settings.defaultTextMeshProTextContainerSize; } } + else + { + textComponent.text = "Sample text"; + textComponent.alignment = TextAlignmentOptions.TopLeft; + } Undo.RegisterCreatedObjectUndo(go, "Create " + go.name); @@ -92,12 +102,18 @@ static void CreateTextMeshProGuiObjectPerform(MenuCommand menuCommand) textComponent.rectTransform.sizeDelta = TMP_Settings.defaultTextMeshProUITextContainerSize; } } + else + { + textComponent.fontSize = 36; + textComponent.color = Color.white; + textComponent.text = "New Text"; + } PlaceUIElementRoot(go, menuCommand); } [MenuItem("GameObject/UI/Button - TextMeshPro", false, 2031)] - static public void AddButton(MenuCommand menuCommand) + public static void AddButton(MenuCommand menuCommand) { GameObject go = TMP_DefaultControls.CreateButton(GetStandardResources()); @@ -119,7 +135,7 @@ static void AddTextMeshProInputField(MenuCommand menuCommand) [MenuItem("GameObject/UI/Dropdown - TextMeshPro", false, 2036)] - static public void AddDropdown(MenuCommand menuCommand) + public static void AddDropdown(MenuCommand menuCommand) { GameObject go = TMP_DefaultControls.CreateDropdown(GetStandardResources()); PlaceUIElementRoot(go, menuCommand); @@ -136,10 +152,10 @@ static public void AddDropdown(MenuCommand menuCommand) private const string kDropdownArrowPath = "UI/Skin/DropdownArrow.psd"; private const string kMaskPath = "UI/Skin/UIMask.psd"; - static private TMP_DefaultControls.Resources s_StandardResources; + private static TMP_DefaultControls.Resources s_StandardResources; - static private TMP_DefaultControls.Resources GetStandardResources() + private static TMP_DefaultControls.Resources GetStandardResources() { if (s_StandardResources.standard == null) { @@ -252,7 +268,7 @@ private static void PlaceUIElementRoot(GameObject element, MenuCommand menuComma } - static public GameObject CreateNewUI() + public static GameObject CreateNewUI() { // Root for the UI var root = new GameObject("Canvas"); @@ -311,7 +327,7 @@ private static void CreateEventSystem(bool select, GameObject parent) // Helper function that returns a Canvas GameObject; preferably a parent of the selection, or other existing Canvas. - static public GameObject GetOrCreateCanvasGameObject() + public static GameObject GetOrCreateCanvasGameObject() { GameObject selectedGo = Selection.activeGameObject; diff --git a/Scripts/Editor/TMPro_FontAssetCreatorWindow.cs b/Scripts/Editor/TMPro_FontAssetCreatorWindow.cs index f374577..a3c6c76 100644 --- a/Scripts/Editor/TMPro_FontAssetCreatorWindow.cs +++ b/Scripts/Editor/TMPro_FontAssetCreatorWindow.cs @@ -83,15 +83,15 @@ public static void ShowFontAtlasCreatorWindow(TMP_FontAsset fontAsset) // Make sure TMP Essential Resources have been imported. window.CheckEssentialResources(); } - + [System.Serializable] class FontAssetCreationSettingsContainer { public List fontAssetCreationSettings; } - + FontAssetCreationSettingsContainer m_FontAssetCreationSettingsContainer; - + //static readonly string[] m_FontCreationPresets = new string[] { "Recent 1", "Recent 2", "Recent 3", "Recent 4" }; int m_FontAssetCreationSettingsCurrentIndex = 0; @@ -103,7 +103,7 @@ class FontAssetCreationSettingsContainer System.Diagnostics.Stopwatch m_StopWatch; double m_GlyphPackingGenerationTime; double m_GlyphRenderingGenerationTime; - + string[] m_FontSizingOptions = { "Auto Sizing", "Custom Size" }; int m_PointSizeSamplingMode; string[] m_FontResolutionLabels = { "8", "16","32", "64", "128", "256", "512", "1024", "2048", "4096", "8192" }; @@ -120,7 +120,7 @@ enum FontPackingModes { Fast = 0, Optimum = 4 }; int m_CharacterCount; Vector2 m_ScrollPosition; Vector2 m_OutputScrollPosition; - + bool m_IsRepaintNeeded; float m_AtlasGenerationProgress; @@ -150,7 +150,7 @@ enum FontPackingModes { Fast = 0, Optimum = 4 }; Texture2D m_FontAtlasTexture; Texture2D m_SavedFontAtlas; - // + // List m_FontGlyphTable = new List(); List m_FontCharacterTable = new List(); @@ -175,7 +175,7 @@ public void OnEnable() { // Used for Diagnostics m_StopWatch = new System.Diagnostics.Stopwatch(); - + // Set Editor window size. minSize = new Vector2(315, minSize.y); @@ -386,7 +386,7 @@ void DrawControls() { m_ScrollPosition = EditorGUILayout.BeginScrollView(m_ScrollPosition); } - + GUILayout.Space(5f); GUILayout.Label(m_SelectedFontAsset != null ? string.Format("Font Settings [{0}]", m_SelectedFontAsset.name) : "Font Settings", EditorStyles.boldLabel); @@ -395,7 +395,7 @@ void DrawControls() EditorGUIUtility.labelWidth = 125f; EditorGUIUtility.fieldWidth = 5f; - + // Disable Options if already generating a font atlas texture. EditorGUI.BeginDisabledGroup(m_IsProcessing); { @@ -505,7 +505,7 @@ void DrawControls() { if (m_ReferencedFontAsset != null) m_CharacterSequence = TMP_EditorUtility.GetDecimalCharacterSequence(TMP_FontAsset.GetCharactersArray(m_ReferencedFontAsset)); - + m_IsFontAtlasInvalid = true; } @@ -522,7 +522,7 @@ void DrawControls() { m_IsFontAtlasInvalid = true; } - + EditorGUILayout.EndVertical(); break; @@ -537,7 +537,7 @@ void DrawControls() { if (m_ReferencedFontAsset != null) m_CharacterSequence = TMP_EditorUtility.GetUnicodeCharacterSequence(TMP_FontAsset.GetCharactersArray(m_ReferencedFontAsset)); - + m_IsFontAtlasInvalid = true; } @@ -569,12 +569,12 @@ void DrawControls() { if (m_ReferencedFontAsset != null) m_CharacterSequence = TMP_FontAsset.GetCharacters(m_ReferencedFontAsset); - + m_IsFontAtlasInvalid = true; } EditorGUI.indentLevel = 0; - + GUILayout.Label("Custom Character List", EditorStyles.boldLabel); EditorGUI.BeginChangeCheck(); m_CharacterSequence = EditorGUILayout.TextArea(m_CharacterSequence, TMP_UIStyleManager.textAreaBoxWindow, GUILayout.Height(120), GUILayout.ExpandWidth(true)); @@ -641,7 +641,7 @@ void DrawControls() { EditorGUILayout.HelpBox(m_WarningMessage, MessageType.Warning); } - + GUI.enabled = m_SourceFontFile != null && !m_IsProcessing && !m_IsGenerationDisabled; // Enable Preview if we are not already rendering a font. if (GUILayout.Button("Generate Font Atlas") && GUI.enabled) { @@ -657,7 +657,7 @@ void DrawControls() { Debug.Log("Font Asset Creator - Error [" + errorCode + "] has occurred while Initializing the FreeType Library."); } - + // Get file path of the source font file. string fontPath = AssetDatabase.GetAssetPath(m_SourceFontFile); @@ -710,14 +710,20 @@ void DrawControls() } m_CharacterCount = characterSet.Length; - + m_AtlasGenerationProgress = 0; m_IsProcessing = true; m_IsGenerationCancelled = false; - GlyphLoadFlags glyphLoadFlags = ((GlyphRasterModes)m_GlyphRenderMode & GlyphRasterModes.RASTER_MODE_HINTED) == GlyphRasterModes.RASTER_MODE_HINTED ? GlyphLoadFlags.LOAD_RENDER : GlyphLoadFlags.LOAD_RENDER | GlyphLoadFlags.LOAD_NO_HINTING; + GlyphLoadFlags glyphLoadFlags = ((GlyphRasterModes)m_GlyphRenderMode & GlyphRasterModes.RASTER_MODE_HINTED) == GlyphRasterModes.RASTER_MODE_HINTED + ? GlyphLoadFlags.LOAD_RENDER + : GlyphLoadFlags.LOAD_RENDER | GlyphLoadFlags.LOAD_NO_HINTING; + + glyphLoadFlags = ((GlyphRasterModes)m_GlyphRenderMode & GlyphRasterModes.RASTER_MODE_MONO) == GlyphRasterModes.RASTER_MODE_MONO + ? glyphLoadFlags | GlyphLoadFlags.LOAD_MONOCHROME + : glyphLoadFlags; - // + // AutoResetEvent autoEvent = new AutoResetEvent(false); // Worker thread to pack glyphs in the given texture space. @@ -932,7 +938,7 @@ void DrawControls() // Add glyphs to list of glyphs that need to be rendered. if (glyph.glyphRect.width > 0 && glyph.glyphRect.height > 0) m_GlyphsToRender.Add(glyph); - + foreach (uint unicode in m_GlyphLookupMap[glyphIndex]) { // Create new Character @@ -940,7 +946,7 @@ void DrawControls() } } - // + // foreach (Glyph glyph in m_GlyphsToPack) { foreach (uint unicode in m_GlyphLookupMap[glyph.index]) @@ -1014,7 +1020,7 @@ void DrawControls() // FONT STATUS & INFORMATION GUI.enabled = true; - + GUILayout.BeginVertical(EditorStyles.helpBox, GUILayout.Height(200)); m_OutputScrollPosition = EditorGUILayout.BeginScrollView(m_OutputScrollPosition); EditorGUILayout.LabelField(m_OutputFeedback, TMP_UIStyleManager.label); @@ -1023,9 +1029,9 @@ void DrawControls() // SAVE TEXTURE & CREATE and SAVE FONT XML FILE GUI.enabled = m_FontAtlasTexture != null && !m_IsProcessing; // Enable Save Button if font_Atlas is not Null. - + EditorGUILayout.BeginHorizontal(); - + if (GUILayout.Button("Save") && GUI.enabled) { if (m_SelectedFontAsset == null) @@ -1057,13 +1063,13 @@ void DrawControls() SaveNewFontAssetWithSameName(m_SelectedFontAsset); } } - + EditorGUILayout.EndHorizontal(); EditorGUILayout.Space(); EditorGUILayout.EndVertical(); - + GUI.enabled = true; // Re-enable GUI if (position.height > position.width || position.width < k_TwoColumnControlsWidth) @@ -1071,7 +1077,7 @@ void DrawControls() DrawPreview(); GUILayout.Space(5); } - + EditorGUILayout.EndScrollView(); if (m_IsFontAtlasInvalid) @@ -1091,7 +1097,7 @@ void ClearGeneratedData() DestroyImmediate(m_FontAtlasTexture); m_FontAtlasTexture = null; } - + m_AtlasGenerationProgressLabel = string.Empty; m_AtlasGenerationProgress = 0; m_SavedFontAtlas = null; @@ -1124,7 +1130,7 @@ void UpdateRenderFeedbackWindow() // Report characters missing from font file missingGlyphReport += "\n\nCharacters missing from font file:"; missingGlyphReport += "\n----------------------------------------"; - + m_OutputFeedback = missingGlyphReport; for (int i = 0; i < m_MissingCharacters.Count; i++) @@ -1175,7 +1181,7 @@ void CreateFontAtlasTexture() colors[i] = new Color32(c, c, c, c); } - // Clear allocation of + // Clear allocation of m_AtlasTextureBuffer = null; if ((m_GlyphRenderMode & GlyphRenderMode.RASTER) == GlyphRenderMode.RASTER || (m_GlyphRenderMode & GlyphRenderMode.RASTER_HINTED) == GlyphRenderMode.RASTER_HINTED) @@ -1197,7 +1203,7 @@ void CreateFontAtlasTexture() void SaveNewFontAsset(Object sourceObject) { string filePath; - + // Save new Font Asset and open save file requester at Source Font File location. string saveDirectory = new FileInfo(AssetDatabase.GetAssetPath(sourceObject)).DirectoryName; @@ -1523,7 +1529,7 @@ void Save_SDF_FontAsset(string filePath) //Set Font Asset Type fontAsset.atlasRenderMode = m_GlyphRenderMode; - // Add FaceInfo to Font Asset + // Add FaceInfo to Font Asset fontAsset.faceInfo = m_FaceInfo; // Add GlyphInfo[] to Font Asset @@ -1717,7 +1723,7 @@ void DrawPreview() pixelRect = GUILayoutUtility.GetAspectRect(1f); } - + if (m_FontAtlasTexture != null) { EditorGUI.DrawTextureAlpha(pixelRect, m_FontAtlasTexture, ScaleMode.StretchToFill); @@ -1779,4 +1785,4 @@ public TMP_FontFeatureTable GetKerningTable() return fontFeatureTable; } } -} \ No newline at end of file +} diff --git a/Scripts/Editor/TMPro_SortingLayerHelper.cs b/Scripts/Editor/TMPro_SortingLayerHelper.cs index 4f44c53..c36f9f8 100644 --- a/Scripts/Editor/TMPro_SortingLayerHelper.cs +++ b/Scripts/Editor/TMPro_SortingLayerHelper.cs @@ -1,105 +1,32 @@ -/* - * Copyright (c) 2014, Nick Gravelyn. - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * - * 3. This notice may not be removed or altered from any source - * distribution. - */ +using UnityEngine; -using UnityEngine; -using UnityEditor; -using System; -using System.Reflection; namespace TMPro { // Helpers used by the different sorting layer classes. public static class SortingLayerHelper { - private static Type _utilityType; - private static PropertyInfo _sortingLayerNamesProperty; - private static MethodInfo _getSortingLayerUserIdMethod; - - static SortingLayerHelper() - { - _utilityType = Type.GetType("UnityEditorInternal.InternalEditorUtility, UnityEditor"); - _sortingLayerNamesProperty = _utilityType.GetProperty("sortingLayerNames", BindingFlags.Static | BindingFlags.NonPublic); - _getSortingLayerUserIdMethod = _utilityType.GetMethod("GetSortingLayerUniqueID", BindingFlags.Static | BindingFlags.NonPublic); - } - // Gets an array of sorting layer names. - // Since this uses reflection, callers should check for 'null' which will be returned if the reflection fails. public static string[] sortingLayerNames { get { - if (_sortingLayerNamesProperty == null) - { - return null; - } - - return _sortingLayerNamesProperty.GetValue(null, null) as string[]; - } - } - - // Given the ID of a sorting layer, returns the sorting layer's name - public static string GetSortingLayerNameFromID(int id) - { - string[] names = sortingLayerNames; - if (names == null) - { - return null; + return GetSortingLayerNames(); } - - for (int i = 0; i < names.Length; i++) - { - if (GetSortingLayerIDForIndex(i) == id) - { - return names[i]; - } - } - - return null; } - // Given the name of a sorting layer, returns the ID. - public static int GetSortingLayerIDForName(string name) + static string[] GetSortingLayerNames() { - string[] names = sortingLayerNames; - if (names == null) - { - return 0; - } + int layerCount = SortingLayer.layers.Length; - return GetSortingLayerIDForIndex(Array.IndexOf(names, name)); - } + string[] layerNames = new string[layerCount]; - // Helper to convert from a sorting layer INDEX to a sorting layer ID. These are not the same thing. - // IDs are based on the order in which layers were created and do not change when reordering the layers. - // Thankfully there is a private helper we can call to get the ID for a layer given its index. - public static int GetSortingLayerIDForIndex(int index) - { - if (_getSortingLayerUserIdMethod == null) + for (int i = 0; i < layerCount; i++) { - return 0; + layerNames[i] = SortingLayer.layers[i].name; } - return (int)_getSortingLayerUserIdMethod.Invoke(null, new object[] { index }); + return layerNames; } } -} \ No newline at end of file +} diff --git a/Scripts/Runtime/TMP_Character.cs b/Scripts/Runtime/TMP_Character.cs index 8fc161e..6b4cc83 100644 --- a/Scripts/Runtime/TMP_Character.cs +++ b/Scripts/Runtime/TMP_Character.cs @@ -28,6 +28,24 @@ public TMP_Character(uint unicode, Glyph glyph) m_ElementType = TextElementType.Character; this.unicode = unicode; + this.textAsset = null; + this.glyph = glyph; + this.glyphIndex = glyph.index; + this.scale = 1.0f; + } + + /// + /// Constructor for new character + /// + /// Unicode value. + /// The font asset to which this character belongs. + /// Glyph + public TMP_Character(uint unicode, TMP_FontAsset fontAsset, Glyph glyph) + { + m_ElementType = TextElementType.Character; + + this.unicode = unicode; + this.textAsset = fontAsset; this.glyph = glyph; this.glyphIndex = glyph.index; this.scale = 1.0f; @@ -43,6 +61,7 @@ internal TMP_Character(uint unicode, uint glyphIndex) m_ElementType = TextElementType.Character; this.unicode = unicode; + this.textAsset = null; this.glyph = null; this.glyphIndex = glyphIndex; this.scale = 1.0f; diff --git a/Scripts/Runtime/TMP_CharacterInfo.cs b/Scripts/Runtime/TMP_CharacterInfo.cs index 8e1f005..7747cbd 100644 --- a/Scripts/Runtime/TMP_CharacterInfo.cs +++ b/Scripts/Runtime/TMP_CharacterInfo.cs @@ -20,7 +20,7 @@ public struct TMP_Vertex } /// - /// + /// /// public struct TMP_Offset { @@ -37,7 +37,7 @@ public struct TMP_Offset public float vertical { get { return m_Top; } set { m_Top = value; m_Bottom = value; } } /// - /// + /// /// public static TMP_Offset zero { get { return k_ZeroOffset; } } @@ -53,7 +53,7 @@ public struct TMP_Offset static readonly TMP_Offset k_ZeroOffset = new TMP_Offset(0F, 0F, 0F, 0F); /// - /// + /// /// /// /// @@ -68,7 +68,7 @@ public TMP_Offset(float left, float right, float top, float bottom) } /// - /// + /// /// /// /// @@ -116,7 +116,7 @@ public bool Equals(TMP_Offset other) /// - /// + /// /// public struct HighlightState { @@ -193,12 +193,15 @@ public struct TMP_CharacterInfo public Vector3 bottomLeft; public Vector3 topRight; public Vector3 bottomRight; + public float origin; + public float xAdvance; public float ascender; public float baseLine; public float descender; + internal float adjustedAscender; + internal float adjustedDescender; - public float xAdvance; public float aspectRatio; public float scale; public Color32 color; diff --git a/Scripts/Runtime/TMP_FontAsset.cs b/Scripts/Runtime/TMP_FontAsset.cs index 49b0df7..d9e8536 100644 --- a/Scripts/Runtime/TMP_FontAsset.cs +++ b/Scripts/Runtime/TMP_FontAsset.cs @@ -4,11 +4,9 @@ using UnityEngine.TextCore; using UnityEngine.TextCore.LowLevel; using UnityEngine.Profiling; -using System.Collections; using System.Collections.Generic; -using System.Diagnostics.Contracts; using System.Linq; -using UnityEditor; + #if UNITY_EDITOR && UNITY_2018_4_OR_NEWER #if !(UNITY_2018_4_0 || UNITY_2018_4_1 || UNITY_2018_4_2 || UNITY_2018_4_3 || UNITY_2018_4_4) using UnityEditor.TextCore.LowLevel; @@ -406,8 +404,7 @@ public TMP_FontWeightPair[] fontWeightTable public byte tabSize = 10; - private byte m_oldTabSize; - internal bool m_IsFontAssetLookupTablesDirty = false; + internal bool IsFontAssetLookupTablesDirty; /// /// Create new instance of a font asset using default settings. @@ -541,7 +538,7 @@ public void ReadFontAssetDefinition() { Profiler.BeginSample("TMP.ReadFontAssetDefinition"); - //Debug.Log("Reading Font Definition for " + this.name + "."); + //Debug.Log("Reading Font Asset 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)) @@ -577,7 +574,7 @@ public void ReadFontAssetDefinition() // Add reference to font asset in TMP Resource Manager //TMP_ResourceManager.AddFontAsset(this); - m_IsFontAssetLookupTablesDirty = false; + IsFontAssetLookupTablesDirty = false; Profiler.EndSample(); } @@ -645,7 +642,7 @@ internal void InitializeCharacterLookupDictionary() else m_CharacterLookupDictionary.Clear(); - // Add the characters contained in the character table into the dictionary for faster lookup. + // Add the characters contained in the character table to the dictionary for faster lookup. for (int i = 0; i < m_CharacterTable.Count; i++) { TMP_Character character = m_CharacterTable[i]; @@ -653,14 +650,20 @@ internal void InitializeCharacterLookupDictionary() uint unicode = character.unicode; uint glyphIndex = character.glyphIndex; + // Add character along with reference to text asset and glyph if (m_CharacterLookupDictionary.ContainsKey(unicode) == false) - m_CharacterLookupDictionary.Add(unicode, character); - - if (m_GlyphLookupDictionary.ContainsKey(glyphIndex)) { + m_CharacterLookupDictionary.Add(unicode, character); + character.textAsset = this; character.glyph = m_GlyphLookupDictionary[glyphIndex]; } } + + // Clear internal fallback references + if (FallbackSearchQueryLookup == null) + FallbackSearchQueryLookup = new HashSet(); + else + FallbackSearchQueryLookup.Clear(); } internal void InitializeGlyphPaidAdjustmentRecordsLookupDictionary() @@ -771,10 +774,12 @@ void AddSynthesizedCharacter(uint unicode, bool addImmediately = false) //Debug.Log("Adding Unicode [" + unicode.ToString("X4") + "]."); - GlyphLoadFlags glyphLoadFlags = ((GlyphRasterModes)m_AtlasRenderMode & GlyphRasterModes.RASTER_MODE_NO_HINTING) == GlyphRasterModes.RASTER_MODE_NO_HINTING ? GlyphLoadFlags.LOAD_NO_BITMAP | GlyphLoadFlags.LOAD_NO_HINTING : GlyphLoadFlags.LOAD_NO_BITMAP; + GlyphLoadFlags glyphLoadFlags = ((GlyphRasterModes)m_AtlasRenderMode & GlyphRasterModes.RASTER_MODE_NO_HINTING) == GlyphRasterModes.RASTER_MODE_NO_HINTING + ? GlyphLoadFlags.LOAD_NO_BITMAP | GlyphLoadFlags.LOAD_NO_HINTING + : GlyphLoadFlags.LOAD_NO_BITMAP; if (FontEngine.TryGetGlyphWithUnicodeValue(unicode, glyphLoadFlags, out glyph)) - m_CharacterLookupDictionary.Add(unicode, new TMP_Character(unicode, glyph)); + m_CharacterLookupDictionary.Add(unicode, new TMP_Character(unicode, this, glyph)); return; } @@ -784,7 +789,17 @@ void AddSynthesizedCharacter(uint unicode, bool addImmediately = false) // Synthesize and add missing glyph and character glyph = new Glyph(0, new GlyphMetrics(0, 0, 0, 0, 0), GlyphRect.zero, 1.0f, 0); - m_CharacterLookupDictionary.Add(unicode, new TMP_Character(unicode, glyph)); + m_CharacterLookupDictionary.Add(unicode, new TMP_Character(unicode, this, glyph)); + } + + internal HashSet FallbackSearchQueryLookup = new HashSet(); + + internal void AddCharacterToLookupCache(uint unicode, TMP_Character character) + { + m_CharacterLookupDictionary.Add(unicode, character); + + // Add font asset to fallback references. + FallbackSearchQueryLookup.Add(character.textAsset.instanceID); } /// @@ -1434,7 +1449,10 @@ public bool TryAddCharacters(uint[] unicodes, out uint[] missingUnicodes, bool i // 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)) { + // Add a reference to the source text asset and glyph character.glyph = m_GlyphLookupDictionary[glyphIndex]; + character.textAsset = this; + m_CharacterTable.Add(character); m_CharacterLookupDictionary.Add(unicode, character); continue; @@ -1498,7 +1516,10 @@ public bool TryAddCharacters(uint[] unicodes, out uint[] missingUnicodes, bool i continue; } + // Add a reference to the source text asset and glyph character.glyph = glyph; + character.textAsset = this; + m_CharacterTable.Add(character); m_CharacterLookupDictionary.Add(character.unicode, character); @@ -1646,7 +1667,10 @@ public bool TryAddCharacters(string characters, out string missingCharacters, bo // 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)) { + // Add a reference to the source text asset and glyph character.glyph = m_GlyphLookupDictionary[glyphIndex]; + character.textAsset = this; + m_CharacterTable.Add(character); m_CharacterLookupDictionary.Add(unicode, character); continue; @@ -1710,7 +1734,10 @@ public bool TryAddCharacters(string characters, out string missingCharacters, bo continue; } + // Add a reference to the source text asset and glyph character.glyph = glyph; + character.textAsset = this; + m_CharacterTable.Add(character); m_CharacterLookupDictionary.Add(character.unicode, character); @@ -1951,7 +1978,7 @@ internal bool TryAddCharacterInternal(uint unicode, out TMP_Character character) // 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]); + character = new TMP_Character(unicode, this, m_GlyphLookupDictionary[glyphIndex]); m_CharacterTable.Add(character); m_CharacterLookupDictionary.Add(unicode, character); @@ -2030,7 +2057,7 @@ internal bool TryAddCharacterInternal(uint unicode, out TMP_Character character) m_GlyphLookupDictionary.Add(glyphIndex, glyph); // Add new character - character = new TMP_Character(unicode, glyph); + character = new TMP_Character(unicode, this, glyph); m_CharacterTable.Add(character); m_CharacterLookupDictionary.Add(unicode, character); @@ -2073,7 +2100,7 @@ internal bool TryAddCharacterInternal(uint unicode, out TMP_Character character) m_GlyphLookupDictionary.Add(glyphIndex, glyph); // Add new character - character = new TMP_Character(unicode, glyph); + character = new TMP_Character(unicode, this, glyph); m_CharacterTable.Add(character); m_CharacterLookupDictionary.Add(unicode, character); @@ -2151,7 +2178,7 @@ internal bool TryGetCharacter_and_QueueRenderToTexture(uint unicode, out TMP_Cha // 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]); + character = new TMP_Character(unicode, this, m_GlyphLookupDictionary[glyphIndex]); m_CharacterTable.Add(character); m_CharacterLookupDictionary.Add(unicode, character); @@ -2171,7 +2198,10 @@ internal bool TryGetCharacter_and_QueueRenderToTexture(uint unicode, out TMP_Cha return true; } - GlyphLoadFlags glyphLoadFlags = ((GlyphRasterModes)m_AtlasRenderMode & GlyphRasterModes.RASTER_MODE_NO_HINTING) == GlyphRasterModes.RASTER_MODE_NO_HINTING ? GlyphLoadFlags.LOAD_NO_BITMAP | GlyphLoadFlags.LOAD_NO_HINTING : GlyphLoadFlags.LOAD_NO_BITMAP; + GlyphLoadFlags glyphLoadFlags = (GlyphRasterModes.RASTER_MODE_NO_HINTING & (GlyphRasterModes)m_AtlasRenderMode) == GlyphRasterModes.RASTER_MODE_NO_HINTING + ? GlyphLoadFlags.LOAD_NO_BITMAP | GlyphLoadFlags.LOAD_NO_HINTING + : GlyphLoadFlags.LOAD_NO_BITMAP; + Glyph glyph = null; if (FontEngine.TryGetGlyphWithIndexValue(glyphIndex, glyphLoadFlags, out glyph)) @@ -2181,7 +2211,7 @@ internal bool TryGetCharacter_and_QueueRenderToTexture(uint unicode, out TMP_Cha m_GlyphLookupDictionary.Add(glyphIndex, glyph); // Add new character - character = new TMP_Character(unicode, glyph); + character = new TMP_Character(unicode, this, glyph); m_CharacterTable.Add(character); m_CharacterLookupDictionary.Add(unicode, character); @@ -2318,7 +2348,10 @@ bool TryAddGlyphsToNewAtlasTexture() continue; } + // Add a reference to the source text asset and glyph character.glyph = glyph; + character.textAsset = this; + m_CharacterTable.Add(character); m_CharacterLookupDictionary.Add(character.unicode, character); @@ -2656,6 +2689,8 @@ public void ClearFontAssetData(bool setAtlasSizeToZero = false) ReadFontAssetDefinition(); + //TMP_ResourceManager.RebuildFontAssetCache(instanceID); + #if UNITY_EDITOR // Makes the changes to the font asset persistent. TMP_EditorResourceManager.RegisterResourceForUpdate(this); @@ -2685,6 +2720,8 @@ internal void UpdateFontAssetData() ReadFontAssetDefinition(); + //TMP_ResourceManager.RebuildFontAssetCache(instanceID); + // Add glyphs TryAddCharacters(unicodeCharacters, true); @@ -2928,7 +2965,7 @@ internal void UpgradeFontAsset() m_GlyphTable.Add(glyph); - TMP_Character character = new TMP_Character((uint)oldGlyph.id, glyph); + TMP_Character character = new TMP_Character((uint)oldGlyph.id, this, glyph); if (oldGlyph.id == 32) isSpaceCharacterPresent = true; @@ -2942,7 +2979,7 @@ internal void UpgradeFontAsset() 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)); + m_CharacterTable.Add(new TMP_Character(32, this, glyph)); } // Clear legacy glyph info list. diff --git a/Scripts/Runtime/TMP_FontAssetUtilities.cs b/Scripts/Runtime/TMP_FontAssetUtilities.cs index bd501c1..a0e6439 100644 --- a/Scripts/Runtime/TMP_FontAssetUtilities.cs +++ b/Scripts/Runtime/TMP_FontAssetUtilities.cs @@ -25,9 +25,9 @@ public static TMP_FontAssetUtilities instance /// - /// List containing instance ID of font assets already searched. + /// HashSet containing instance ID of font assets already searched. /// - private static List k_SearchedFontAssets; + private static HashSet k_SearchedAssets; /// @@ -41,20 +41,20 @@ public static TMP_FontAssetUtilities instance /// Include the fallback font assets in the search /// The font style /// The font weight - /// Indicates if the OUT font asset is an alternative typeface or fallback font asset + /// Indicates if the OUT font asset is an alternative typeface or fallback font asset /// The font asset that contains the requested character /// - public static TMP_Character GetCharacterFromFontAsset(uint unicode, TMP_FontAsset sourceFontAsset, bool includeFallbacks, FontStyles fontStyle, FontWeight fontWeight, out bool isAlternativeTypeface, out TMP_FontAsset fontAsset) + public static TMP_Character GetCharacterFromFontAsset(uint unicode, TMP_FontAsset sourceFontAsset, bool includeFallbacks, FontStyles fontStyle, FontWeight fontWeight, out bool isAlternativeTypeface) { if (includeFallbacks) { - if (k_SearchedFontAssets == null) - k_SearchedFontAssets = new List(); + if (k_SearchedAssets == null) + k_SearchedAssets = new HashSet(); else - k_SearchedFontAssets.Clear(); + k_SearchedAssets.Clear(); } - return GetCharacterFromFontAsset_Internal(unicode, sourceFontAsset, includeFallbacks, fontStyle, fontWeight, out isAlternativeTypeface, out fontAsset); + return GetCharacterFromFontAsset_Internal(unicode, sourceFontAsset, includeFallbacks, fontStyle, fontWeight, out isAlternativeTypeface); } @@ -62,11 +62,10 @@ public static TMP_Character GetCharacterFromFontAsset(uint unicode, TMP_FontAsse /// 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. /// - private static TMP_Character GetCharacterFromFontAsset_Internal(uint unicode, TMP_FontAsset sourceFontAsset, bool includeFallbacks, FontStyles fontStyle, FontWeight fontWeight, out bool isAlternativeTypeface, out TMP_FontAsset fontAsset) + private static TMP_Character GetCharacterFromFontAsset_Internal(uint unicode, TMP_FontAsset sourceFontAsset, bool includeFallbacks, FontStyles fontStyle, FontWeight fontWeight, out bool isAlternativeTypeface) { - fontAsset = null; isAlternativeTypeface = false; - TMP_Character characterData = null; + TMP_Character character = null; #region FONT WEIGHT AND FONT STYLE HANDLING // Determine if a font weight or style is used. If so check if an alternative typeface is assigned for the given weight and / or style. @@ -109,24 +108,24 @@ private static TMP_Character GetCharacterFromFontAsset_Internal(uint unicode, TM break; } - fontAsset = isItalic ? fontWeights[fontWeightIndex].italicTypeface : fontWeights[fontWeightIndex].regularTypeface; + TMP_FontAsset temp = isItalic ? fontWeights[fontWeightIndex].italicTypeface : fontWeights[fontWeightIndex].regularTypeface; - if (fontAsset != null) + if (temp != null) { - if (fontAsset.characterLookupTable.TryGetValue(unicode, out characterData)) + if (temp.characterLookupTable.TryGetValue(unicode, out character)) { isAlternativeTypeface = true; - return characterData; + return character; } - if (fontAsset.atlasPopulationMode == AtlasPopulationMode.Dynamic) + if (temp.atlasPopulationMode == AtlasPopulationMode.Dynamic) { - if (fontAsset.TryAddCharacterInternal(unicode, out characterData)) + if (temp.TryAddCharacterInternal(unicode, out character)) { isAlternativeTypeface = true; - return characterData; + return character; } // Check if the source font file contains the requested character. @@ -152,28 +151,17 @@ private static TMP_Character GetCharacterFromFontAsset_Internal(uint unicode, TM #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; - } + if (sourceFontAsset.characterLookupTable.TryGetValue(unicode, out character)) + return character; if (sourceFontAsset.atlasPopulationMode == AtlasPopulationMode.Dynamic) { - if (sourceFontAsset.TryAddCharacterInternal(unicode, out characterData)) - { - fontAsset = sourceFontAsset; - - return characterData; - } - - // Unable to add character and glyph to font asset + if (sourceFontAsset.TryAddCharacterInternal(unicode, out character)) + return character; } // Search fallback font assets if we still don't have a valid character and include fallback is set to true. - if (characterData == null && includeFallbacks && sourceFontAsset.fallbackFontAssetTable != null) + if (character == null && includeFallbacks && sourceFontAsset.fallbackFontAssetTable != null) { // Get reference to the list of fallback font assets. List fallbackFontAssets = sourceFontAsset.fallbackFontAssetTable; @@ -181,30 +169,28 @@ private static TMP_Character GetCharacterFromFontAsset_Internal(uint unicode, TM if (fallbackFontAssets != null && fallbackCount > 0) { - for (int i = 0; i < fallbackCount && characterData == null; i++) + for (int i = 0; i < fallbackCount; i++) { TMP_FontAsset temp = fallbackFontAssets[i]; - if (temp == null) continue; + if (temp == null) + continue; - int id = temp.GetInstanceID(); + int id = temp.instanceID; - // Skip over the fallback font asset in the event it is null or if already searched. - if (k_SearchedFontAssets.Contains(id)) + // Try adding font asset to search list. If already present skip to the next one otherwise check if it contains the requested character. + if (k_SearchedAssets.Add(id) == false) continue; - // Add to list of font assets already searched. - k_SearchedFontAssets.Add(id); + // Add reference to this search query + sourceFontAsset.FallbackSearchQueryLookup.Add(id); - characterData = GetCharacterFromFontAsset_Internal(unicode, temp, includeFallbacks, fontStyle, fontWeight, out isAlternativeTypeface, out fontAsset); + character = GetCharacterFromFontAsset_Internal(unicode, temp, true, fontStyle, fontWeight, out isAlternativeTypeface); - if (characterData != null) - { - return characterData; - } + if (character != null) + return character; } } - } return null; @@ -218,45 +204,156 @@ private static TMP_Character GetCharacterFromFontAsset_Internal(uint unicode, TM /// The typeface type indicates whether the returned font asset is the source font asset, an alternative typeface or fallback font asset. /// /// The unicode value of the requested character + /// The font asset originating the search query /// The list of font assets to search /// Determines if the fallback of each font assets on the list will be searched /// The font style /// The font weight - /// Determines if the OUT font asset is an alternative typeface or fallback font asset - /// The font asset that contains the requested character + /// Determines if the OUT font asset is an alternative typeface or fallback font asset /// - public static TMP_Character GetCharacterFromFontAssets(uint unicode, List fontAssets, bool includeFallbacks, FontStyles fontStyle, FontWeight fontWeight, out bool isAlternativeTypeface, out TMP_FontAsset fontAsset) + public static TMP_Character GetCharacterFromFontAssets(uint unicode, TMP_FontAsset sourceFontAsset, List fontAssets, bool includeFallbacks, FontStyles fontStyle, FontWeight fontWeight, out bool isAlternativeTypeface) { isAlternativeTypeface = false; // Make sure font asset list is valid if (fontAssets == null || fontAssets.Count == 0) - { - fontAsset = null; return null; - } if (includeFallbacks) { - if (k_SearchedFontAssets == null) - k_SearchedFontAssets = new List(); + if (k_SearchedAssets == null) + k_SearchedAssets = new HashSet(); else - k_SearchedFontAssets.Clear(); + k_SearchedAssets.Clear(); } int fontAssetCount = fontAssets.Count; for (int i = 0; i < fontAssetCount; i++) { - if (fontAssets[i] == null) continue; + TMP_FontAsset fontAsset = fontAssets[i]; + + if (fontAsset == null) continue; - TMP_Character characterData = GetCharacterFromFontAsset_Internal(unicode, fontAssets[i], includeFallbacks, fontStyle, fontWeight, out isAlternativeTypeface, out fontAsset); + // Add reference to this search query + sourceFontAsset.FallbackSearchQueryLookup.Add(fontAsset.instanceID); - if (characterData != null) - return characterData; + TMP_Character character = GetCharacterFromFontAsset_Internal(unicode, fontAsset, includeFallbacks, fontStyle, fontWeight, out isAlternativeTypeface); + + if (character != null) + return character; } - fontAsset = null; + return null; + } + + // ===================================================================== + // SPRITE ASSET - Functions + // ===================================================================== + + /// + /// + /// + /// + /// + /// + /// + public static TMP_SpriteCharacter GetSpriteCharacterFromSpriteAsset(uint unicode, TMP_SpriteAsset spriteAsset, bool includeFallbacks) + { + // Make sure we have a valid sprite asset to search + if (spriteAsset == null) + return null; + + TMP_SpriteCharacter spriteCharacter; + + // Search sprite asset for potential sprite character for the given unicode value + if (spriteAsset.spriteCharacterLookupTable.TryGetValue(unicode, out spriteCharacter)) + return spriteCharacter; + + if (includeFallbacks) + { + // Clear searched assets + if (k_SearchedAssets == null) + k_SearchedAssets = new HashSet(); + else + k_SearchedAssets.Clear(); + + // Add current sprite asset to already searched assets. + k_SearchedAssets.Add(spriteAsset.instanceID); + + List fallbackSpriteAsset = spriteAsset.fallbackSpriteAssets; + + if (fallbackSpriteAsset != null && fallbackSpriteAsset.Count > 0) + { + int fallbackCount = fallbackSpriteAsset.Count; + + for (int i = 0; i < fallbackCount; i++) + { + TMP_SpriteAsset temp = fallbackSpriteAsset[i]; + + if (temp == null) + continue; + + int id = temp.instanceID; + + // Try adding asset to search list. If already present skip to the next one otherwise check if it contains the requested character. + if (k_SearchedAssets.Add(id) == false) + continue; + + spriteCharacter = GetSpriteCharacterFromSpriteAsset_Internal(unicode, temp, true); + + if (spriteCharacter != null) + return spriteCharacter; + } + } + } + + return null; + } + + /// + /// + /// + /// + /// + /// + /// + static TMP_SpriteCharacter GetSpriteCharacterFromSpriteAsset_Internal(uint unicode, TMP_SpriteAsset spriteAsset, bool includeFallbacks) + { + TMP_SpriteCharacter spriteCharacter; + + // Search sprite asset for potential sprite character for the given unicode value + if (spriteAsset.spriteCharacterLookupTable.TryGetValue(unicode, out spriteCharacter)) + return spriteCharacter; + + if (includeFallbacks) + { + List fallbackSpriteAsset = spriteAsset.fallbackSpriteAssets; + + if (fallbackSpriteAsset != null && fallbackSpriteAsset.Count > 0) + { + int fallbackCount = fallbackSpriteAsset.Count; + + for (int i = 0; i < fallbackCount; i++) + { + TMP_SpriteAsset temp = fallbackSpriteAsset[i]; + + if (temp == null) + continue; + + int id = temp.instanceID; + + // Try adding asset to search list. If already present skip to the next one otherwise check if it contains the requested character. + if (k_SearchedAssets.Add(id) == false) + continue; + + spriteCharacter = GetSpriteCharacterFromSpriteAsset_Internal(unicode, temp, true); + + if (spriteCharacter != null) + return spriteCharacter; + } + } + } return null; } diff --git a/Scripts/Runtime/TMP_InputField.cs b/Scripts/Runtime/TMP_InputField.cs index 04ef0a9..91db26f 100644 --- a/Scripts/Runtime/TMP_InputField.cs +++ b/Scripts/Runtime/TMP_InputField.cs @@ -1048,7 +1048,6 @@ protected override void OnEnable() { m_TextComponent.RegisterDirtyVerticesCallback(MarkGeometryAsDirty); m_TextComponent.RegisterDirtyVerticesCallback(UpdateLabel); - m_TextComponent.ignoreClipping = true; // Cache reference to Vertical Scrollbar RectTransform and add listener. if (m_VerticalScrollbar != null) @@ -1073,7 +1072,6 @@ protected override void OnDisable() { m_TextComponent.UnregisterDirtyVerticesCallback(MarkGeometryAsDirty); m_TextComponent.UnregisterDirtyVerticesCallback(UpdateLabel); - m_TextComponent.ignoreClipping = false; if (m_VerticalScrollbar != null) m_VerticalScrollbar.onValueChanged.RemoveListener(OnScrollbarValueChange); @@ -3941,7 +3939,7 @@ private void ActivateInputFieldInternal() if (TouchScreenKeyboard.isSupported && shouldHideSoftKeyboard == false) { - if (inputSystem.touchSupported) + if (inputSystem != null && inputSystem.touchSupported) { TouchScreenKeyboard.hideInput = shouldHideMobileInput; } @@ -3972,7 +3970,7 @@ private void ActivateInputFieldInternal() } else { - if (!TouchScreenKeyboard.isSupported && m_ReadOnly == false) + if (!TouchScreenKeyboard.isSupported && m_ReadOnly == false && inputSystem != null) inputSystem.imeCompositionMode = IMECompositionMode.On; OnFocus(); diff --git a/Scripts/Runtime/TMP_PackageResourceImporter.cs b/Scripts/Runtime/TMP_PackageResourceImporter.cs index 7d904c4..05d4b89 100644 --- a/Scripts/Runtime/TMP_PackageResourceImporter.cs +++ b/Scripts/Runtime/TMP_PackageResourceImporter.cs @@ -4,7 +4,7 @@ using System.IO; using UnityEngine; using UnityEditor; - + namespace TMPro { @@ -62,6 +62,9 @@ public void OnGUI() // Set flag to get around importing scripts as per of this package which results in an assembly reload which in turn prevents / clears any callbacks. m_IsImportingExamples = true; + // Disable AssetDatabase refresh until examples have been imported. + //AssetDatabase.DisallowAutoRefresh(); + var packageFullPath = GetPackageFullPath(); AssetDatabase.ImportPackage(packageFullPath + "/Package Resources/TMP Examples & Extras.unitypackage", false); } @@ -80,7 +83,7 @@ internal void RegisterResourceImportCallback() } /// - /// + /// /// /// void ImportCallback(string packageName) @@ -98,6 +101,7 @@ void ImportCallback(string packageName) { m_ExamplesAndExtrasResourcesImported = true; m_IsImportingExamples = false; + //AssetDatabase.AllowAutoRefresh(); } Debug.Log("[" + packageName + "] have been imported."); @@ -199,7 +203,7 @@ void OnInspectorUpdate() { Repaint(); } - + /// /// Limits the minimum size of the editor window. /// diff --git a/Scripts/Runtime/TMP_ResourcesManager.cs b/Scripts/Runtime/TMP_ResourcesManager.cs index 817a58d..55f2f5d 100644 --- a/Scripts/Runtime/TMP_ResourcesManager.cs +++ b/Scripts/Runtime/TMP_ResourcesManager.cs @@ -6,13 +6,10 @@ namespace TMPro { /// - /// + /// /// public class TMP_ResourceManager { - private static TMP_ResourceManager instance { get { return s_instance; } } - - private static Dictionary s_FontAssetReferenceLookup = new Dictionary(); private static readonly TMP_ResourceManager s_instance = new TMP_ResourceManager(); static TMP_ResourceManager() { } @@ -21,8 +18,11 @@ static TMP_ResourceManager() { } // FONT ASSET MANAGEMENT - Fields, Properties and Functions // ====================================================== + private static readonly List s_FontAssetReferences = new List(); + private static readonly Dictionary s_FontAssetReferenceLookup = new Dictionary(); + /// - /// + /// /// /// public static void AddFontAsset(TMP_FontAsset fontAsset) @@ -32,11 +32,12 @@ public static void AddFontAsset(TMP_FontAsset fontAsset) if (s_FontAssetReferenceLookup.ContainsKey(hashcode)) return; + s_FontAssetReferences.Add(fontAsset); s_FontAssetReferenceLookup.Add(hashcode, fontAsset); } /// - /// + /// /// /// /// @@ -45,10 +46,20 @@ public static bool TryGetFontAsset(int hashcode, out TMP_FontAsset fontAsset) { fontAsset = null; - if (s_FontAssetReferenceLookup.TryGetValue(hashcode, out fontAsset)) - return true; + return s_FontAssetReferenceLookup.TryGetValue(hashcode, out fontAsset); + } + + + internal static void RebuildFontAssetCache(int instanceID) + { + // Iterate over loaded font assets to update affected font assets + for (int i = 0; i < s_FontAssetReferences.Count; i++) + { + TMP_FontAsset fontAsset = s_FontAssetReferences[i]; - return false; + if (fontAsset.FallbackSearchQueryLookup.Contains(instanceID)) + fontAsset.ReadFontAssetDefinition(); + } } } } diff --git a/Scripts/Runtime/TMP_Settings.cs b/Scripts/Runtime/TMP_Settings.cs index 9dbfaa4..c7ab798 100644 --- a/Scripts/Runtime/TMP_Settings.cs +++ b/Scripts/Runtime/TMP_Settings.cs @@ -251,6 +251,17 @@ public static bool enableEmojiSupport [SerializeField] private bool m_enableEmojiSupport; + /// + /// The unicode value of the sprite that will be used when the requested sprite is missing from the sprite asset and potential fallbacks. + /// + public static uint missingCharacterSpriteUnicode + { + get { return instance.m_MissingCharacterSpriteUnicode; } + set { instance.m_MissingCharacterSpriteUnicode = value; } + } + [SerializeField] + private uint m_MissingCharacterSpriteUnicode; + /// /// Determines if sprites will be scaled relative to the primary font asset assigned to the text object or relative to the current font asset. /// @@ -313,7 +324,7 @@ public static TextAsset followingCharacters private TextAsset m_followingCharacters; /// - /// + /// /// public static LineBreakingTable linebreakingRules { diff --git a/Scripts/Runtime/TMP_ShaderUtilities.cs b/Scripts/Runtime/TMP_ShaderUtilities.cs index e2b224d..edd789f 100644 --- a/Scripts/Runtime/TMP_ShaderUtilities.cs +++ b/Scripts/Runtime/TMP_ShaderUtilities.cs @@ -11,21 +11,21 @@ public static class ShaderUtilities public static int ID_MainTex; public static int ID_FaceTex; - public static int ID_FaceColor; + 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_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_WeightNormal; public static int ID_WeightBold; public static int ID_OutlineTex; - public static int ID_OutlineWidth; + public static int ID_OutlineWidth; public static int ID_OutlineSoftness; public static int ID_OutlineColor; @@ -33,20 +33,20 @@ public static class ShaderUtilities public static int ID_Outline2Width; public static int ID_Padding; - public static int ID_GradientScale; - public static int ID_ScaleX; - public static int ID_ScaleY; + public static int ID_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_TextureWidth; + public static int ID_TextureHeight; - public static int ID_BevelAmount; + public static int ID_BevelAmount; - public static int ID_GlowColor; + public static int ID_GlowColor; public static int ID_GlowOffset; - public static int ID_GlowPower; + public static int ID_GlowPower; public static int ID_GlowOuter; public static int ID_GlowInner; @@ -58,10 +58,10 @@ public static class ShaderUtilities //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_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; @@ -70,12 +70,12 @@ public static class ShaderUtilities public static int ID_StencilComp; public static int ID_StencilReadMask; public static int ID_StencilWriteMask; - - public static int ID_ShaderFlags; + + 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"; @@ -125,7 +125,7 @@ internal static Shader ShaderRef_MobileBitmap /// - /// + /// /// static ShaderUtilities() { @@ -133,7 +133,7 @@ static ShaderUtilities() } /// - /// + /// /// public static void GetShaderPropertyIDs() { @@ -148,7 +148,7 @@ public static void GetShaderPropertyIDs() 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"); @@ -222,7 +222,7 @@ public static void GetShaderPropertyIDs() - // Scale Ratios to ensure property ranges are optimum in Material Editor + // Scale Ratios to ensure property ranges are optimum in Material Editor public static void UpdateShaderRatios(Material mat) { //Debug.Log("UpdateShaderRatios() called."); @@ -233,6 +233,9 @@ public static void UpdateShaderRatios(Material mat) bool isRatioEnabled = !mat.shaderKeywords.Contains(Keyword_Ratios); + if (!mat.HasProperty(ID_GradientScale) || !mat.HasProperty(ID_FaceDilate)) + return; + // Compute Ratio A float scale = mat.GetFloat(ID_GradientScale); float faceDilate = mat.GetFloat(ID_FaceDilate); @@ -301,7 +304,7 @@ public static Vector4 GetFontExtent(Material material) /* 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; @@ -346,7 +349,7 @@ public static float GetPadding(Material material, bool enableExtraPadding, bool if (material.HasProperty(ID_Padding)) extraPadding += (int)material.GetFloat(ID_Padding); - return extraPadding; + return extraPadding + 1.0f; } Vector4 padding = Vector4.zero; @@ -365,7 +368,7 @@ public static float GetPadding(Material material, bool enableExtraPadding, bool 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); @@ -448,19 +451,17 @@ public static float GetPadding(Material material, bool enableExtraPadding, bool } - - // 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 diff --git a/Scripts/Runtime/TMP_SpriteAsset.cs b/Scripts/Runtime/TMP_SpriteAsset.cs index 24a1e6d..55b1b17 100644 --- a/Scripts/Runtime/TMP_SpriteAsset.cs +++ b/Scripts/Runtime/TMP_SpriteAsset.cs @@ -9,7 +9,6 @@ namespace TMPro public class TMP_SpriteAsset : TMP_Asset { - internal Dictionary m_UnicodeLookup; internal Dictionary m_NameLookup; internal Dictionary m_GlyphIndexLookup; @@ -39,6 +38,9 @@ public FaceInfo faceInfo // The texture which contains the sprites. public Texture spriteSheet; + /// + /// + /// public List spriteCharacterTable { get @@ -54,6 +56,22 @@ public List spriteCharacterTable private List m_SpriteCharacterTable = new List(); + /// + /// Dictionary used to lookup sprite characters by their unicode value. + /// + public Dictionary spriteCharacterLookupTable + { + get + { + if (m_SpriteCharacterLookup == null) + UpdateLookupTables(); + + return m_SpriteCharacterLookup; + } + internal set { m_SpriteCharacterLookup = value; } + } + internal Dictionary m_SpriteCharacterLookup; + public List spriteGlyphTable { get { return m_SpriteGlyphTable; } @@ -62,15 +80,11 @@ public List spriteGlyphTable [SerializeField] private List m_SpriteGlyphTable = new List(); + internal Dictionary m_SpriteGlyphLookup; + // List which contains the SpriteInfo for the sprites contained in the sprite sheet. public List spriteInfoList; - /// - /// Dictionary used to lookup the index of a given sprite based on a Unicode value. - /// - //private Dictionary m_SpriteUnicodeLookup; - - /// /// List which contains the Fallback font assets for this font. /// @@ -79,6 +93,7 @@ public List spriteGlyphTable internal bool m_IsSpriteAssetLookupTablesDirty = false; + void Awake() { // Check version number of sprite asset to see if it needs to be upgraded. @@ -87,21 +102,6 @@ void Awake() } - #if UNITY_EDITOR - /// - /// - /// - void OnValidate() - { - //Debug.Log("Sprite Asset [" + name + "] has changed."); - - //UpdateLookupTables(); - - //TMPro_EventManager.ON_SPRITE_ASSET_PROPERTY_CHANGED(true, this); - } - #endif - - /// /// Create a material for the sprite asset. /// @@ -145,26 +145,58 @@ public void UpdateLookupTables() else m_GlyphIndexLookup.Clear(); + // + if (m_SpriteGlyphLookup == null) + m_SpriteGlyphLookup = new Dictionary(); + else + m_SpriteGlyphLookup.Clear(); + + // Initialize SpriteGlyphLookup for (int i = 0; i < m_SpriteGlyphTable.Count; i++) { - uint glyphIndex = m_SpriteGlyphTable[i].index; + TMP_SpriteGlyph spriteGlyph = m_SpriteGlyphTable[i]; + uint glyphIndex = spriteGlyph.index; if (m_GlyphIndexLookup.ContainsKey(glyphIndex) == false) m_GlyphIndexLookup.Add(glyphIndex, i); + + if (m_SpriteGlyphLookup.ContainsKey(glyphIndex) == false) + m_SpriteGlyphLookup.Add(glyphIndex, spriteGlyph); } + // Initialize name lookup if (m_NameLookup == null) m_NameLookup = new Dictionary(); else m_NameLookup.Clear(); - if (m_UnicodeLookup == null) - m_UnicodeLookup = new Dictionary(); + + // Initialize character lookup + if (m_SpriteCharacterLookup == null) + m_SpriteCharacterLookup = new Dictionary(); else - m_UnicodeLookup.Clear(); + m_SpriteCharacterLookup.Clear(); + + // Populate Sprite Character lookup tables for (int i = 0; i < m_SpriteCharacterTable.Count; i++) { + TMP_SpriteCharacter spriteCharacter = m_SpriteCharacterTable[i]; + + // Make sure sprite character is valid + if (spriteCharacter == null) + continue; + + uint glyphIndex = spriteCharacter.glyphIndex; + + // Lookup the glyph for this character + if (m_SpriteGlyphLookup.ContainsKey(glyphIndex) == false) + continue; + + // Assign glyph and text asset to this character + spriteCharacter.glyph = m_SpriteGlyphLookup[glyphIndex]; + spriteCharacter.textAsset = this; + int nameHashCode = m_SpriteCharacterTable[i].hashCode; if (m_NameLookup.ContainsKey(nameHashCode) == false) @@ -172,15 +204,8 @@ public void UpdateLookupTables() 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; - int index; - - if (m_GlyphIndexLookup.TryGetValue(glyphIndex, out index)) - m_SpriteCharacterTable[i].glyph = m_SpriteGlyphTable[index]; + if (unicode != 0xFFFE && m_SpriteCharacterLookup.ContainsKey(unicode) == false) + m_SpriteCharacterLookup.Add(unicode, spriteCharacter); } m_IsSpriteAssetLookupTablesDirty = false; @@ -213,13 +238,13 @@ public int GetSpriteIndexFromHashcode(int hashCode) /// public int GetSpriteIndexFromUnicode (uint unicode) { - if (m_UnicodeLookup == null) + if (m_SpriteCharacterLookup == null) UpdateLookupTables(); - int index; + TMP_SpriteCharacter spriteCharacter; - if (m_UnicodeLookup.TryGetValue(unicode, out index)) - return index; + if (m_SpriteCharacterLookup.TryGetValue(unicode, out spriteCharacter)) + return (int)spriteCharacter.glyphIndex; return -1; } @@ -244,7 +269,7 @@ public int GetSpriteIndexFromName (string name) /// /// Used to keep track of which Sprite Assets have been searched. /// - private static List k_searchedSpriteAssets; + private static HashSet k_searchedSpriteAssets; /// /// Search through the given sprite asset and its fallbacks for the specified sprite matching the given unicode character. @@ -265,9 +290,9 @@ public static TMP_SpriteAsset SearchForSpriteByUnicode(TMP_SpriteAsset spriteAss // Initialize list to track instance of Sprite Assets that have already been searched. if (k_searchedSpriteAssets == null) - k_searchedSpriteAssets = new List(); - - k_searchedSpriteAssets.Clear(); + k_searchedSpriteAssets = new HashSet(); + else + k_searchedSpriteAssets.Clear(); // Get instance ID of sprite asset and add to list. int id = spriteAsset.GetInstanceID(); @@ -275,11 +300,11 @@ public static TMP_SpriteAsset SearchForSpriteByUnicode(TMP_SpriteAsset spriteAss // 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); + return SearchForSpriteByUnicodeInternal(spriteAsset.fallbackSpriteAssets, unicode, true, out spriteIndex); // Search default sprite asset potentially assigned in the TMP Settings. if (includeFallbacks && TMP_Settings.defaultSpriteAsset != null) - return SearchForSpriteByUnicodeInternal(TMP_Settings.defaultSpriteAsset, unicode, includeFallbacks, out spriteIndex); + return SearchForSpriteByUnicodeInternal(TMP_Settings.defaultSpriteAsset, unicode, true, out spriteIndex); spriteIndex = -1; return null; @@ -303,11 +328,9 @@ private static TMP_SpriteAsset SearchForSpriteByUnicodeInternal(List 0) - return SearchForSpriteByUnicodeInternal(spriteAsset.fallbackSpriteAssets, unicode, includeFallbacks, out spriteIndex); + return SearchForSpriteByUnicodeInternal(spriteAsset.fallbackSpriteAssets, unicode, true, out spriteIndex); spriteIndex = -1; return null; @@ -360,22 +384,66 @@ public static TMP_SpriteAsset SearchForSpriteByHashCode(TMP_SpriteAsset spriteAs if (spriteIndex != -1) return spriteAsset; - // Initialize list to track instance of Sprite Assets that have already been searched. + // Initialize or clear list to Sprite Assets that have already been searched. if (k_searchedSpriteAssets == null) - k_searchedSpriteAssets = new List(); + k_searchedSpriteAssets = new HashSet(); + else + k_searchedSpriteAssets.Clear(); - k_searchedSpriteAssets.Clear(); + int id = spriteAsset.instanceID; - int id = spriteAsset.GetInstanceID(); // Add to list of font assets already searched. k_searchedSpriteAssets.Add(id); + TMP_SpriteAsset tempSpriteAsset; + + // Search potential fallbacks assigned to local sprite asset. if (includeFallbacks && spriteAsset.fallbackSpriteAssets != null && spriteAsset.fallbackSpriteAssets.Count > 0) - return SearchForSpriteByHashCodeInternal(spriteAsset.fallbackSpriteAssets, hashCode, includeFallbacks, out spriteIndex); + { + tempSpriteAsset = SearchForSpriteByHashCodeInternal(spriteAsset.fallbackSpriteAssets, hashCode, true, out spriteIndex); + + if (spriteIndex != -1) + return tempSpriteAsset; + } // Search default sprite asset potentially assigned in the TMP Settings. if (includeFallbacks && TMP_Settings.defaultSpriteAsset != null) - return SearchForSpriteByHashCodeInternal(TMP_Settings.defaultSpriteAsset, hashCode, includeFallbacks, out spriteIndex); + { + tempSpriteAsset = SearchForSpriteByHashCodeInternal(TMP_Settings.defaultSpriteAsset, hashCode, true, out spriteIndex); + + if (spriteIndex != -1) + return tempSpriteAsset; + } + + // Clear search list since we are now looking for the missing sprite character. + k_searchedSpriteAssets.Clear(); + + uint missingSpriteCharacterUnicode = TMP_Settings.missingCharacterSpriteUnicode; + + // Get sprite index for the given unicode + spriteIndex = spriteAsset.GetSpriteIndexFromUnicode(missingSpriteCharacterUnicode); + if (spriteIndex != -1) + return spriteAsset; + + // Add current sprite asset to list of assets already searched. + k_searchedSpriteAssets.Add(id); + + // Search for the missing sprite character in the local sprite asset and potential fallbacks. + if (includeFallbacks && spriteAsset.fallbackSpriteAssets != null && spriteAsset.fallbackSpriteAssets.Count > 0) + { + tempSpriteAsset = SearchForSpriteByUnicodeInternal(spriteAsset.fallbackSpriteAssets, missingSpriteCharacterUnicode, true, out spriteIndex); + + if (spriteIndex != -1) + return tempSpriteAsset; + } + + // Search for the missing sprite character in the default sprite asset and potential fallbacks. + if (includeFallbacks && TMP_Settings.defaultSpriteAsset != null) + { + tempSpriteAsset = SearchForSpriteByUnicodeInternal(TMP_Settings.defaultSpriteAsset, missingSpriteCharacterUnicode, true, out spriteIndex); + if (spriteIndex != -1) + return tempSpriteAsset; + } spriteIndex = -1; return null; @@ -398,13 +466,11 @@ private static TMP_SpriteAsset SearchForSpriteByHashCodeInternal(List 0) - return SearchForSpriteByHashCodeInternal(spriteAsset.fallbackSpriteAssets, hashCode, searchFallbacks, out spriteIndex); + return SearchForSpriteByHashCodeInternal(spriteAsset.fallbackSpriteAssets, hashCode, true, out spriteIndex); spriteIndex = -1; return null; @@ -487,7 +553,7 @@ private void UpgradeSpriteAsset() TMP_Sprite oldSprite = spriteInfoList[i]; TMP_SpriteGlyph spriteGlyph = new TMP_SpriteGlyph(); - spriteGlyph.index = (uint)i; + 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); @@ -497,7 +563,9 @@ private void UpgradeSpriteAsset() m_SpriteGlyphTable.Add(spriteGlyph); - TMP_SpriteCharacter spriteCharacter = new TMP_SpriteCharacter((uint)oldSprite.unicode, spriteGlyph); + TMP_SpriteCharacter spriteCharacter = new TMP_SpriteCharacter(); + spriteCharacter.glyph = spriteGlyph; + spriteCharacter.unicode = oldSprite.unicode == 0x0 ? 0xFFFE : (uint)oldSprite.unicode; spriteCharacter.name = oldSprite.name; spriteCharacter.scale = oldSprite.scale; diff --git a/Scripts/Runtime/TMP_SubMesh.cs b/Scripts/Runtime/TMP_SubMesh.cs index 12cd915..d0a4d02 100644 --- a/Scripts/Runtime/TMP_SubMesh.cs +++ b/Scripts/Runtime/TMP_SubMesh.cs @@ -7,7 +7,6 @@ namespace TMPro { [RequireComponent(typeof(MeshRenderer))] - [RequireComponent(typeof(MeshFilter))] [ExecuteAlways] public class TMP_SubMesh : MonoBehaviour { @@ -149,11 +148,22 @@ public float padding /// public MeshFilter meshFilter { - get { if (m_meshFilter == null) m_meshFilter = GetComponent(); + get + { + if (m_meshFilter == null) + { + m_meshFilter = GetComponent(); + + if (m_meshFilter == null) + { + m_meshFilter = gameObject.AddComponent(); + m_meshFilter.hideFlags = HideFlags.HideInInspector | HideFlags.HideAndDontSave; + } + } + return m_meshFilter; } } - [SerializeField] private MeshFilter m_meshFilter; @@ -168,7 +178,6 @@ public Mesh mesh { m_mesh = new Mesh(); m_mesh.hideFlags = HideFlags.HideAndDontSave; - this.meshFilter.mesh = m_mesh; } return m_mesh; @@ -424,8 +433,6 @@ public static TMP_SubMesh AddSubTextObject(TextMeshPro textComponent, MaterialRe go.transform.localScale = Vector3.one; go.layer = textComponent.gameObject.layer; - subMesh.m_meshFilter = go.GetComponent(); - subMesh.m_TextComponent = textComponent; subMesh.m_fontAsset = materialReference.fontAsset; subMesh.m_spriteAsset = materialReference.spriteAsset; diff --git a/Scripts/Runtime/TMP_SubMeshUI.cs b/Scripts/Runtime/TMP_SubMeshUI.cs index 438182b..d17a44e 100644 --- a/Scripts/Runtime/TMP_SubMeshUI.cs +++ b/Scripts/Runtime/TMP_SubMeshUI.cs @@ -213,6 +213,7 @@ public static TMP_SubMeshUI AddSubTextObject(TextMeshProUGUI textComponent, Mate go.hideFlags = HideFlags.DontSave; go.transform.SetParent(textComponent.transform, false); + go.transform.SetAsFirstSibling(); go.layer = textComponent.gameObject.layer; RectTransform rectTransform = go.GetComponent(); @@ -629,6 +630,14 @@ public void SetPivotDirty() this.rectTransform.pivot = m_TextComponent.rectTransform.pivot; } + Transform GetRootCanvasTransform() + { + if (m_RootCanvasTransform == null) + m_RootCanvasTransform = m_TextComponent.canvas.rootCanvas.transform; + + return m_RootCanvasTransform; + } + private Transform m_RootCanvasTransform; /// /// Override to Cull function of MaskableGraphic to prevent Culling. @@ -637,14 +646,16 @@ public void SetPivotDirty() /// public override void Cull(Rect clipRect, bool validRect) { - if (validRect && m_TextComponent.ignoreClipping) + // Get compound rect for the text object and sub text objects in local canvas space. + Rect rect = m_TextComponent.GetCanvasSpaceClippingRect(); + + var cull = !validRect || !clipRect.Overlaps(rect, true); + if (canvasRenderer.cull != cull) { - canvasRenderer.cull = false; - CanvasUpdateRegistry.RegisterCanvasElementForGraphicRebuild(this); - return; + canvasRenderer.cull = cull; + onCullStateChanged.Invoke(cull); + OnCullingChanged(); } - - base.Cull(clipRect, validRect); } diff --git a/Scripts/Runtime/TMP_Text.cs b/Scripts/Runtime/TMP_Text.cs index 740d529..bc45dc0 100644 --- a/Scripts/Runtime/TMP_Text.cs +++ b/Scripts/Runtime/TMP_Text.cs @@ -123,7 +123,6 @@ public virtual string text m_text = value; m_inputSource = TextInputSources.String; m_havePropertiesChanged = true; - m_isCalculateSizeRequired = true; m_isInputParsingRequired = true; SetVerticesDirty(); SetLayoutDirty(); @@ -150,7 +149,7 @@ public ITextPreprocessor textPreprocessor 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(); } + set { if (m_isRightToLeft == value) return; m_isRightToLeft = value; m_havePropertiesChanged = true; m_isInputParsingRequired = true; SetVerticesDirty(); SetLayoutDirty(); } } [SerializeField] protected bool m_isRightToLeft = false; @@ -162,7 +161,7 @@ public bool isRightToLeftText 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(); } + set { if (m_fontAsset == value) return; m_fontAsset = value; LoadFontAsset(); m_havePropertiesChanged = true; m_isInputParsingRequired = true; SetVerticesDirty(); SetLayoutDirty(); } } [SerializeField] protected TMP_FontAsset m_fontAsset; @@ -184,7 +183,7 @@ public virtual Material fontSharedMaterial protected MaterialReference[] m_materialReferences = new MaterialReference[32]; protected Dictionary m_materialReferenceIndexLookup = new Dictionary(); - protected TMP_RichTextTagStack m_materialReferenceStack = new TMP_RichTextTagStack(new MaterialReference[16]); + protected TMP_TextProcessingStack m_materialReferenceStack = new TMP_TextProcessingStack(new MaterialReference[16]); protected int m_currentMaterialIndex; @@ -315,7 +314,7 @@ public TMP_ColorGradient colorGradientPreset public TMP_SpriteAsset spriteAsset { get { return m_spriteAsset; } - set { m_spriteAsset = value; m_havePropertiesChanged = true; m_isInputParsingRequired = true; m_isCalculateSizeRequired = true; SetVerticesDirty(); SetLayoutDirty(); } + set { m_spriteAsset = value; m_havePropertiesChanged = true; m_isInputParsingRequired = true; SetVerticesDirty(); SetLayoutDirty(); } } [SerializeField] protected TMP_SpriteAsset m_spriteAsset; @@ -340,7 +339,7 @@ public bool tintAllSprites public TMP_StyleSheet styleSheet { get { return m_StyleSheet; } - set { m_StyleSheet = value; m_havePropertiesChanged = true; m_isInputParsingRequired = true; m_isCalculateSizeRequired = true; SetVerticesDirty(); SetLayoutDirty(); } + set { m_StyleSheet = value; m_havePropertiesChanged = true; m_isInputParsingRequired = true; SetVerticesDirty(); SetLayoutDirty(); } } [SerializeField] protected TMP_StyleSheet m_StyleSheet; @@ -363,7 +362,7 @@ public TMP_Style textStyle return m_TextStyle; } - set { m_TextStyle = value; m_TextStyleHashCode = m_TextStyle.hashCode; m_havePropertiesChanged = true; m_isInputParsingRequired = true; m_isCalculateSizeRequired = true; SetVerticesDirty(); SetLayoutDirty(); } + set { m_TextStyle = value; m_TextStyleHashCode = m_TextStyle.hashCode; m_havePropertiesChanged = true; m_isInputParsingRequired = true; SetVerticesDirty(); SetLayoutDirty(); } } internal TMP_Style m_TextStyle; [SerializeField] @@ -415,7 +414,7 @@ public Color32 outlineColor set { if (m_outlineColor.Compare(value)) return; SetOutlineColor(value); m_havePropertiesChanged = true; m_outlineColor = value; SetVerticesDirty(); } } - [SerializeField] + //[SerializeField] protected Color32 m_outlineColor = Color.black; @@ -442,14 +441,14 @@ public float outlineWidth 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(); } + set { if (m_fontSize == value) return; m_havePropertiesChanged = 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] + [SerializeField] // TODO: Review if this should be serialized protected float m_fontSizeBase = 36; - protected TMP_RichTextTagStack m_sizeStack = new TMP_RichTextTagStack(16); + protected TMP_TextProcessingStack m_sizeStack = new TMP_TextProcessingStack(16); /// @@ -467,12 +466,12 @@ public float fontScale 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(); } + set { if (m_fontWeight == value) return; m_fontWeight = value; m_havePropertiesChanged = true; m_isInputParsingRequired = true; SetVerticesDirty(); SetLayoutDirty(); } } [SerializeField] protected FontWeight m_fontWeight = FontWeight.Regular; protected FontWeight m_FontWeightInternal = FontWeight.Regular; - protected TMP_RichTextTagStack m_FontWeightStack = new TMP_RichTextTagStack(8); + protected TMP_TextProcessingStack m_FontWeightStack = new TMP_TextProcessingStack(8); /// /// @@ -543,7 +542,7 @@ public float fontSizeMax 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(); } + set { if (m_fontStyle == value) return; m_fontStyle = value; m_havePropertiesChanged = true; m_isInputParsingRequired = true; SetVerticesDirty(); SetLayoutDirty(); } } [SerializeField] protected FontStyles m_fontStyle = FontStyles.Normal; @@ -621,7 +620,7 @@ public TextAlignmentOptions alignment protected TextAlignmentOptions m_textAlignment = TextAlignmentOptions.Converted; protected HorizontalAlignmentOptions m_lineJustification; - protected TMP_RichTextTagStack m_lineJustificationStack = new TMP_RichTextTagStack(new HorizontalAlignmentOptions[16]); + protected TMP_TextProcessingStack m_lineJustificationStack = new TMP_TextProcessingStack(new HorizontalAlignmentOptions[16]); protected Vector3[] m_textContainerLocalCorners = new Vector3[4]; /// @@ -642,7 +641,7 @@ public TextAlignmentOptions alignment public float characterSpacing { get { return m_characterSpacing; } - set { if (m_characterSpacing == value) return; m_havePropertiesChanged = true; m_isCalculateSizeRequired = true; m_characterSpacing = value; SetVerticesDirty(); SetLayoutDirty(); } + set { if (m_characterSpacing == value) return; m_havePropertiesChanged = true; m_characterSpacing = value; SetVerticesDirty(); SetLayoutDirty(); } } [SerializeField] protected float m_characterSpacing = 0; @@ -655,7 +654,7 @@ public float characterSpacing public float wordSpacing { get { return m_wordSpacing; } - set { if (m_wordSpacing == value) return; m_havePropertiesChanged = true; m_isCalculateSizeRequired = true; m_wordSpacing = value; SetVerticesDirty(); SetLayoutDirty(); } + set { if (m_wordSpacing == value) return; m_havePropertiesChanged = true; m_wordSpacing = value; SetVerticesDirty(); SetLayoutDirty(); } } [SerializeField] protected float m_wordSpacing = 0; @@ -666,7 +665,7 @@ public float wordSpacing public float lineSpacing { get { return m_lineSpacing; } - set { if (m_lineSpacing == value) return; m_havePropertiesChanged = true; m_isCalculateSizeRequired = true; m_lineSpacing = value; SetVerticesDirty(); SetLayoutDirty(); } + set { if (m_lineSpacing == value) return; m_havePropertiesChanged = true; m_lineSpacing = value; SetVerticesDirty(); SetLayoutDirty(); } } [SerializeField] protected float m_lineSpacing = 0; @@ -681,7 +680,7 @@ public float lineSpacing public float lineSpacingAdjustment { get { return m_lineSpacingMax; } - set { if (m_lineSpacingMax == value) return; m_havePropertiesChanged = true; m_isCalculateSizeRequired = true; m_lineSpacingMax = value; SetVerticesDirty(); SetLayoutDirty(); } + set { if (m_lineSpacingMax == value) return; m_havePropertiesChanged = true; m_lineSpacingMax = value; SetVerticesDirty(); SetLayoutDirty(); } } [SerializeField] protected float m_lineSpacingMax = 0; // Text Auto Sizing Max Line spacing reduction. @@ -693,7 +692,7 @@ public float lineSpacingAdjustment public float paragraphSpacing { get { return m_paragraphSpacing; } - set { if (m_paragraphSpacing == value) return; m_havePropertiesChanged = true; m_isCalculateSizeRequired = true; m_paragraphSpacing = value; SetVerticesDirty(); SetLayoutDirty(); } + set { if (m_paragraphSpacing == value) return; m_havePropertiesChanged = true; m_paragraphSpacing = value; SetVerticesDirty(); SetLayoutDirty(); } } [SerializeField] protected float m_paragraphSpacing = 0; @@ -705,7 +704,7 @@ public float paragraphSpacing public float characterWidthAdjustment { get { return m_charWidthMaxAdj; } - set { if (m_charWidthMaxAdj == value) return; m_havePropertiesChanged = true; m_isCalculateSizeRequired = true; m_charWidthMaxAdj = value; SetVerticesDirty(); SetLayoutDirty(); } + set { if (m_charWidthMaxAdj == value) return; m_havePropertiesChanged = true; m_charWidthMaxAdj = value; SetVerticesDirty(); SetLayoutDirty(); } } [SerializeField] protected float m_charWidthMaxAdj = 0f; // Text Auto Sizing Max Character Width reduction. @@ -718,7 +717,7 @@ public float characterWidthAdjustment 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(); } + set { if (m_enableWordWrapping == value) return; m_havePropertiesChanged = true; m_isInputParsingRequired = true; m_enableWordWrapping = value; SetVerticesDirty(); SetLayoutDirty(); } } [SerializeField] protected bool m_enableWordWrapping = false; @@ -732,7 +731,7 @@ public bool enableWordWrapping public float wordWrappingRatios { get { return m_wordWrappingRatios; } - set { if (m_wordWrappingRatios == value) return; m_wordWrappingRatios = value; m_havePropertiesChanged = true; m_isCalculateSizeRequired = true; SetVerticesDirty(); SetLayoutDirty(); } + set { if (m_wordWrappingRatios == value) return; m_wordWrappingRatios = value; m_havePropertiesChanged = true; SetVerticesDirty(); SetLayoutDirty(); } } [SerializeField] protected float m_wordWrappingRatios = 0.4f; // Controls word wrapping ratios between word or characters. @@ -757,7 +756,7 @@ public float wordWrappingRatios public TextOverflowModes overflowMode { get { return m_overflowMode; } - set { if (m_overflowMode == value) return; m_overflowMode = value; m_havePropertiesChanged = true; m_isCalculateSizeRequired = true; SetVerticesDirty(); SetLayoutDirty(); } + set { if (m_overflowMode == value) return; m_overflowMode = value; m_havePropertiesChanged = true; SetVerticesDirty(); SetLayoutDirty(); } } [SerializeField] protected TextOverflowModes m_overflowMode = TextOverflowModes.Overflow; @@ -779,7 +778,7 @@ public int firstOverflowCharacterIndex { get { return m_firstOverflowCharacterIndex; } } - [SerializeField] + //[SerializeField] protected int m_firstOverflowCharacterIndex = -1; @@ -814,7 +813,6 @@ public TMP_Text linkedTextComponent } m_havePropertiesChanged = true; - m_isCalculateSizeRequired = true; SetVerticesDirty(); SetLayoutDirty(); } @@ -829,7 +827,7 @@ public TMP_Text linkedTextComponent /// Property indicating whether the text is Truncated or using Ellipsis. /// public bool isTextTruncated { get { return m_isTextTruncated; } } - [SerializeField] + //[SerializeField] protected bool m_isTextTruncated; @@ -839,7 +837,7 @@ public TMP_Text linkedTextComponent public bool enableKerning { get { return m_enableKerning; } - set { if (m_enableKerning == value) return; m_havePropertiesChanged = true; m_isCalculateSizeRequired = true; m_enableKerning = value; SetVerticesDirty(); SetLayoutDirty(); } + set { if (m_enableKerning == value) return; m_havePropertiesChanged = true; m_enableKerning = value; SetVerticesDirty(); SetLayoutDirty(); } } [SerializeField] protected bool m_enableKerning; @@ -851,7 +849,7 @@ public bool enableKerning 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();*/ } + set { if (m_enableExtraPadding == value) return; m_havePropertiesChanged = true; m_enableExtraPadding = value; UpdateMeshPadding(); SetVerticesDirty(); /* SetLayoutDirty();*/ } } [SerializeField] protected bool m_enableExtraPadding = false; @@ -865,7 +863,7 @@ public bool extraPadding 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(); } + set { if (m_isRichText == value) return; m_isRichText = value; m_havePropertiesChanged = true; m_isInputParsingRequired = true; SetVerticesDirty(); SetLayoutDirty(); } } [SerializeField] protected bool m_isRichText = true; // Used to enable or disable Rich Text. @@ -877,7 +875,7 @@ public bool richText 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(); } + set { if (m_parseCtrlCharacters == value) return; m_parseCtrlCharacters = value; m_havePropertiesChanged = true; m_isInputParsingRequired = true; SetVerticesDirty(); SetLayoutDirty(); } } [SerializeField] protected bool m_parseCtrlCharacters = true; @@ -929,7 +927,7 @@ public bool ignoreVisibility get { return m_ignoreCulling; } set { if (m_ignoreCulling == value) return; m_havePropertiesChanged = true; m_ignoreCulling = value; } } - [SerializeField] + //[SerializeField] protected bool m_ignoreCulling = true; // Not implemented yet. @@ -1045,7 +1043,7 @@ public int firstVisibleCharacter get { return m_firstVisibleCharacter; } set { if (m_firstVisibleCharacter == value) return; m_havePropertiesChanged = true; m_firstVisibleCharacter = value; SetVerticesDirty(); } } - [SerializeField] + //[SerializeField] protected int m_firstVisibleCharacter; /// @@ -1087,7 +1085,7 @@ public int maxVisibleLines public bool useMaxVisibleDescender { get { return m_useMaxVisibleDescender; } - set { if (m_useMaxVisibleDescender == value) return; m_havePropertiesChanged = true; m_isInputParsingRequired = true; SetVerticesDirty(); } + set { if (m_useMaxVisibleDescender == value) return; m_havePropertiesChanged = true; m_isInputParsingRequired = true; m_useMaxVisibleDescender = value; SetVerticesDirty(); } } [SerializeField] protected bool m_useMaxVisibleDescender = true; @@ -1129,7 +1127,7 @@ public TMP_TextInfo textInfo { get { return m_textInfo; } } - [SerializeField] + //[SerializeField] protected TMP_TextInfo m_textInfo; // Class which holds information about the Text object such as characters, lines, mesh data as well as metrics. /// @@ -1189,7 +1187,12 @@ public bool isUsingLegacyAnimationComponent /// /// Used to track potential changes in RectTransform size to allow us to ignore OnRectTransformDimensionsChange getting called due to rounding errors when using Stretch Anchors. /// - protected Rect m_RectTransformRect; + protected Vector2 m_PreviousRectTransformSize; + + /// + /// Used to track potential changes in pivot position to allow us to ignore OnRectTransformDimensionsChange getting called due to rounding errors when using Stretch Anchors. + /// + protected Vector2 m_PreviousPivotPosition; /// @@ -1318,7 +1321,7 @@ protected TMP_SpriteAnimator spriteAnimator } } - [SerializeField] + //[SerializeField] protected TMP_SpriteAnimator m_spriteAnimator; @@ -1395,7 +1398,7 @@ protected LayoutElement layoutElement /// /// Computed preferred width of the text object. /// - public virtual float preferredWidth { get { if (!m_isPreferredWidthDirty) return m_preferredWidth; m_preferredWidth = GetPreferredWidth(); return m_preferredWidth; } } + public virtual float preferredWidth { get { m_preferredWidth = GetPreferredWidth(); return m_preferredWidth; } } protected float m_preferredWidth; protected float m_renderedWidth; protected bool m_isPreferredWidthDirty; @@ -1403,7 +1406,7 @@ protected LayoutElement layoutElement /// /// Computed preferred height of the text object. /// - public virtual float preferredHeight { get { if (!m_isPreferredHeightDirty) return m_preferredHeight; m_preferredHeight = GetPreferredHeight(); return m_preferredHeight; } } + public virtual float preferredHeight { get { m_preferredHeight = GetPreferredHeight(); return m_preferredHeight; } } protected float m_preferredHeight; protected float m_renderedHeight; protected bool m_isPreferredHeightDirty; @@ -1429,7 +1432,6 @@ protected LayoutElement layoutElement public int layoutPriority { get { return m_layoutPriority; } } protected int m_layoutPriority = 0; - protected bool m_isCalculateSizeRequired = false; protected bool m_isLayoutDirty; protected bool m_isAwake; @@ -1471,7 +1473,7 @@ internal enum TextInputSources { Text = 0, SetText = 1, SetCharArray = 2, String protected float tag_LineIndent = 0; protected float tag_Indent = 0; - protected TMP_RichTextTagStack m_indentStack = new TMP_RichTextTagStack(new float[16]); + protected TMP_TextProcessingStack m_indentStack = new TMP_TextProcessingStack(new float[16]); protected bool tag_NoParsing; //protected TMP_LinkInfo tag_LinkInfo = new TMP_LinkInfo(); @@ -1502,6 +1504,14 @@ protected struct SpecialCharacter public TMP_FontAsset fontAsset; public Material material; public int materialIndex; + + public SpecialCharacter(TMP_Character character, int materialIndex) + { + this.character = character; + this.fontAsset = character.textAsset as TMP_FontAsset; + this.material = this.fontAsset != null ? this.fontAsset.material : null; + this.materialIndex = materialIndex; + } } private TMP_CharacterInfo[] m_internalCharacterInfo; // Used by functions to calculate preferred values. @@ -1516,6 +1526,9 @@ protected struct SpecialCharacter protected WordWrapState m_SavedLastValidState = new WordWrapState(); protected WordWrapState m_SavedSoftLineBreakState = new WordWrapState(); + //internal Stack m_LineBreakCandiateStack = new Stack(); + internal TMP_TextProcessingStack m_EllipsisInsertionCandidateStack = new TMP_TextProcessingStack(8, 8); + // Fields whose state is saved in conjunction with text parsing and word wrapping. protected int m_characterCount; //protected int m_visibleCharacterCount; @@ -1528,13 +1541,14 @@ protected struct SpecialCharacter protected int m_lineVisibleCharacterCount; protected int m_pageNumber; protected float m_PageAscender; - protected float m_maxAscender; + protected float m_maxTextAscender; protected float m_maxCapHeight; protected float m_ElementAscender; protected float m_ElementDescender; protected float m_maxLineAscender; protected float m_maxLineDescender; protected float m_startOfLineAscender; + protected float m_startOfLineDescender; //protected float m_maxFontScale; protected float m_lineOffset; protected Extents m_meshExtents; @@ -1542,30 +1556,30 @@ protected struct SpecialCharacter // Fields used for vertex colors protected Color32 m_htmlColor = new Color(255, 255, 255, 128); - protected TMP_RichTextTagStack m_colorStack = new TMP_RichTextTagStack(new Color32[16]); - protected TMP_RichTextTagStack m_underlineColorStack = new TMP_RichTextTagStack(new Color32[16]); - protected TMP_RichTextTagStack m_strikethroughColorStack = new TMP_RichTextTagStack(new Color32[16]); - protected TMP_RichTextTagStack m_HighlightStateStack = new TMP_RichTextTagStack(new HighlightState[16]); + protected TMP_TextProcessingStack m_colorStack = new TMP_TextProcessingStack(new Color32[16]); + protected TMP_TextProcessingStack m_underlineColorStack = new TMP_TextProcessingStack(new Color32[16]); + protected TMP_TextProcessingStack m_strikethroughColorStack = new TMP_TextProcessingStack(new Color32[16]); + protected TMP_TextProcessingStack m_HighlightStateStack = new TMP_TextProcessingStack(new HighlightState[16]); protected TMP_ColorGradient m_colorGradientPreset; - protected TMP_RichTextTagStack m_colorGradientStack = new TMP_RichTextTagStack(new TMP_ColorGradient[16]); + protected TMP_TextProcessingStack m_colorGradientStack = new TMP_TextProcessingStack(new TMP_ColorGradient[16]); protected bool m_colorGradientPresetIsTinted; protected float m_tabSpacing = 0; protected float m_spacing = 0; // STYLE TAGS - protected TMP_RichTextTagStack[] m_TextStyleStacks = new TMP_RichTextTagStack[8]; + protected TMP_TextProcessingStack[] m_TextStyleStacks = new TMP_TextProcessingStack[8]; protected int m_TextStyleStackDepth = 0; - protected TMP_RichTextTagStack m_ItalicAngleStack = new TMP_RichTextTagStack(new int[16]); + protected TMP_TextProcessingStack m_ItalicAngleStack = new TMP_TextProcessingStack(new int[16]); protected int m_ItalicAngle; - protected TMP_RichTextTagStack m_actionStack = new TMP_RichTextTagStack(new int[16]); + protected TMP_TextProcessingStack m_actionStack = new TMP_TextProcessingStack(new int[16]); protected float m_padding = 0; protected float m_baselineOffset; // Used for superscript and subscript. - protected TMP_RichTextTagStack m_baselineOffsetStack = new TMP_RichTextTagStack(new float[16]); + protected TMP_TextProcessingStack m_baselineOffsetStack = new TMP_TextProcessingStack(new float[16]); protected float m_xAdvance; // Tracks x advancement from character to character. protected TMP_TextElementType m_textElementType; @@ -1697,12 +1711,6 @@ protected virtual void SetShaderDepth() { } /// protected virtual void SetCulling() { } - /// - /// Used to prevent clipping of text objects using nested RectMasks unless the compounded clip rect is no longer valid. - /// This is used by the TMP Input Field to prevent clipping when the RectTransform of the text ends up outside of the viewport RectMask. - /// - internal bool ignoreClipping; - /// /// Get the padding value for the currently assigned material /// @@ -2163,7 +2171,6 @@ public void SetText(string text, float arg0, float arg1, float arg2, float arg3, m_inputSource = TextInputSources.SetText; m_isInputParsingRequired = true; m_havePropertiesChanged = true; - m_isCalculateSizeRequired = true; SetVerticesDirty(); SetLayoutDirty(); @@ -2190,7 +2197,6 @@ public void SetText(StringBuilder text) m_isInputParsingRequired = true; m_havePropertiesChanged = true; - m_isCalculateSizeRequired = true; SetVerticesDirty(); SetLayoutDirty(); @@ -2371,7 +2377,6 @@ public void SetCharArray(char[] sourceText) m_inputSource = TextInputSources.SetCharArray; m_isInputParsingRequired = true; m_havePropertiesChanged = true; - m_isCalculateSizeRequired = true; SetVerticesDirty(); SetLayoutDirty(); @@ -2540,7 +2545,6 @@ public void SetCharArray(char[] sourceText, int start, int length) m_inputSource = TextInputSources.SetCharArray; m_havePropertiesChanged = true; m_isInputParsingRequired = true; - m_isCalculateSizeRequired = true; SetVerticesDirty(); SetLayoutDirty(); @@ -2694,7 +2698,6 @@ public void SetCharArray(int[] sourceText, int start, int length) m_inputSource = TextInputSources.SetCharArray; m_havePropertiesChanged = true; m_isInputParsingRequired = true; - m_isCalculateSizeRequired = true; SetVerticesDirty(); SetLayoutDirty(); @@ -4966,6 +4969,10 @@ protected float GetPreferredWidth() { if (TMP_Settings.instance == null) return 0; + // Return cached preferred height if already computed + if (!m_isPreferredWidthDirty) + return m_preferredWidth; + float fontSize = m_enableAutoSizing ? m_fontSizeMax : m_fontSize; // Reset auto sizing point size bounds @@ -4987,7 +4994,7 @@ protected float GetPreferredWidth() m_isPreferredWidthDirty = false; - //Debug.Log("GetPreferredWidth() Called at frame " + Time.frameCount + ". Returning width of " + preferredWidth); + //Debug.Log("GetPreferredWidth() called on Object ID: " + GetInstanceID() + " on frame: " + Time.frameCount + ". Returning width of " + preferredWidth); return preferredWidth; } @@ -5024,6 +5031,10 @@ protected float GetPreferredHeight() { if (TMP_Settings.instance == null) return 0; + // Return cached preferred height if already computed + if (!m_isPreferredHeightDirty) + return m_preferredHeight; + float fontSize = m_enableAutoSizing ? m_fontSizeMax : m_fontSize; // Reset auto sizing point size bounds @@ -5055,7 +5066,7 @@ protected float GetPreferredHeight() m_isPreferredHeightDirty = false; - //Debug.Log("GetPreferredHeight() Called. Returning height of " + preferredHeight); + //Debug.Log("GetPreferredHeight() called on Object ID: " + GetInstanceID() + " on frame: " + Time.frameCount +". Returning height of " + preferredHeight); return preferredHeight; } @@ -5254,7 +5265,7 @@ protected virtual Vector2 CalculatePreferredValues(ref float fontSize, Vector2 m // Tracking of the highest Ascender m_maxCapHeight = 0; - m_maxAscender = 0; + m_maxTextAscender = 0; m_ElementDescender = 0; float maxVisibleDescender = 0; bool isMaxVisibleDescenderSet = false; @@ -5446,15 +5457,16 @@ protected virtual Vector2 CalculatePreferredValues(ref float fontSize, Vector2 m m_currentMaterialIndex = m_textInfo.characterInfo[m_characterCount].materialReferenceIndex; + float adjustedScale; if (isInjectingCharacter && m_InternalParsingBuffer[i].unicode == 0x0A && m_characterCount != m_firstCharacterOfLine) - m_fontScale = m_textInfo.characterInfo[m_characterCount - 1].pointSize * smallCapsMultiplier / m_currentFontAsset.m_FaceInfo.pointSize * m_currentFontAsset.m_FaceInfo.scale * (m_isOrthographic ? 1 : 0.1f); + adjustedScale = m_textInfo.characterInfo[m_characterCount - 1].pointSize * smallCapsMultiplier / m_currentFontAsset.m_FaceInfo.pointSize * m_currentFontAsset.m_FaceInfo.scale * (m_isOrthographic ? 1 : 0.1f); else - m_fontScale = m_currentFontSize * smallCapsMultiplier / m_currentFontAsset.m_FaceInfo.pointSize * m_currentFontAsset.m_FaceInfo.scale * (m_isOrthographic ? 1 : 0.1f); + adjustedScale = m_currentFontSize * smallCapsMultiplier / m_currentFontAsset.m_FaceInfo.pointSize * m_currentFontAsset.m_FaceInfo.scale * (m_isOrthographic ? 1 : 0.1f); elementAscentLine = m_currentFontAsset.m_FaceInfo.ascentLine; elementDescentLine = m_currentFontAsset.m_FaceInfo.descentLine; - currentElementScale = m_fontScale * m_fontScaleMultiplier * m_cached_TextElement.scale; + currentElementScale = adjustedScale * m_fontScaleMultiplier * m_cached_TextElement.scale; //baselineOffset = m_currentFontAsset.faceInfo.baseline * m_fontScale * m_fontScaleMultiplier * m_currentFontAsset.faceInfo.scale; m_internalCharacterInfo[m_characterCount].elementType = TMP_TextElementType.Character; @@ -5556,54 +5568,53 @@ protected virtual Vector2 CalculatePreferredValues(ref float fontSize, Vector2 m ? elementAscentLine * currentElementScale / smallCapsMultiplier + m_baselineOffset : elementAscentLine * spriteScale + m_baselineOffset; - if (isInjectingCharacter && charCode != 0x03) - elementAscender = m_maxLineAscender; - // Element Descender in line space float elementDescender = m_textElementType == TMP_TextElementType.Character ? elementDescentLine * currentElementScale / smallCapsMultiplier + m_baselineOffset : elementDescentLine * spriteScale + m_baselineOffset; - if (isInjectingCharacter && charCode != 0x03) - elementDescender = m_maxLineDescender; + float adjustedAscender = elementAscender; + float adjustedDescender = elementDescender; + + bool isFirstCharacterOfLine = m_characterCount == m_firstCharacterOfLine; + // Max line ascender and descender in line space + if (isFirstCharacterOfLine || isWhiteSpace == false) + { + // Special handling for Superscript and Subscript where we use the unadjusted line ascender and descender + if (m_baselineOffset != 0) + { + adjustedAscender = Mathf.Max((elementAscender - m_baselineOffset) / m_fontScaleMultiplier, adjustedAscender); + adjustedDescender = Mathf.Min((elementDescender - m_baselineOffset) / m_fontScaleMultiplier, adjustedDescender); + } + + m_maxLineAscender = Mathf.Max(adjustedAscender, m_maxLineAscender); + m_maxLineDescender = Mathf.Min(adjustedDescender, m_maxLineDescender); + } // Element Ascender and Descender in object space - if (!isWhiteSpace || m_characterCount == m_firstCharacterOfLine) + if (isFirstCharacterOfLine || isWhiteSpace == false) { + m_internalCharacterInfo[m_characterCount].adjustedAscender = adjustedAscender; + m_internalCharacterInfo[m_characterCount].adjustedDescender = adjustedDescender; + m_ElementAscender = m_internalCharacterInfo[m_characterCount].ascender = elementAscender - m_lineOffset; m_ElementDescender = m_internalCharacterInfo[m_characterCount].descender = elementDescender - m_lineOffset; } else { + m_internalCharacterInfo[m_characterCount].adjustedAscender = m_maxLineAscender; + m_internalCharacterInfo[m_characterCount].adjustedDescender = m_maxLineDescender; + m_ElementAscender = m_internalCharacterInfo[m_characterCount].ascender = m_maxLineAscender - m_lineOffset; m_ElementDescender = m_internalCharacterInfo[m_characterCount].descender = m_maxLineDescender - m_lineOffset; } - // Max line ascender and descender in line space - if (!isWhiteSpace || m_characterCount == m_firstCharacterOfLine) - { - m_maxLineAscender = elementAscender > m_maxLineAscender ? elementAscender : m_maxLineAscender; - 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.m_FaceInfo.subscriptSize; - elementAscender = m_maxLineAscender; - m_maxLineAscender = baseAscender > m_maxLineAscender ? baseAscender : m_maxLineAscender; - - float baseDescender = (elementDescender - m_baselineOffset) / m_currentFontAsset.m_FaceInfo.subscriptSize; - //elementDescender = m_maxLineDescender; - m_maxLineDescender = baseDescender < m_maxLineDescender ? baseDescender : m_maxLineDescender; - } - // Max text object ascender and cap height if (m_lineNumber == 0 || m_isNewPage) { - if (!isWhiteSpace || m_characterCount == m_firstCharacterOfLine) + if (isFirstCharacterOfLine || isWhiteSpace == false) { - m_maxAscender = m_maxAscender > elementAscender ? m_maxAscender : elementAscender; + m_maxTextAscender = m_maxLineAscender; m_maxCapHeight = Mathf.Max(m_maxCapHeight, m_currentFontAsset.m_FaceInfo.capLine * currentElementScale / smallCapsMultiplier); } } @@ -5740,7 +5751,7 @@ protected virtual Vector2 CalculatePreferredValues(ref float fontSize, Vector2 m renderedWidth += m_xAdvance; if (isWordWrappingEnabled) - renderedHeight = m_maxAscender - m_ElementDescender; + renderedHeight = m_maxTextAscender - m_ElementDescender; else renderedHeight = Mathf.Max(renderedHeight, lineAscender - lineDescender); @@ -5749,10 +5760,11 @@ protected virtual Vector2 CalculatePreferredValues(ref float fontSize, Vector2 m m_lineNumber += 1; + float ascender = m_internalCharacterInfo[m_characterCount].adjustedAscender; + // Compute potential new line offset in the event a line break is needed. if (m_lineHeight == TMP_Math.FLOAT_UNSET) { - float ascender = m_internalCharacterInfo[m_characterCount].ascender - m_internalCharacterInfo[m_characterCount].baseLine; m_lineOffset += 0 - m_maxLineDescender + ascender + (lineGap + m_lineSpacingDelta) * baseScale + m_lineSpacing * currentEmScale; m_IsDrivenLineSpacing = false; } @@ -5764,7 +5776,7 @@ protected virtual Vector2 CalculatePreferredValues(ref float fontSize, Vector2 m m_maxLineAscender = k_LargeNegativeFloat; m_maxLineDescender = k_LargePositiveFloat; - m_startOfLineAscender = elementAscender; + m_startOfLineAscender = ascender; m_xAdvance = 0 + tag_Indent; //isStartOfNewLine = true; @@ -5838,12 +5850,13 @@ protected virtual Vector2 CalculatePreferredValues(ref float fontSize, Vector2 m if (charCode == 10 || charCode == 11 || charCode == 0x03 || charCode == 0x2028 || charCode == 0x2029 || m_characterCount == totalCharacterCount - 1) { // Check if Line Spacing of previous line needs to be adjusted. - if (m_lineOffset > 0 && !TMP_Math.Approximately(m_maxLineAscender, m_startOfLineAscender) && m_IsDrivenLineSpacing == false && !m_isNewPage && isInjectingCharacter == false) + float baselineAdjustmentDelta = m_maxLineAscender - m_startOfLineAscender; + if (m_lineOffset > 0 && Math.Abs(baselineAdjustmentDelta) > 0.01f && m_IsDrivenLineSpacing == false && !m_isNewPage) { - float offsetDelta = m_maxLineAscender - m_startOfLineAscender; - m_ElementDescender -= offsetDelta; - m_lineOffset += offsetDelta; + m_ElementDescender -= baselineAdjustmentDelta; + m_lineOffset += baselineAdjustmentDelta; } + 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; @@ -5861,7 +5874,7 @@ protected virtual Vector2 CalculatePreferredValues(ref float fontSize, Vector2 m renderedWidth = 0; } - renderedHeight = m_maxAscender - m_ElementDescender; + renderedHeight = m_maxTextAscender - m_ElementDescender; // Add new line if not last lines or character. if (charCode == 10 || charCode == 11 || charCode == 0x2D || charCode == 0x2028 || charCode == 0x2029) @@ -5874,10 +5887,12 @@ protected virtual Vector2 CalculatePreferredValues(ref float fontSize, Vector2 m m_lineNumber += 1; m_firstCharacterOfLine = m_characterCount + 1; + float ascender = m_internalCharacterInfo[m_characterCount].adjustedAscender; + // Apply Line Spacing with special handling for VT char(11) if (m_lineHeight == TMP_Math.FLOAT_UNSET) { - float lineOffsetDelta = 0 - m_maxLineDescender + elementAscender + (lineGap + m_lineSpacingDelta) * baseScale + (m_lineSpacing + (charCode == 10 || charCode == 0x2029 ? m_paragraphSpacing : 0)) * currentEmScale; + float lineOffsetDelta = 0 - m_maxLineDescender + ascender + (lineGap + m_lineSpacingDelta) * baseScale + (m_lineSpacing + (charCode == 10 || charCode == 0x2029 ? m_paragraphSpacing : 0)) * currentEmScale; m_lineOffset += lineOffsetDelta; m_IsDrivenLineSpacing = false; } @@ -5889,7 +5904,7 @@ protected virtual Vector2 CalculatePreferredValues(ref float fontSize, Vector2 m m_maxLineAscender = k_LargeNegativeFloat; m_maxLineDescender = k_LargePositiveFloat; - m_startOfLineAscender = elementAscender; + m_startOfLineAscender = ascender; m_xAdvance = 0 + tag_LineIndent + tag_Indent; @@ -6026,6 +6041,7 @@ protected virtual Vector2 CalculatePreferredValues(ref float fontSize, Vector2 m /// protected virtual Bounds GetCompoundBounds() { return new Bounds(); } + internal virtual Rect GetCanvasSpaceClippingRect() { return Rect.zero; } /// /// Method which returns the bounds of the text object; @@ -6280,17 +6296,15 @@ protected void InsertNewLine(int i, float baseScale, float currentEmScale, float // 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; + float ascender = m_textInfo.characterInfo[m_characterCount].adjustedAscender; float lineOffsetDelta = 0 - m_maxLineDescender + ascender + (lineGap + m_lineSpacingDelta) * baseScale + m_lineSpacing * currentEmScale; m_lineOffset += lineOffsetDelta; m_startOfLineAscender = ascender; - //isDrivenLineSpacing = false; } else { m_lineOffset += m_lineHeight + m_lineSpacing * currentEmScale; - //isDrivenLineSpacing = true; } m_maxLineAscender = k_LargeNegativeFloat; @@ -6334,7 +6348,7 @@ protected void SaveWordWrappingState(ref WordWrapState state, int index, int cou state.xAdvance = m_xAdvance; state.maxCapHeight = m_maxCapHeight; - state.maxAscender = m_maxAscender; + state.maxAscender = m_maxTextAscender; state.maxDescender = m_ElementDescender; state.startOfLineAscender = m_startOfLineAscender; state.maxLineAscender = m_maxLineAscender; @@ -6425,7 +6439,7 @@ protected int RestoreWordWrappingState(ref WordWrapState state) m_xAdvance = state.xAdvance; m_maxCapHeight = state.maxCapHeight; - m_maxAscender = state.maxAscender; + m_maxTextAscender = state.maxAscender; m_ElementDescender = state.maxDescender; m_startOfLineAscender = state.startOfLineAscender; m_maxLineAscender = state.maxLineAscender; @@ -7163,7 +7177,7 @@ protected virtual void DrawTextHighlight(Vector3 start, Vector3 end, ref int ind /// protected void LoadDefaultSettings() { - if (m_text == null || m_isWaitingOnResourceLoad) + if (m_fontAsset == null || m_isWaitingOnResourceLoad) { m_rectTransform = this.rectTransform; @@ -7228,48 +7242,42 @@ protected void GetSpecialCharacters(TMP_FontAsset fontAsset) protected void GetEllipsisSpecialCharacter(TMP_FontAsset fontAsset) { bool isUsingAlternativeTypeface; - TMP_FontAsset tempFontAsset; // Search base font asset - m_Ellipsis.character = TMP_FontAssetUtilities.GetCharacterFromFontAsset(0x2026, fontAsset, false, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface, out tempFontAsset); + TMP_Character character = TMP_FontAssetUtilities.GetCharacterFromFontAsset(0x2026, fontAsset, false, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface); - if (m_Ellipsis.character == null) + if (character == null) { // Search primary fallback list if (fontAsset.m_FallbackFontAssetTable != null && fontAsset.m_FallbackFontAssetTable.Count > 0) - m_Ellipsis.character = TMP_FontAssetUtilities.GetCharacterFromFontAssets(0x2026, fontAsset.m_FallbackFontAssetTable, true, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface, out tempFontAsset); + character = TMP_FontAssetUtilities.GetCharacterFromFontAssets(0x2026, fontAsset, fontAsset.m_FallbackFontAssetTable, true, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface); } // Search TMP Settings general fallback list - if (m_Ellipsis.character == null) + if (character == null) { if (TMP_Settings.fallbackFontAssets != null && TMP_Settings.fallbackFontAssets.Count > 0) - m_Ellipsis.character = TMP_FontAssetUtilities.GetCharacterFromFontAssets(0x2026, TMP_Settings.fallbackFontAssets, true, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface, out tempFontAsset); + character = TMP_FontAssetUtilities.GetCharacterFromFontAssets(0x2026, fontAsset, TMP_Settings.fallbackFontAssets, true, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface); } // Search TMP Settings' default font asset - if (m_Ellipsis.character == null) + if (character == null) { if (TMP_Settings.defaultFontAsset != null) - m_Ellipsis.character = TMP_FontAssetUtilities.GetCharacterFromFontAsset(0x2026, TMP_Settings.defaultFontAsset, true, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface, out tempFontAsset); + character = TMP_FontAssetUtilities.GetCharacterFromFontAsset(0x2026, TMP_Settings.defaultFontAsset, true, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface); } - if (m_Ellipsis.character != null) - { - m_Ellipsis.fontAsset = tempFontAsset; - m_Ellipsis.material = tempFontAsset.material; - m_Ellipsis.materialIndex = 0; - } + if (character != null) + m_Ellipsis = new SpecialCharacter(character, 0); } protected void GetUnderlineSpecialCharacter(TMP_FontAsset fontAsset) { bool isUsingAlternativeTypeface; - TMP_FontAsset tempFontAsset; // Search base font asset - m_Underline.character = TMP_FontAssetUtilities.GetCharacterFromFontAsset(0x5F, fontAsset, false, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface, out tempFontAsset); + TMP_Character character = TMP_FontAssetUtilities.GetCharacterFromFontAsset(0x5F, fontAsset, false, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface); /* if (m_Underline.character == null) @@ -7294,11 +7302,9 @@ protected void GetUnderlineSpecialCharacter(TMP_FontAsset fontAsset) } */ - if (m_Underline.character != null) + if (character != null) { - m_Underline.fontAsset = tempFontAsset; - m_Underline.material = tempFontAsset.material; - m_Underline.materialIndex = 0; + m_Underline = new SpecialCharacter(character, 0); } else { @@ -7360,6 +7366,101 @@ protected TMP_FontAsset GetFontAssetForWeight(int fontWeight) return fontAsset; } + internal TMP_TextElement GetTextElement(uint unicode, TMP_FontAsset fontAsset, FontStyles fontStyle, FontWeight fontWeight, out bool isUsingAlternativeTypeface) + { + TMP_Character character = TMP_FontAssetUtilities.GetCharacterFromFontAsset(unicode, fontAsset, false, fontStyle, fontWeight, out isUsingAlternativeTypeface); + + if (character != null) + return character; + + // Search potential list of fallback font assets assigned to the font asset. + if (fontAsset.m_FallbackFontAssetTable != null && fontAsset.m_FallbackFontAssetTable.Count > 0) + character = TMP_FontAssetUtilities.GetCharacterFromFontAssets(unicode, fontAsset, fontAsset.m_FallbackFontAssetTable, true, fontStyle, fontWeight, out isUsingAlternativeTypeface); + + if (character != null) + { + // Add character to font asset lookup cache + //fontAsset.AddCharacterToLookupCache(unicode, character); + + return character; + } + + // Search for the character in the primary font asset if not the current font asset + if (fontAsset.instanceID != m_fontAsset.instanceID) + { + // Search primary font asset + character = TMP_FontAssetUtilities.GetCharacterFromFontAsset(unicode, m_fontAsset, false, fontStyle, fontWeight, out isUsingAlternativeTypeface); + + // Use material and index of primary font asset. + if (character != null) + { + m_currentMaterialIndex = 0; + m_currentMaterial = m_materialReferences[0].material; + + // Add character to font asset lookup cache + //fontAsset.AddCharacterToLookupCache(unicode, character); + + return character; + } + + // Search list of potential fallback font assets assigned to the primary font asset. + if (m_fontAsset.m_FallbackFontAssetTable != null && m_fontAsset.m_FallbackFontAssetTable.Count > 0) + character = TMP_FontAssetUtilities.GetCharacterFromFontAssets(unicode, fontAsset, m_fontAsset.m_FallbackFontAssetTable, true, fontStyle, fontWeight, out isUsingAlternativeTypeface); + + if (character != null) + { + // Add character to font asset lookup cache + //fontAsset.AddCharacterToLookupCache(unicode, character); + + return character; + } + } + + // Search for the character in potential local Sprite Asset assigned to the text object. + if (m_spriteAsset != null) + { + TMP_SpriteCharacter spriteCharacter = TMP_FontAssetUtilities.GetSpriteCharacterFromSpriteAsset(unicode, m_spriteAsset, true); + + if (spriteCharacter != null) + return spriteCharacter; + } + + // Search for the character in the list of fallback assigned in the TMP Settings (General Fallbacks). + if (TMP_Settings.fallbackFontAssets != null && TMP_Settings.fallbackFontAssets.Count > 0) + character = TMP_FontAssetUtilities.GetCharacterFromFontAssets(unicode, fontAsset, TMP_Settings.fallbackFontAssets, true, fontStyle, fontWeight, out isUsingAlternativeTypeface); + + if (character != null) + { + // Add character to font asset lookup cache + //fontAsset.AddCharacterToLookupCache(unicode, character); + + return character; + } + + // Search for the character in the Default Font Asset assigned in the TMP Settings file. + if (TMP_Settings.defaultFontAsset != null) + character = TMP_FontAssetUtilities.GetCharacterFromFontAsset(unicode, TMP_Settings.defaultFontAsset, true, fontStyle, fontWeight, out isUsingAlternativeTypeface); + + if (character != null) + { + // Add character to font asset lookup cache + //fontAsset.AddCharacterToLookupCache(unicode, character); + + return character; + } + + // Search for the character in the Default Sprite Asset assigned in the TMP Settings file. + if (TMP_Settings.defaultSpriteAsset != null) + { + TMP_SpriteCharacter spriteCharacter = TMP_FontAssetUtilities.GetSpriteCharacterFromSpriteAsset(unicode, TMP_Settings.defaultSpriteAsset, true); + + if (spriteCharacter != null) + return spriteCharacter; + } + + return null; + } + /// /// Method to Enable or Disable child SubMesh objects. @@ -7371,7 +7472,7 @@ protected virtual void SetActiveSubMeshes(bool state) { } /// /// Destroy Sub Mesh Objects. /// - protected virtual void ClearSubMeshObjects() { } + protected virtual void DestroySubMeshObjects() { } /// diff --git a/Scripts/Runtime/TMP_TextElement.cs b/Scripts/Runtime/TMP_TextElement.cs index fa34fd6..9bb8cf1 100644 --- a/Scripts/Runtime/TMP_TextElement.cs +++ b/Scripts/Runtime/TMP_TextElement.cs @@ -26,6 +26,11 @@ public class TMP_TextElement /// public uint unicode { get { return m_Unicode; } set { m_Unicode = value; } } + /// + /// The Text Asset to which this Text Element belongs. + /// + public TMP_Asset textAsset { get { return m_TextAsset; } set { m_TextAsset = value; } } + /// /// The glyph used by this text element. /// @@ -51,6 +56,8 @@ public class TMP_TextElement [SerializeField] internal uint m_Unicode; + internal TMP_Asset m_TextAsset; + internal Glyph m_Glyph; [SerializeField] diff --git a/Scripts/Runtime/TMP_RichTextTagStack.cs b/Scripts/Runtime/TMP_TextProcessingStack.cs similarity index 76% rename from Scripts/Runtime/TMP_RichTextTagStack.cs rename to Scripts/Runtime/TMP_TextProcessingStack.cs index 1ce4f81..2325795 100644 --- a/Scripts/Runtime/TMP_RichTextTagStack.cs +++ b/Scripts/Runtime/TMP_TextProcessingStack.cs @@ -1,4 +1,8 @@ -namespace TMPro +using System; +using System.Diagnostics; +using UnityEngine; + +namespace TMPro { /// /// Structure used to track basic XML tags which are binary (on / off) @@ -120,16 +124,73 @@ public byte Remove(FontStyles style) /// Structure used to track XML tags of various types. /// /// - public struct TMP_RichTextTagStack + [DebuggerDisplay("Item count = {m_Count}")] + public struct TMP_TextProcessingStack { public T[] itemStack; public int index; - int m_Capacity; T m_DefaultItem; + int m_Capacity; + int m_RolloverSize; + int m_Count; const int k_DefaultCapacity = 4; + + /// + /// Constructor to create a new item stack. + /// + /// + public TMP_TextProcessingStack(T[] stack) + { + itemStack = stack; + m_Capacity = stack.Length; + index = 0; + m_RolloverSize = 0; + + m_DefaultItem = default; + m_Count = 0; + } + + + /// + /// Constructor for a new item stack with the given capacity. + /// + /// + public TMP_TextProcessingStack(int capacity) + { + itemStack = new T[capacity]; + m_Capacity = capacity; + index = 0; + m_RolloverSize = 0; + + m_DefaultItem = default; + m_Count = 0; + } + + + public TMP_TextProcessingStack(int capacity, int rolloverSize) + { + itemStack = new T[capacity]; + m_Capacity = capacity; + index = 0; + m_RolloverSize = rolloverSize; + + m_DefaultItem = default; + m_Count = 0; + } + + + /// + /// + /// + public int Count + { + get { return m_Count; } + } + + /// /// Returns the current item on the stack. /// @@ -144,30 +205,20 @@ public T current } } - /// - /// Constructor to create a new item stack. - /// - /// - public TMP_RichTextTagStack(T[] tagStack) - { - itemStack = tagStack; - m_Capacity = tagStack.Length; - index = 0; - - m_DefaultItem = default(T); - } /// - /// Constructor for a new item stack with the given capacity. + /// /// - /// - public TMP_RichTextTagStack(int capacity) + public int rolloverSize { - itemStack = new T[capacity]; - m_Capacity = capacity; - index = 0; + get { return m_RolloverSize; } + set + { + m_RolloverSize = value; - m_DefaultItem = default(T); + //if (m_Capacity < m_RolloverSize) + // Array.Resize(ref itemStack, m_RolloverSize); + } } @@ -177,6 +228,7 @@ public TMP_RichTextTagStack(int capacity) public void Clear() { index = 0; + m_Count = 0; } @@ -190,7 +242,7 @@ public void SetDefault(T item) { m_Capacity = k_DefaultCapacity; itemStack = new T[m_Capacity]; - m_DefaultItem = default(T); + m_DefaultItem = default; } itemStack[0] = item; @@ -238,27 +290,47 @@ public void Push(T item) if (m_Capacity == 0) m_Capacity = k_DefaultCapacity; - System.Array.Resize(ref itemStack, m_Capacity); + Array.Resize(ref itemStack, m_Capacity); } itemStack[index] = item; - index += 1; + + if (m_RolloverSize == 0) + { + index += 1; + m_Count += 1; + } + else + { + index = (index + 1) % m_RolloverSize; + m_Count = m_Count < m_RolloverSize ? m_Count + 1 : m_RolloverSize; + } + } public T Pop() { - if (index == 0) - return default(T); + if (index == 0 && m_RolloverSize == 0) + return default; + + if (m_RolloverSize == 0) + index -= 1; + else + { + index = (index - 1) % m_RolloverSize; + index = index < 0 ? index + m_RolloverSize : index; + } - index -= 1; T item = itemStack[index]; itemStack[index] = m_DefaultItem; + m_Count = m_Count > 0 ? m_Count - 1 : 0; + return item; } /// - /// + /// /// /// public T Peek() diff --git a/Scripts/Runtime/TMP_RichTextTagStack.cs.meta b/Scripts/Runtime/TMP_TextProcessingStack.cs.meta similarity index 100% rename from Scripts/Runtime/TMP_RichTextTagStack.cs.meta rename to Scripts/Runtime/TMP_TextProcessingStack.cs.meta diff --git a/Scripts/Runtime/TMPro_ExtensionMethods.cs b/Scripts/Runtime/TMPro_ExtensionMethods.cs index 30c2140..5c8ddbd 100644 --- a/Scripts/Runtime/TMPro_ExtensionMethods.cs +++ b/Scripts/Runtime/TMPro_ExtensionMethods.cs @@ -67,7 +67,7 @@ public static string IntToString(this int[] unicodes, int start, int length) public static int FindInstanceID (this List list, T target) where T : Object { int targetID = target.GetInstanceID(); - + for (int i = 0; i < list.Count; i++) { if (list[i].GetInstanceID() == targetID) @@ -239,5 +239,11 @@ public static bool Approximately(float a, float b) { return (b - 0.0001f) < a && a < (b + 0.0001f); } + + public static int Mod(int a, int b) + { + int r = a % b; + return r < 0 ? r + b : r; + } } } diff --git a/Scripts/Runtime/TMPro_MeshUtilities.cs b/Scripts/Runtime/TMPro_MeshUtilities.cs index cb5bb12..88a8efd 100644 --- a/Scripts/Runtime/TMPro_MeshUtilities.cs +++ b/Scripts/Runtime/TMPro_MeshUtilities.cs @@ -60,7 +60,7 @@ public enum TMP_VertexDataUpdateFlags //public struct TMP_VertexInfo - //{ + //{ // public TMP_Vertex topLeft; // public TMP_Vertex bottomLeft; // public TMP_Vertex topRight; @@ -193,7 +193,7 @@ 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; @@ -214,7 +214,7 @@ public struct TMP_SpriteInfo //public struct SpriteInfo //{ - // + // //} @@ -234,7 +234,7 @@ public Extents(Vector2 min, Vector2 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 = "Min (" + min.x.ToString("f2") + ", " + min.y.ToString("f2") + ") Max (" + max.x.ToString("f2") + ", " + max.y.ToString("f2") + ")"; return s; } } @@ -245,8 +245,8 @@ public struct Mesh_Extents { public Vector2 min; public Vector2 max; - - + + public Mesh_Extents(Vector2 min, Vector2 max) { this.min = min; @@ -261,8 +261,88 @@ public override string ToString() } } - - // Structure used for Word Wrapping which tracks the state of execution when the last space or carriage return character was encountered. + // internal struct TMP_TextProcessingState + // { + // // Multi Font & Material support related + // public TMP_FontAsset CurrentFontAsset; + // public TMP_SpriteAsset CurrentSpriteAsset; + // public Material CurrentMaterial; + // public int CurrentMaterialIndex; + // + // public float CurrentFontSize; + // public float FontScale; + // public float FontScaleMultiplier; + // public FontStyles FontStyle; + // public int ItalicAngle; + // + // public float CharacterSpacing; + // public float CharacterMonoSpacing; + // public bool TagNoParsing; + // + // public float HorizontalAdvance; + // public float MaxCapHeight; + // public float MaxTextAscender; + // public float MaxTextDescender; + // public float MaxElementAscender; + // public float MaxElementDescender; + // public float StartOfLineAscender; + // public float MaxLineAscender; + // public float MaxLineDescender; + // public float PageAscender; + // + // public int PreviousWordBreak; + // public int TotalCharacterCount; + // //public int VisibleCharacterCount; + // //public int VisibleSpriteCount; + // public int VisibleLinkCount; + // public int FirstCharacterIndex; + // public int FirstVisibleCharacterIndex; + // public int LastCharacterIndex; + // public int LastVisibleCharIndex; + // + // public int LineNumber; + // public float baselineOffset; + // public float lineOffset; + // public bool isDrivenLineSpacing; + // public bool IsNonBreakingSpace; + // + // public HorizontalAlignmentOptions HorizontalAlignment; + // public float MarginLeft; + // public float MarginRight; + // + // public float PreferredWidth; + // public float PreferredHeight; + // + // public Color32 VertexColor; + // public Color32 UnderlineColor; + // public Color32 StrikethroughColor; + // //public Color32 HighlightColor; + // + // public Extents MeshExtents; + // public TMP_LineInfo lineInfo; + // + // public int spriteAnimationID; + // + // public TMP_FontStyleStack BasicStyleStack; + // public TMP_RichTextTagStack ItalicAngleStack; + // public TMP_RichTextTagStack ColorStack; + // public TMP_RichTextTagStack UnderlineColorStack; + // public TMP_RichTextTagStack StrikethroughColorStack; + // public TMP_RichTextTagStack HighlightColorStack; + // public TMP_RichTextTagStack HighlightStateStack; + // public TMP_RichTextTagStack ColorGradientStack; + // public TMP_RichTextTagStack SizeStack; + // public TMP_RichTextTagStack IndentStack; + // public TMP_RichTextTagStack FontWeightStack; + // + // public TMP_RichTextTagStack BaselineStack; + // //public TMP_RichTextTagStack ActionStack; + // public TMP_RichTextTagStack MaterialReferenceStack; + // public TMP_RichTextTagStack LineJustificationStack; + // } + + + // 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; @@ -283,7 +363,7 @@ public struct WordWrapState public float maxLineAscender; public float maxLineDescender; public float pageAscender; - + public HorizontalAlignmentOptions horizontalAlignment; public float marginLeft; public float marginRight; @@ -293,13 +373,13 @@ public struct WordWrapState public float preferredHeight; //public float maxFontScale; public float previousLineScale; - + public int wordCount; public FontStyles fontStyle; public int italicAngle; public float fontScale; public float fontScaleMultiplier; - + public float currentFontSize; public float baselineOffset; public float lineOffset; @@ -309,30 +389,28 @@ public struct WordWrapState public float mSpace; 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 italicAngleStack; - public TMP_RichTextTagStack colorStack; - public TMP_RichTextTagStack underlineColorStack; - public TMP_RichTextTagStack strikethroughColorStack; - public TMP_RichTextTagStack highlightColorStack; - public TMP_RichTextTagStack highlightStateStack; - public TMP_RichTextTagStack colorGradientStack; - public TMP_RichTextTagStack sizeStack; - public TMP_RichTextTagStack indentStack; - public TMP_RichTextTagStack fontWeightStack; - public TMP_RichTextTagStack styleStack; - public TMP_RichTextTagStack baselineStack; - public TMP_RichTextTagStack actionStack; - public TMP_RichTextTagStack materialReferenceStack; - public TMP_RichTextTagStack lineJustificationStack; - //public TMP_XmlTagStack spriteAnimationStack; + public TMP_TextProcessingStack italicAngleStack; + public TMP_TextProcessingStack colorStack; + public TMP_TextProcessingStack underlineColorStack; + public TMP_TextProcessingStack strikethroughColorStack; + public TMP_TextProcessingStack highlightColorStack; + public TMP_TextProcessingStack highlightStateStack; + public TMP_TextProcessingStack colorGradientStack; + public TMP_TextProcessingStack sizeStack; + public TMP_TextProcessingStack indentStack; + public TMP_TextProcessingStack fontWeightStack; + public TMP_TextProcessingStack styleStack; + public TMP_TextProcessingStack baselineStack; + public TMP_TextProcessingStack actionStack; + public TMP_TextProcessingStack materialReferenceStack; + public TMP_TextProcessingStack lineJustificationStack; public int spriteAnimationID; public TMP_FontAsset currentFontAsset; @@ -344,7 +422,6 @@ public struct WordWrapState public bool tagNoParsing; public bool isNonBreakingSpace; - //public Mesh_Extents lineExtents; } diff --git a/Scripts/Runtime/TMPro_Private.cs b/Scripts/Runtime/TMPro_Private.cs index 635094d..dfbcd6f 100644 --- a/Scripts/Runtime/TMPro_Private.cs +++ b/Scripts/Runtime/TMPro_Private.cs @@ -84,7 +84,6 @@ protected override void Awake() // Get reference to CanvasRenderer (if one exists) m_CanvasRenderer = GetComponent(); - if (m_CanvasRenderer != null) m_CanvasRenderer.hideFlags = HideFlags.HideInInspector; @@ -107,7 +106,7 @@ protected override void Awake() #if DEVELOPMENT_BUILD || UNITY_EDITOR m_mesh.name = "TextMeshPro Mesh"; #endif - m_meshFilter.mesh = m_mesh; + m_meshFilter.sharedMesh = m_mesh; // Create new TextInfo for the text object. m_textInfo = new TMP_TextInfo(this); @@ -138,7 +137,6 @@ protected override void Awake() // Set flags to ensure our text is parsed and redrawn. m_isInputParsingRequired = true; m_havePropertiesChanged = true; - m_isCalculateSizeRequired = true; m_isAwake = true; } @@ -197,7 +195,7 @@ protected override void OnDisable() TMP_UpdateManager.UnRegisterTextElementForRebuild(this); TMP_UpdateManager.UnRegisterTextObjectForUpdate(this); - m_meshFilter.sharedMesh = null; + meshFilter.sharedMesh = null; SetActiveSubMeshes(false); } @@ -208,9 +206,7 @@ protected override void OnDestroy() // 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 @@ -267,7 +263,6 @@ protected override void OnValidate() if (m_fontAsset == null || m_hasFontAssetChanged) { LoadFontAsset(); - m_isCalculateSizeRequired = true; m_hasFontAssetChanged = false; } @@ -277,7 +272,6 @@ protected override void OnValidate() m_isInputParsingRequired = true; m_inputSource = TextInputSources.Text; m_havePropertiesChanged = true; - m_isCalculateSizeRequired = true; m_isPreferredWidthDirty = true; m_isPreferredHeightDirty = true; @@ -366,6 +360,8 @@ void ON_MATERIAL_PROPERTY_CHANGED(bool isChanged, Material mat) // Event received when font asset properties are changed in Font Inspector void ON_FONT_PROPERTY_CHANGED(bool isChanged, TMP_FontAsset font) { + //Debug.Log("ON_FONT_PROPERTY_CHANGED event received. Target is [" + font.name + "]"); + if (MaterialReference.Contains(m_materialReferences, font)) { //Debug.Log("ON_FONT_PROPERTY_CHANGED event received."); @@ -484,36 +480,27 @@ protected override void LoadFontAsset() 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 + m_renderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off; } 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 + if (m_sharedMaterial == null || m_sharedMaterial.GetTexture(ShaderUtilities.ID_MainTex) == null || m_fontAsset.atlasTexture.GetInstanceID() != m_sharedMaterial.GetTexture(ShaderUtilities.ID_MainTex).GetInstanceID()) { - m_sharedMaterial = m_renderer.sharedMaterial; + 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; } - //m_sharedMaterial.SetFloat("_CullMode", 0); m_sharedMaterial.SetFloat(ShaderUtilities.ShaderTag_ZTestMode, 4); // Check if we are using the SDF Surface Shader @@ -526,18 +513,12 @@ protected override void LoadFontAsset() } 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; + SetMaterialDirty(); } @@ -1105,13 +1086,10 @@ protected override int SetArraySizes(UnicodeChar[] unicodeChars) 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) @@ -1138,156 +1116,27 @@ protected override int SetArraySizes(UnicodeChar[] unicodeChars) } #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); + TMP_TextElement character = GetTextElement((uint)unicode, m_currentFontAsset, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface); - // Search for the glyph in the list of fallback assigned to the primary font asset. - if (character == null) + // 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 (m_currentFontAsset.m_FallbackFontAssetTable != null && m_currentFontAsset.m_FallbackFontAssetTable.Count > 0) - character = TMP_FontAssetUtilities.GetCharacterFromFontAssets((uint)unicode, m_currentFontAsset.m_FallbackFontAssetTable, true, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface, out tempFontAsset); - } - - // Search for the glyph in the primary font asset if not the current font asset - if (character == null) - { - if (m_currentFontAsset.instanceID != m_fontAsset.instanceID) + if (char.IsLower((char)c)) { - // Search primary font asset - character = TMP_FontAssetUtilities.GetCharacterFromFontAsset((uint)unicode, m_fontAsset, false, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface, out tempFontAsset); - - // Use material and index of primary font asset. - if (character != null) - { - m_currentMaterialIndex = 0; - m_currentMaterial = m_materialReferences[0].material; - } - - // Search list of potential fallback font assets assigned to the primary font asset. - if (character == null) - { - if (m_fontAsset.m_FallbackFontAssetTable != null && m_fontAsset.m_FallbackFontAssetTable.Count > 0) - character = TMP_FontAssetUtilities.GetCharacterFromFontAssets((uint)unicode, m_fontAsset.m_FallbackFontAssetTable, true, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface, out tempFontAsset); - } + if (m_currentFontAsset.characterDictionary.TryGetValue(char.ToUpper((char)c), out glyph)) + c = chars[i] = char.ToUpper((char)c); } - } - - // Search for the glyph in the Sprite Asset assigned to the text object. - if (character == null) - { - TMP_SpriteAsset spriteAsset = this.spriteAsset; - - if (spriteAsset != null) + else if (char.IsUpper((char)c)) { - 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[spriteIndex]; - m_textInfo.characterInfo[m_totalCharacterCount].materialReferenceIndex = m_currentMaterialIndex; - m_textInfo.characterInfo[m_totalCharacterCount].index = unicodeChars[i].stringIndex; - m_textInfo.characterInfo[m_totalCharacterCount].stringLength = unicodeChars[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; - } + if (m_currentFontAsset.characterDictionary.TryGetValue(char.ToLower((char)c), out glyph)) + c = chars[i] = char.ToLower((char)c); } - } - - // 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[spriteIndex]; - m_textInfo.characterInfo[m_totalCharacterCount].materialReferenceIndex = m_currentMaterialIndex; - m_textInfo.characterInfo[m_totalCharacterCount].index = unicodeChars[i].stringIndex; - m_textInfo.characterInfo[m_totalCharacterCount].stringLength = unicodeChars[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); - // } - //} + }*/ + // Special handling for missing character. // Replace missing glyph by the Square (9633) glyph or possibly the Space (32) glyph. if (character == null) { @@ -1298,27 +1147,27 @@ protected override int SetArraySizes(UnicodeChar[] unicodeChars) unicode = unicodeChars[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); + character = TMP_FontAssetUtilities.GetCharacterFromFontAsset((uint)unicode, m_currentFontAsset, true, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface); 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); + character = TMP_FontAssetUtilities.GetCharacterFromFontAssets((uint)unicode, m_currentFontAsset, TMP_Settings.fallbackFontAssets, true, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface); } 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); + character = TMP_FontAssetUtilities.GetCharacterFromFontAsset((uint)unicode, TMP_Settings.defaultFontAsset, true, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface); } if (character == null) { // Use Space (32) Glyph from the currently assigned font asset. unicode = unicodeChars[i].unicode = 32; - character = TMP_FontAssetUtilities.GetCharacterFromFontAsset((uint)unicode, m_currentFontAsset, true, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface, out tempFontAsset); + character = TMP_FontAssetUtilities.GetCharacterFromFontAsset((uint)unicode, m_currentFontAsset, true, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface); if (!TMP_Settings.warningsDisabled) { string formattedWarning = srcGlyph > 0xFFFF @@ -1330,24 +1179,47 @@ protected override int SetArraySizes(UnicodeChar[] unicodeChars) } } - // Determine if the font asset is still the current font asset or a fallback. - if (tempFontAsset != null) + if (character.elementType == TextElementType.Character) { - if (tempFontAsset.instanceID != m_currentFontAsset.instanceID) + if (character.textAsset.instanceID != m_currentFontAsset.instanceID) { isUsingFallbackOrAlternativeTypeface = true; - m_currentFontAsset = tempFontAsset; + m_currentFontAsset = character.textAsset as TMP_FontAsset; + } } #endregion + // Save text element data 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 = unicodeChars[i].stringIndex; m_textInfo.characterInfo[m_totalCharacterCount].stringLength = unicodeChars[i].length; + m_textInfo.characterInfo[m_totalCharacterCount].fontAsset = m_currentFontAsset; + + // Special handling if the character is a sprite. + if (character.elementType == TextElementType.Sprite) + { + TMP_SpriteAsset spriteAssetRef = character.textAsset as TMP_SpriteAsset; + m_currentMaterialIndex = MaterialReference.AddMaterialReference(spriteAssetRef.material, spriteAssetRef, m_materialReferences, m_materialReferenceIndexLookup); + m_materialReferences[m_currentMaterialIndex].referenceCount += 1; + + m_textInfo.characterInfo[m_totalCharacterCount].elementType = TMP_TextElementType.Sprite; + m_textInfo.characterInfo[m_totalCharacterCount].materialReferenceIndex = m_currentMaterialIndex; + m_textInfo.characterInfo[m_totalCharacterCount].spriteAsset = spriteAssetRef; + m_textInfo.characterInfo[m_totalCharacterCount].spriteIndex = (int)character.glyphIndex; + + // Restore element type and material index to previous values. + m_textElementType = TMP_TextElementType.Character; + m_currentMaterialIndex = prev_materialIndex; + + spriteCount += 1; + m_totalCharacterCount += 1; + + continue; + } if (isUsingFallbackOrAlternativeTypeface && m_currentFontAsset.instanceID != m_fontAsset.instanceID) { @@ -1551,10 +1423,15 @@ protected override void OnRectTransformDimensionsChange() //Debug.Log("*** OnRectTransformDimensionsChange() ***"); // Ignore changes to RectTransform SizeDelta that are very small and typically the result of rounding errors when using RectTransform in Anchor Stretch mode. - if (rectTransform != null && Mathf.Abs(m_rectTransform.rect.width - m_RectTransformRect.width) < 0.0001f && Mathf.Abs(m_rectTransform.rect.height - m_RectTransformRect.height) < 0.0001f) + if (rectTransform != null && + Mathf.Abs(m_rectTransform.rect.width - m_PreviousRectTransformSize.x) < 0.0001f && Mathf.Abs(m_rectTransform.rect.height - m_PreviousRectTransformSize.y) < 0.0001f && + Mathf.Abs(m_rectTransform.pivot.x - m_PreviousPivotPosition.x) < 0.0001f && Mathf.Abs(m_rectTransform.pivot.y - m_PreviousPivotPosition.y) < 0.0001f) + { return; + } - m_RectTransformRect = m_rectTransform.rect; + m_PreviousRectTransformSize = m_rectTransform.rect.size; + m_PreviousPivotPosition = m_rectTransform.pivot; ComputeMarginSize(); @@ -1572,7 +1449,10 @@ internal override void InternalUpdate() if (m_havePropertiesChanged == false) { float lossyScaleY = m_rectTransform.lossyScale.y; - if (lossyScaleY != m_previousLossyScaleY) + + // Ignore very small lossy scale changes as their effect on SDF Scale would not be visually noticeable. + // Do not update SDF Scale if the text is null or empty + if (Mathf.Abs(lossyScaleY - m_previousLossyScaleY) > 0.0001f && m_InternalParsingBuffer[0].unicode != 0) { float scaleDelta = lossyScaleY / m_previousLossyScaleY; @@ -1819,6 +1699,7 @@ protected override void GenerateTextMesh() m_maxLineDescender = k_LargePositiveFloat; m_lineNumber = 0; m_startOfLineAscender = 0; + m_startOfLineDescender = 0; m_lineVisibleCharacterCount = 0; bool isStartOfNewLine = true; m_IsDrivenLineSpacing = false; @@ -1845,7 +1726,7 @@ protected override void GenerateTextMesh() // Tracking of the highest Ascender m_maxCapHeight = 0; - m_maxAscender = 0; + m_maxTextAscender = 0; m_ElementDescender = 0; m_PageAscender = 0; float maxVisibleDescender = 0; @@ -1856,7 +1737,7 @@ protected override void GenerateTextMesh() bool isFirstWordOfLine = true; m_isNonBreakingSpace = false; bool ignoreNonBreakingSpace = false; - bool isLastCharacterCJK = false; + //bool isLastCharacterCJK = false; int lastSoftLineBreak = 0; CharacterSubstitution characterToSubstitute = new CharacterSubstitution(-1, 0); @@ -1870,6 +1751,11 @@ protected override void GenerateTextMesh() SaveWordWrappingState(ref m_SavedLastValidState, -1, -1); SaveWordWrappingState(ref m_SavedSoftLineBreakState, -1, -1); + m_EllipsisInsertionCandidateStack.Clear(); + + // Safety Tracker + int restoreCount = 0; + #if TMP_PROFILE_ON Profiler.BeginSample("TMP GenerateText() - Phase I"); #endif @@ -1879,6 +1765,13 @@ protected override void GenerateTextMesh() { charCode = m_InternalParsingBuffer[i].unicode; + if (restoreCount > 5) + { + Debug.LogError("Line breaking recursion max threshold hit... Character [" + charCode + "] index: " + i); + characterToSubstitute.index = m_characterCount; + characterToSubstitute.unicode = 0x03; + } + // Parse Rich Text Tag #region Parse Rich Text Tag if (m_isRichText && charCode == 60) // '<' @@ -1919,7 +1812,7 @@ protected override void GenerateTextMesh() } #endregion End Parse Rich Text Tag - int prev_MaterialIndex = m_currentMaterialIndex; + int previousMaterialIndex = m_currentMaterialIndex; bool isUsingAltTypeface = m_textInfo.characterInfo[m_characterCount].isUsingAlternateTypeface; m_isParsingText = false; @@ -2057,7 +1950,7 @@ protected override void GenerateTextMesh() m_textInfo.characterInfo[m_characterCount].fontAsset = m_currentFontAsset; m_textInfo.characterInfo[m_characterCount].materialReferenceIndex = m_currentMaterialIndex; - m_currentMaterialIndex = prev_MaterialIndex; + m_currentMaterialIndex = previousMaterialIndex; padding = 0; } @@ -2070,16 +1963,18 @@ protected override void GenerateTextMesh() m_currentMaterial = m_textInfo.characterInfo[m_characterCount].material; m_currentMaterialIndex = m_textInfo.characterInfo[m_characterCount].materialReferenceIndex; + // Special handling if replaced character was a line feed where in this case we have to use the scale of the previous character. + float adjustedScale; if (isInjectingCharacter && m_InternalParsingBuffer[i].unicode == 0x0A && m_characterCount != m_firstCharacterOfLine) - m_fontScale = m_textInfo.characterInfo[m_characterCount - 1].pointSize * smallCapsMultiplier / m_currentFontAsset.m_FaceInfo.pointSize * m_currentFontAsset.m_FaceInfo.scale * (m_isOrthographic ? 1 : 0.1f); + adjustedScale = m_textInfo.characterInfo[m_characterCount - 1].pointSize * smallCapsMultiplier / m_currentFontAsset.m_FaceInfo.pointSize * m_currentFontAsset.m_FaceInfo.scale * (m_isOrthographic ? 1 : 0.1f); else - m_fontScale = m_currentFontSize * smallCapsMultiplier / m_currentFontAsset.m_FaceInfo.pointSize * m_currentFontAsset.m_FaceInfo.scale * (m_isOrthographic ? 1 : 0.1f); + adjustedScale = m_currentFontSize * smallCapsMultiplier / m_currentFontAsset.m_FaceInfo.pointSize * m_currentFontAsset.m_FaceInfo.scale * (m_isOrthographic ? 1 : 0.1f); elementAscentLine = m_currentFontAsset.m_FaceInfo.ascentLine; elementDescentLine = m_currentFontAsset.m_FaceInfo.descentLine; - currentElementScale = m_fontScale * m_fontScaleMultiplier * m_cached_TextElement.m_Scale * m_cached_TextElement.m_Glyph.scale; - baselineOffset = m_currentFontAsset.m_FaceInfo.baseline * m_fontScale * m_fontScaleMultiplier * m_currentFontAsset.m_FaceInfo.scale; + currentElementScale = adjustedScale * m_fontScaleMultiplier * m_cached_TextElement.m_Scale * m_cached_TextElement.m_Glyph.scale; + baselineOffset = m_currentFontAsset.m_FaceInfo.baseline * adjustedScale * m_fontScaleMultiplier * m_currentFontAsset.m_FaceInfo.scale; m_textInfo.characterInfo[m_characterCount].elementType = TMP_TextElementType.Character; m_textInfo.characterInfo[m_characterCount].scale = currentElementScale; @@ -2114,6 +2009,7 @@ protected override void GenerateTextMesh() // Optimization to avoid calling this more than once per character. bool isWhiteSpace = char.IsWhiteSpace((char)charCode); + //bool isVisibleCharacter = !isWhiteSpace; // Handle Kerning if Enabled. #region Handle Kerning @@ -2313,54 +2209,53 @@ protected override void GenerateTextMesh() ? elementAscentLine * currentElementScale / smallCapsMultiplier + m_baselineOffset : elementAscentLine * spriteScale + m_baselineOffset; - if (isInjectingCharacter && charCode != 0x03) - elementAscender = m_maxLineAscender; - // Element Descender in line space float elementDescender = m_textElementType == TMP_TextElementType.Character ? elementDescentLine * currentElementScale / smallCapsMultiplier + m_baselineOffset : elementDescentLine * spriteScale + m_baselineOffset; - if (isInjectingCharacter && charCode != 0x03) - elementDescender = m_maxLineDescender; + float adjustedAscender = elementAscender; + float adjustedDescender = elementDescender; + + bool isFirstCharacterOfLine = m_characterCount == m_firstCharacterOfLine; + // Max line ascender and descender in line space + if (isFirstCharacterOfLine || isWhiteSpace == false) + { + // Special handling for Superscript and Subscript where we use the unadjusted line ascender and descender + if (m_baselineOffset != 0) + { + adjustedAscender = Mathf.Max((elementAscender - m_baselineOffset) / m_fontScaleMultiplier, adjustedAscender); + adjustedDescender = Mathf.Min((elementDescender - m_baselineOffset) / m_fontScaleMultiplier, adjustedDescender); + } + + m_maxLineAscender = Mathf.Max(adjustedAscender, m_maxLineAscender); + m_maxLineDescender = Mathf.Min(adjustedDescender, m_maxLineDescender); + } // Element Ascender and Descender in object space - if (!isWhiteSpace || m_characterCount == m_firstCharacterOfLine) + if (isFirstCharacterOfLine || isWhiteSpace == false) { + m_textInfo.characterInfo[m_characterCount].adjustedAscender = adjustedAscender; + m_textInfo.characterInfo[m_characterCount].adjustedDescender = adjustedDescender; + m_ElementAscender = m_textInfo.characterInfo[m_characterCount].ascender = elementAscender - m_lineOffset; m_ElementDescender = m_textInfo.characterInfo[m_characterCount].descender = elementDescender - m_lineOffset; } else { + m_textInfo.characterInfo[m_characterCount].adjustedAscender = m_maxLineAscender; + m_textInfo.characterInfo[m_characterCount].adjustedDescender = m_maxLineDescender; + m_ElementAscender = m_textInfo.characterInfo[m_characterCount].ascender = m_maxLineAscender - m_lineOffset; m_ElementDescender = m_textInfo.characterInfo[m_characterCount].descender = m_maxLineDescender - m_lineOffset; } - // Max line ascender and descender in line space - if (!isWhiteSpace || m_characterCount == m_firstCharacterOfLine) - { - m_maxLineAscender = elementAscender > m_maxLineAscender ? elementAscender : m_maxLineAscender; - 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.m_FaceInfo.subscriptSize; - elementAscender = m_maxLineAscender; - m_maxLineAscender = baseAscender > m_maxLineAscender ? baseAscender : m_maxLineAscender; - - float baseDescender = (elementDescender - m_baselineOffset) / m_currentFontAsset.m_FaceInfo.subscriptSize; - //elementDescender = m_maxLineDescender; - m_maxLineDescender = baseDescender < m_maxLineDescender ? baseDescender : m_maxLineDescender; - } - // Max text object ascender and cap height if (m_lineNumber == 0 || m_isNewPage) { - if (!isWhiteSpace || m_characterCount == m_firstCharacterOfLine) + if (isFirstCharacterOfLine || isWhiteSpace == false) { - m_maxAscender = m_maxAscender > elementAscender ? m_maxAscender : elementAscender; + m_maxTextAscender = m_maxLineAscender; m_maxCapHeight = Mathf.Max(m_maxCapHeight, m_currentFontAsset.m_FaceInfo.capLine * currentElementScale / smallCapsMultiplier); } } @@ -2368,7 +2263,7 @@ protected override void GenerateTextMesh() // Page ascender if (m_lineOffset == 0) { - if (!isWhiteSpace || m_characterCount == m_firstCharacterOfLine) + if (isFirstCharacterOfLine || isWhiteSpace == false) m_PageAscender = m_PageAscender > elementAscender ? m_PageAscender : elementAscender; } #if TMP_PROFILE_ON @@ -2441,7 +2336,7 @@ protected override void GenerateTextMesh() // Calculate the line breaking width of the text. float textWidth = Mathf.Abs(m_xAdvance) + (!m_isRightToLeft ? currentGlyphMetrics.horizontalAdvance : 0) * (1 - m_charWidthAdjDelta) * (charCode == 0xAD ? currentElementUnmodifiedScale : currentElementScale); - float textHeight = m_maxAscender - m_ElementDescender + (m_lineOffset > 0 && m_IsDrivenLineSpacing == false ? m_maxLineAscender - m_startOfLineAscender : 0); + float textHeight = m_maxTextAscender - (m_maxLineDescender - m_lineOffset) + (m_lineOffset > 0 && m_IsDrivenLineSpacing == false ? m_maxLineAscender - m_startOfLineAscender : 0); int testedCharacterCount = m_characterCount; @@ -2503,21 +2398,25 @@ protected override void GenerateTextMesh() continue; case TextOverflowModes.Ellipsis: - i = RestoreWordWrappingState(ref m_SavedEllipsisState); - - if (i < 0 || testedCharacterCount == 0) + if (m_EllipsisInsertionCandidateStack.Count == 0) { i = -1; m_characterCount = 0; characterToSubstitute.index = 0; characterToSubstitute.unicode = 0x03; + m_firstCharacterOfLine = 0; continue; } + var ellipsisState = m_EllipsisInsertionCandidateStack.Pop(); + i = RestoreWordWrappingState(ref ellipsisState); + i -= 1; m_characterCount -= 1; characterToSubstitute.index = m_characterCount; characterToSubstitute.unicode = 0x2026; + + restoreCount += 1; continue; case TextOverflowModes.Linked: @@ -2569,7 +2468,7 @@ protected override void GenerateTextMesh() m_xAdvance = 0 + tag_Indent; m_lineOffset = 0; - m_maxAscender = 0; + m_maxTextAscender = 0; m_PageAscender = 0; m_lineNumber += 1; m_pageNumber += 1; @@ -2599,7 +2498,7 @@ protected override void GenerateTextMesh() float lineOffsetDelta = 0; if (m_lineHeight == TMP_Math.FLOAT_UNSET) { - float ascender = m_textInfo.characterInfo[m_characterCount].ascender - m_textInfo.characterInfo[m_characterCount].baseLine; + float ascender = m_textInfo.characterInfo[m_characterCount].adjustedAscender; lineOffsetDelta = (m_lineOffset > 0 && m_IsDrivenLineSpacing == false ? m_maxLineAscender - m_startOfLineAscender : 0) - m_maxLineDescender + ascender + (lineGap + m_lineSpacingDelta) * baseScale + m_lineSpacing * currentEmScale; } else @@ -2609,7 +2508,7 @@ protected override void GenerateTextMesh() } // Calculate new text height - float newTextHeight = m_maxAscender - m_textInfo.characterInfo[m_characterCount].descender + lineOffsetDelta; + float newTextHeight = m_maxTextAscender + lineOffsetDelta + m_lineOffset - m_textInfo.characterInfo[m_characterCount].adjustedDescender; // Replace Soft Hyphen by Hyphen Minus 0x2D #region Handle Soft Hyphenation @@ -2791,20 +2690,25 @@ protected override void GenerateTextMesh() continue; case TextOverflowModes.Ellipsis: - i = RestoreWordWrappingState(ref m_SavedEllipsisState); - - if (i < 0) + if (m_EllipsisInsertionCandidateStack.Count == 0) { + i = -1; m_characterCount = 0; characterToSubstitute.index = 0; characterToSubstitute.unicode = 0x03; + m_firstCharacterOfLine = 0; continue; } + var ellipsisState = m_EllipsisInsertionCandidateStack.Pop(); + i = RestoreWordWrappingState(ref ellipsisState); + i -= 1; m_characterCount -= 1; characterToSubstitute.index = m_characterCount; characterToSubstitute.unicode = 0x2026; + + restoreCount += 1; continue; case TextOverflowModes.Linked: @@ -2830,7 +2734,7 @@ protected override void GenerateTextMesh() m_startOfLineAscender = 0; m_lineOffset = 0; - m_maxAscender = 0; + m_maxTextAscender = 0; m_PageAscender = 0; m_pageNumber += 1; @@ -2948,20 +2852,25 @@ protected override void GenerateTextMesh() continue; case TextOverflowModes.Ellipsis: - i = RestoreWordWrappingState(ref m_SavedEllipsisState); - - if (i < 0) + if (m_EllipsisInsertionCandidateStack.Count == 0) { + i = -1; m_characterCount = 0; characterToSubstitute.index = 0; characterToSubstitute.unicode = 0x03; + m_firstCharacterOfLine = 0; continue; } + var ellipsisState = m_EllipsisInsertionCandidateStack.Pop(); + i = RestoreWordWrappingState(ref ellipsisState); + i -= 1; m_characterCount -= 1; characterToSubstitute.index = m_characterCount; characterToSubstitute.unicode = 0x2026; + + restoreCount += 1; continue; case TextOverflowModes.Linked: @@ -3087,12 +2996,15 @@ protected override void GenerateTextMesh() marginRight = m_textInfo.lineInfo[m_lineNumber].marginRight; } - float textHeight = m_maxAscender - m_ElementDescender + (m_lineOffset > 0 && m_IsDrivenLineSpacing == false ? m_maxLineAscender - m_startOfLineAscender : 0); + float textHeight = m_maxTextAscender - (m_maxLineDescender - m_lineOffset) + (m_lineOffset > 0 && m_IsDrivenLineSpacing == false ? m_maxLineAscender - m_startOfLineAscender : 0); float textWidth = Mathf.Abs(m_xAdvance) + (!m_isRightToLeft ? m_Ellipsis.character.m_Glyph.metrics.horizontalAdvance : 0) * (1 - m_charWidthAdjDelta) * scale; float widthOfTextAreaForEllipsis = m_width != -1 ? Mathf.Min(marginWidth + 0.0001f - marginLeft - marginRight, m_width) : marginWidth + 0.0001f - marginLeft - marginRight; if (textWidth < widthOfTextAreaForEllipsis * (isJustifiedOrFlush ? 1.05f : 1.0f) && textHeight < marginHeight + 0.0001f) + { SaveWordWrappingState(ref m_SavedEllipsisState, i, m_characterCount); + m_EllipsisInsertionCandidateStack.Push(m_SavedEllipsisState); + } } #endregion @@ -3175,7 +3087,7 @@ protected override void GenerateTextMesh() // Adjust current line spacing (if necessary) before inserting new line float baselineAdjustmentDelta = m_maxLineAscender - m_startOfLineAscender; - if (m_lineOffset > 0 && Math.Abs(baselineAdjustmentDelta) > 0.01f && m_IsDrivenLineSpacing == false && !m_isNewPage) // && isInjectingCharacter == false) + if (m_lineOffset > 0 && Math.Abs(baselineAdjustmentDelta) > 0.01f && m_IsDrivenLineSpacing == false && !m_isNewPage) { //Debug.Log("Line Feed - Adjusting Line Spacing on line #" + m_lineNumber); AdjustLineOffset(m_firstCharacterOfLine, m_characterCount, baselineAdjustmentDelta); @@ -3185,8 +3097,10 @@ protected override void GenerateTextMesh() // Adjust saved ellipsis state only if we are adjusting the same line number if (m_SavedEllipsisState.lineNumber == m_lineNumber) { + m_SavedEllipsisState = m_EllipsisInsertionCandidateStack.Pop(); m_SavedEllipsisState.startOfLineAscender += baselineAdjustmentDelta; m_SavedEllipsisState.lineOffset += baselineAdjustmentDelta; + m_EllipsisInsertionCandidateStack.Push(m_SavedEllipsisState); } } m_isNewPage = false; @@ -3247,7 +3161,7 @@ protected override void GenerateTextMesh() if (m_lineNumber >= m_textInfo.lineInfo.Length) ResizeLineExtents(m_lineNumber); - float lastVisibleAscender = m_textInfo.characterInfo[m_lastVisibleCharacterOfLine].ascender + m_lineOffset; + float lastVisibleAscender = m_textInfo.characterInfo[m_characterCount].adjustedAscender; // Apply Line Spacing with special handling for VT char(11) if (m_lineHeight == TMP_Math.FLOAT_UNSET) @@ -3264,12 +3178,10 @@ protected override void GenerateTextMesh() m_maxLineAscender = k_LargeNegativeFloat; m_maxLineDescender = k_LargePositiveFloat; - m_startOfLineAscender = lastVisibleAscender; // elementAscender; + m_startOfLineAscender = lastVisibleAscender; m_xAdvance = 0 + tag_LineIndent + tag_Indent; - //ellipsisIndex = m_characterCount - 1; - SaveWordWrappingState(ref m_SavedWordWrapState, i, m_characterCount); SaveWordWrappingState(ref m_SavedLastValidState, i, m_characterCount); @@ -3354,7 +3266,7 @@ protected override void GenerateTextMesh() // for Word Wrapping. SaveWordWrappingState(ref m_SavedWordWrapState, i, m_characterCount); isFirstWordOfLine = false; - isLastCharacterCJK = false; + //isLastCharacterCJK = false; // Reset soft line breaking point since we now have a valid hard break point. m_SavedSoftLineBreakState.previous_WordBreak = -1; @@ -3371,12 +3283,12 @@ protected override void GenerateTextMesh() charCode > 0xFE30 && charCode < 0xFE4F || /* CJK Compatibility Forms */ charCode > 0xFF00 && charCode < 0xFFEF))) /* CJK Halfwidth */ { - bool isLeadingCharacter = TMP_Settings.linebreakingRules.leadingCharacters.ContainsKey(charCode); - bool isFollowingCharacter = m_characterCount < totalCharacterCount - 1 && TMP_Settings.linebreakingRules.followingCharacters.ContainsKey(m_textInfo.characterInfo[m_characterCount + 1].character); + bool isCurrentLeadingCharacter = TMP_Settings.linebreakingRules.leadingCharacters.ContainsKey(charCode); + bool isNextFollowingCharacter = m_characterCount < totalCharacterCount - 1 && TMP_Settings.linebreakingRules.followingCharacters.ContainsKey(m_textInfo.characterInfo[m_characterCount + 1].character); - if (isFirstWordOfLine || isLeadingCharacter == false) + if (isCurrentLeadingCharacter == false) { - if (isFollowingCharacter == false) + if (isNextFollowingCharacter == false) { SaveWordWrappingState(ref m_SavedWordWrapState, i, m_characterCount); isFirstWordOfLine = false; @@ -3391,17 +3303,19 @@ protected override void GenerateTextMesh() SaveWordWrappingState(ref m_SavedWordWrapState, i, m_characterCount); } } + else + { + if (isFirstWordOfLine && isFirstCharacterOfLine) + { + // Special handling for non-breaking space and soft line breaks + if (isWhiteSpace) + SaveWordWrappingState(ref m_SavedSoftLineBreakState, i, m_characterCount); - isLastCharacterCJK = true; - } - else if (isLastCharacterCJK) - { - bool isLeadingCharacter = TMP_Settings.linebreakingRules.leadingCharacters.ContainsKey(charCode); - - if (isLeadingCharacter == false) - SaveWordWrappingState(ref m_SavedWordWrapState, i, m_characterCount); + SaveWordWrappingState(ref m_SavedWordWrapState, i, m_characterCount); + } + } - isLastCharacterCJK = false; + //isLastCharacterCJK = true; } else if (isFirstWordOfLine) { @@ -3410,7 +3324,7 @@ protected override void GenerateTextMesh() SaveWordWrappingState(ref m_SavedSoftLineBreakState, i, m_characterCount); SaveWordWrappingState(ref m_SavedWordWrapState, i, m_characterCount); - isLastCharacterCJK = false; + //isLastCharacterCJK = false; } #if TMP_PROFILE_ON @@ -3445,13 +3359,12 @@ protected override void GenerateTextMesh() #endregion End Auto-sizing Check m_IsAutoSizePointSizeSet = true; - //m_isCharacterWrappingEnabled = false; if (m_AutoSizeIterationCount >= m_AutoSizeMaxIterationCount) Debug.Log("Auto Size Iteration Count: " + m_AutoSizeIterationCount + ". Final Point Size: " + m_fontSize); - // If there are no visible characters... no need to continue - if (m_characterCount == 0) // && m_visibleSpriteCount == 0) + // If there are no visible characters or only character is End of Text (0x03)... no need to continue + if (m_characterCount == 0 || (m_characterCount == 1 && charCode == 0x03)) { ClearMesh(true); @@ -3484,7 +3397,7 @@ protected override void GenerateTextMesh() // Top Vertically case VerticalAlignmentOptions.Top: if (m_overflowMode != TextOverflowModes.Page) - anchorOffset = corners[1] + new Vector3(0 + margins.x, 0 - m_maxAscender - margins.y, 0); + anchorOffset = corners[1] + new Vector3(0 + margins.x, 0 - m_maxTextAscender - margins.y, 0); else anchorOffset = corners[1] + new Vector3(0 + margins.x, 0 - m_textInfo.pageInfo[pageToDisplay].ascender - margins.y, 0); break; @@ -3492,7 +3405,7 @@ protected override void GenerateTextMesh() // Middle Vertically case VerticalAlignmentOptions.Middle: 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); + anchorOffset = (corners[0] + corners[1]) / 2 + new Vector3(0 + margins.x, 0 - (m_maxTextAscender + 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; @@ -4427,13 +4340,10 @@ protected override void SetActiveSubMeshes(bool state) /// /// Destroy Sub Mesh Objects /// - protected override void ClearSubMeshObjects() + protected override void DestroySubMeshObjects() { for (int i = 1; i < m_subTextObjects.Length && m_subTextObjects[i] != null; i++) - { - Debug.Log("Destroying Sub Text object[" + i + "]."); DestroyImmediate(m_subTextObjects[i]); - } } diff --git a/Scripts/Runtime/TMPro_UGUI_Private.cs b/Scripts/Runtime/TMPro_UGUI_Private.cs index 99f6b1d..087e4cb 100644 --- a/Scripts/Runtime/TMPro_UGUI_Private.cs +++ b/Scripts/Runtime/TMPro_UGUI_Private.cs @@ -129,7 +129,6 @@ protected override void Awake() // Set flags to ensure our text is parsed and redrawn. m_isInputParsingRequired = true; m_havePropertiesChanged = true; - m_isCalculateSizeRequired = true; m_isAwake = true; } @@ -279,14 +278,12 @@ protected override void OnValidate() 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; } @@ -296,7 +293,6 @@ protected override void OnValidate() m_isInputParsingRequired = true; m_inputSource = TextInputSources.Text; m_havePropertiesChanged = true; - m_isCalculateSizeRequired = true; m_isPreferredWidthDirty = true; m_isPreferredHeightDirty = true; @@ -1161,13 +1157,10 @@ protected override int SetArraySizes(UnicodeChar[] unicodeChars) 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) @@ -1194,156 +1187,27 @@ protected override int SetArraySizes(UnicodeChar[] unicodeChars) } #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 current font asset. - if (character == null) - { - if (m_currentFontAsset.m_FallbackFontAssetTable != null && m_currentFontAsset.m_FallbackFontAssetTable.Count > 0) - character = TMP_FontAssetUtilities.GetCharacterFromFontAssets((uint)unicode, m_currentFontAsset.m_FallbackFontAssetTable, true, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface, out tempFontAsset); - } + TMP_TextElement character = GetTextElement((uint)unicode, m_currentFontAsset, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface); - // Search for the glyph in the primary font asset if not the current font asset - if (character == null) + // 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 (m_currentFontAsset.instanceID != m_fontAsset.instanceID) + if (char.IsLower((char)c)) { - // Search primary font asset - character = TMP_FontAssetUtilities.GetCharacterFromFontAsset((uint)unicode, m_fontAsset, false, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface, out tempFontAsset); - - // Use material and index of primary font asset. - if (character != null) - { - m_currentMaterialIndex = 0; - m_currentMaterial = m_materialReferences[0].material; - } - - // Search list of potential fallback font assets assigned to the primary font asset. - if (character == null) - { - if (m_fontAsset.m_FallbackFontAssetTable != null && m_fontAsset.m_FallbackFontAssetTable.Count > 0) - character = TMP_FontAssetUtilities.GetCharacterFromFontAssets((uint)unicode, m_fontAsset.m_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[spriteIndex]; - m_textInfo.characterInfo[m_totalCharacterCount].materialReferenceIndex = m_currentMaterialIndex; - m_textInfo.characterInfo[m_totalCharacterCount].index = unicodeChars[i].stringIndex; - m_textInfo.characterInfo[m_totalCharacterCount].stringLength = unicodeChars[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; - } + if (m_currentFontAsset.characterDictionary.TryGetValue(char.ToUpper((char)c), out glyph)) + c = chars[i] = char.ToUpper((char)c); } - } - - // 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) + else if (char.IsUpper((char)c)) { - 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[spriteIndex]; - m_textInfo.characterInfo[m_totalCharacterCount].materialReferenceIndex = m_currentMaterialIndex; - m_textInfo.characterInfo[m_totalCharacterCount].index = unicodeChars[i].stringIndex; - m_textInfo.characterInfo[m_totalCharacterCount].stringLength = unicodeChars[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; - } - + if (m_currentFontAsset.characterDictionary.TryGetValue(char.ToLower((char)c), out glyph)) + c = chars[i] = char.ToLower((char)c); } - } - - //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); - // } - //} + }*/ + // Special handling for missing character. // Replace missing glyph by the Square (9633) glyph or possibly the Space (32) glyph. if (character == null) { @@ -1354,27 +1218,27 @@ protected override int SetArraySizes(UnicodeChar[] unicodeChars) unicode = unicodeChars[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); + character = TMP_FontAssetUtilities.GetCharacterFromFontAsset((uint)unicode, m_currentFontAsset, true, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface); 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); + character = TMP_FontAssetUtilities.GetCharacterFromFontAssets((uint)unicode, m_currentFontAsset, TMP_Settings.fallbackFontAssets, true, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface); } 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); + character = TMP_FontAssetUtilities.GetCharacterFromFontAsset((uint)unicode, TMP_Settings.defaultFontAsset, true, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface); } if (character == null) { // Use Space (32) Glyph from the currently assigned font asset. unicode = unicodeChars[i].unicode = 32; - character = TMP_FontAssetUtilities.GetCharacterFromFontAsset((uint)unicode, m_currentFontAsset, true, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface, out tempFontAsset); + character = TMP_FontAssetUtilities.GetCharacterFromFontAsset((uint)unicode, m_currentFontAsset, true, m_FontStyleInternal, m_FontWeightInternal, out isUsingAlternativeTypeface); if (!TMP_Settings.warningsDisabled) { string formattedWarning = srcGlyph > 0xFFFF @@ -1386,24 +1250,47 @@ protected override int SetArraySizes(UnicodeChar[] unicodeChars) } } - // Determine if the font asset is still the current font asset or a fallback. - if (tempFontAsset != null) + if (character.elementType == TextElementType.Character) { - if (tempFontAsset.instanceID != m_currentFontAsset.instanceID) + if (character.textAsset.instanceID != m_currentFontAsset.instanceID) { isUsingFallbackOrAlternativeTypeface = true; - m_currentFontAsset = tempFontAsset; + m_currentFontAsset = character.textAsset as TMP_FontAsset; + } } #endregion + // Save text element data 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 = unicodeChars[i].stringIndex; m_textInfo.characterInfo[m_totalCharacterCount].stringLength = unicodeChars[i].length; + m_textInfo.characterInfo[m_totalCharacterCount].fontAsset = m_currentFontAsset; + + // Special handling if the character is a sprite. + if (character.elementType == TextElementType.Sprite) + { + TMP_SpriteAsset spriteAssetRef = character.textAsset as TMP_SpriteAsset; + m_currentMaterialIndex = MaterialReference.AddMaterialReference(spriteAssetRef.material, spriteAssetRef, m_materialReferences, m_materialReferenceIndexLookup); + m_materialReferences[m_currentMaterialIndex].referenceCount += 1; + + m_textInfo.characterInfo[m_totalCharacterCount].elementType = TMP_TextElementType.Sprite; + m_textInfo.characterInfo[m_totalCharacterCount].materialReferenceIndex = m_currentMaterialIndex; + m_textInfo.characterInfo[m_totalCharacterCount].spriteAsset = spriteAssetRef; + m_textInfo.characterInfo[m_totalCharacterCount].spriteIndex = (int)character.glyphIndex; + + // Restore element type and material index to previous values. + m_textElementType = TMP_TextElementType.Character; + m_currentMaterialIndex = prev_materialIndex; + + spriteCount += 1; + m_totalCharacterCount += 1; + + continue; + } if (isUsingFallbackOrAlternativeTypeface && m_currentFontAsset.instanceID != m_fontAsset.instanceID) { @@ -1640,10 +1527,15 @@ protected override void OnRectTransformDimensionsChange() return; // Ignore changes to RectTransform SizeDelta that are very small and typically the result of rounding errors when using RectTransform in Anchor Stretch mode. - if (rectTransform != null && Mathf.Abs(m_rectTransform.rect.width - m_RectTransformRect.width) < 0.0001f && Mathf.Abs(m_rectTransform.rect.height - m_RectTransformRect.height) < 0.0001f) + if (rectTransform != null && + Mathf.Abs(m_rectTransform.rect.width - m_PreviousRectTransformSize.x) < 0.0001f && Mathf.Abs(m_rectTransform.rect.height - m_PreviousRectTransformSize.y) < 0.0001f && + Mathf.Abs(m_rectTransform.pivot.x - m_PreviousPivotPosition.x) < 0.0001f && Mathf.Abs(m_rectTransform.pivot.y - m_PreviousPivotPosition.y) < 0.0001f) + { return; + } - m_RectTransformRect = m_rectTransform.rect; + m_PreviousRectTransformSize = m_rectTransform.rect.size; + m_PreviousPivotPosition = m_rectTransform.pivot; ComputeMarginSize(); @@ -1663,7 +1555,10 @@ internal override void InternalUpdate() if (m_havePropertiesChanged == false) { float lossyScaleY = m_rectTransform.lossyScale.y; - if (lossyScaleY != m_previousLossyScaleY) + + // Ignore very small lossy scale changes as their effect on SDF Scale would not be visually noticeable. + // Do not update SDF Scale if the text is null or empty + if (Mathf.Abs(lossyScaleY - m_previousLossyScaleY) > 0.0001f && m_InternalParsingBuffer[0].unicode != 0) { float scaleDelta = lossyScaleY / m_previousLossyScaleY; @@ -1917,6 +1812,7 @@ protected override void GenerateTextMesh() m_maxLineDescender = k_LargePositiveFloat; m_lineNumber = 0; m_startOfLineAscender = 0; + m_startOfLineDescender = 0; m_lineVisibleCharacterCount = 0; bool isStartOfNewLine = true; m_IsDrivenLineSpacing = false; @@ -1943,7 +1839,7 @@ protected override void GenerateTextMesh() // Tracking of the highest Ascender m_maxCapHeight = 0; - m_maxAscender = 0; + m_maxTextAscender = 0; m_ElementDescender = 0; m_PageAscender = 0; float maxVisibleDescender = 0; @@ -1954,7 +1850,7 @@ protected override void GenerateTextMesh() bool isFirstWordOfLine = true; m_isNonBreakingSpace = false; bool ignoreNonBreakingSpace = false; - bool isLastCharacterCJK = false; + //bool isLastCharacterCJK = false; int lastSoftLineBreak = 0; CharacterSubstitution characterToSubstitute = new CharacterSubstitution(-1, 0); @@ -1968,6 +1864,11 @@ protected override void GenerateTextMesh() SaveWordWrappingState(ref m_SavedLastValidState, -1, -1); SaveWordWrappingState(ref m_SavedSoftLineBreakState, -1, -1); + m_EllipsisInsertionCandidateStack.Clear(); + + // Safety Tracker + int restoreCount = 0; + #if TMP_PROFILE_ON Profiler.BeginSample("TMP GenerateText() - Phase I"); #endif @@ -1977,6 +1878,13 @@ protected override void GenerateTextMesh() { charCode = m_InternalParsingBuffer[i].unicode; + if (restoreCount > 5) + { + Debug.LogError("Line breaking recursion max threshold hit... Character [" + charCode + "] index: " + i); + characterToSubstitute.index = m_characterCount; + characterToSubstitute.unicode = 0x03; + } + // Parse Rich Text Tag #region Parse Rich Text Tag if (m_isRichText && charCode == 60) // '<' @@ -2017,7 +1925,7 @@ protected override void GenerateTextMesh() } #endregion End Parse Rich Text Tag - int prev_MaterialIndex = m_currentMaterialIndex; + int previousMaterialIndex = m_currentMaterialIndex; bool isUsingAltTypeface = m_textInfo.characterInfo[m_characterCount].isUsingAlternateTypeface; m_isParsingText = false; @@ -2155,7 +2063,7 @@ protected override void GenerateTextMesh() m_textInfo.characterInfo[m_characterCount].fontAsset = m_currentFontAsset; m_textInfo.characterInfo[m_characterCount].materialReferenceIndex = m_currentMaterialIndex; - m_currentMaterialIndex = prev_MaterialIndex; + m_currentMaterialIndex = previousMaterialIndex; padding = 0; } @@ -2168,16 +2076,18 @@ protected override void GenerateTextMesh() m_currentMaterial = m_textInfo.characterInfo[m_characterCount].material; m_currentMaterialIndex = m_textInfo.characterInfo[m_characterCount].materialReferenceIndex; + // Special handling if replaced character was a line feed where in this case we have to use the scale of the previous character. + float adjustedScale; if (isInjectingCharacter && m_InternalParsingBuffer[i].unicode == 0x0A && m_characterCount != m_firstCharacterOfLine) - m_fontScale = m_textInfo.characterInfo[m_characterCount - 1].pointSize * smallCapsMultiplier / m_currentFontAsset.m_FaceInfo.pointSize * m_currentFontAsset.m_FaceInfo.scale * (m_isOrthographic ? 1 : 0.1f); + adjustedScale = m_textInfo.characterInfo[m_characterCount - 1].pointSize * smallCapsMultiplier / m_currentFontAsset.m_FaceInfo.pointSize * m_currentFontAsset.m_FaceInfo.scale * (m_isOrthographic ? 1 : 0.1f); else - m_fontScale = m_currentFontSize * smallCapsMultiplier / m_currentFontAsset.m_FaceInfo.pointSize * m_currentFontAsset.m_FaceInfo.scale * (m_isOrthographic ? 1 : 0.1f); + adjustedScale = m_currentFontSize * smallCapsMultiplier / m_currentFontAsset.m_FaceInfo.pointSize * m_currentFontAsset.m_FaceInfo.scale * (m_isOrthographic ? 1 : 0.1f); elementAscentLine = m_currentFontAsset.m_FaceInfo.ascentLine; elementDescentLine = m_currentFontAsset.m_FaceInfo.descentLine; - currentElementScale = m_fontScale * m_fontScaleMultiplier * m_cached_TextElement.m_Scale * m_cached_TextElement.m_Glyph.scale; - baselineOffset = m_currentFontAsset.m_FaceInfo.baseline * m_fontScale * m_fontScaleMultiplier * m_currentFontAsset.m_FaceInfo.scale; + currentElementScale = adjustedScale * m_fontScaleMultiplier * m_cached_TextElement.m_Scale * m_cached_TextElement.m_Glyph.scale; + baselineOffset = m_currentFontAsset.m_FaceInfo.baseline * adjustedScale * m_fontScaleMultiplier * m_currentFontAsset.m_FaceInfo.scale; m_textInfo.characterInfo[m_characterCount].elementType = TMP_TextElementType.Character; m_textInfo.characterInfo[m_characterCount].scale = currentElementScale; @@ -2212,6 +2122,7 @@ protected override void GenerateTextMesh() // Optimization to avoid calling this more than once per character. bool isWhiteSpace = char.IsWhiteSpace((char)charCode); + //bool isVisibleCharacter = !isWhiteSpace; // Handle Kerning if Enabled. #region Handle Kerning @@ -2411,54 +2322,53 @@ protected override void GenerateTextMesh() ? elementAscentLine * currentElementScale / smallCapsMultiplier + m_baselineOffset : elementAscentLine * spriteScale + m_baselineOffset; - if (isInjectingCharacter && charCode != 0x03) - elementAscender = m_maxLineAscender; - // Element Descender in line space float elementDescender = m_textElementType == TMP_TextElementType.Character ? elementDescentLine * currentElementScale / smallCapsMultiplier + m_baselineOffset : elementDescentLine * spriteScale + m_baselineOffset; - if (isInjectingCharacter && charCode != 0x03) - elementDescender = m_maxLineDescender; + float adjustedAscender = elementAscender; + float adjustedDescender = elementDescender; + + bool isFirstCharacterOfLine = m_characterCount == m_firstCharacterOfLine; + // Max line ascender and descender in line space + if (isFirstCharacterOfLine || isWhiteSpace == false) + { + // Special handling for Superscript and Subscript where we use the unadjusted line ascender and descender + if (m_baselineOffset != 0) + { + adjustedAscender = Mathf.Max((elementAscender - m_baselineOffset) / m_fontScaleMultiplier, adjustedAscender); + adjustedDescender = Mathf.Min((elementDescender - m_baselineOffset) / m_fontScaleMultiplier, adjustedDescender); + } + + m_maxLineAscender = Mathf.Max(adjustedAscender, m_maxLineAscender); + m_maxLineDescender = Mathf.Min(adjustedDescender, m_maxLineDescender); + } // Element Ascender and Descender in object space - if (!isWhiteSpace || m_characterCount == m_firstCharacterOfLine) + if (isFirstCharacterOfLine || isWhiteSpace == false) { + m_textInfo.characterInfo[m_characterCount].adjustedAscender = adjustedAscender; + m_textInfo.characterInfo[m_characterCount].adjustedDescender = adjustedDescender; + m_ElementAscender = m_textInfo.characterInfo[m_characterCount].ascender = elementAscender - m_lineOffset; m_ElementDescender = m_textInfo.characterInfo[m_characterCount].descender = elementDescender - m_lineOffset; } else { + m_textInfo.characterInfo[m_characterCount].adjustedAscender = m_maxLineAscender; + m_textInfo.characterInfo[m_characterCount].adjustedDescender = m_maxLineDescender; + m_ElementAscender = m_textInfo.characterInfo[m_characterCount].ascender = m_maxLineAscender - m_lineOffset; m_ElementDescender = m_textInfo.characterInfo[m_characterCount].descender = m_maxLineDescender - m_lineOffset; } - // Max line ascender and descender in line space - if (!isWhiteSpace || m_characterCount == m_firstCharacterOfLine) - { - m_maxLineAscender = elementAscender > m_maxLineAscender ? elementAscender : m_maxLineAscender; - 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.m_FaceInfo.subscriptSize; - elementAscender = m_maxLineAscender; - m_maxLineAscender = baseAscender > m_maxLineAscender ? baseAscender : m_maxLineAscender; - - float baseDescender = (elementDescender - m_baselineOffset) / m_currentFontAsset.m_FaceInfo.subscriptSize; - //elementDescender = m_maxLineDescender; - m_maxLineDescender = baseDescender < m_maxLineDescender ? baseDescender : m_maxLineDescender; - } - // Max text object ascender and cap height if (m_lineNumber == 0 || m_isNewPage) { - if (!isWhiteSpace || m_characterCount == m_firstCharacterOfLine) + if (isFirstCharacterOfLine || isWhiteSpace == false) { - m_maxAscender = m_maxAscender > elementAscender ? m_maxAscender : elementAscender; + m_maxTextAscender = m_maxLineAscender; m_maxCapHeight = Mathf.Max(m_maxCapHeight, m_currentFontAsset.m_FaceInfo.capLine * currentElementScale / smallCapsMultiplier); } } @@ -2466,7 +2376,7 @@ protected override void GenerateTextMesh() // Page ascender if (m_lineOffset == 0) { - if (!isWhiteSpace || m_characterCount == m_firstCharacterOfLine) + if (isFirstCharacterOfLine || isWhiteSpace == false) m_PageAscender = m_PageAscender > elementAscender ? m_PageAscender : elementAscender; } #if TMP_PROFILE_ON @@ -2539,7 +2449,7 @@ protected override void GenerateTextMesh() // Calculate the line breaking width of the text. float textWidth = Mathf.Abs(m_xAdvance) + (!m_isRightToLeft ? currentGlyphMetrics.horizontalAdvance : 0) * (1 - m_charWidthAdjDelta) * (charCode == 0xAD ? currentElementUnmodifiedScale : currentElementScale); - float textHeight = m_maxAscender - m_ElementDescender + (m_lineOffset > 0 && m_IsDrivenLineSpacing == false ? m_maxLineAscender - m_startOfLineAscender : 0); + float textHeight = m_maxTextAscender - (m_maxLineDescender - m_lineOffset) + (m_lineOffset > 0 && m_IsDrivenLineSpacing == false ? m_maxLineAscender - m_startOfLineAscender : 0); int testedCharacterCount = m_characterCount; @@ -2601,21 +2511,25 @@ protected override void GenerateTextMesh() continue; case TextOverflowModes.Ellipsis: - i = RestoreWordWrappingState(ref m_SavedEllipsisState); - - if (i < 0 || testedCharacterCount == 0) + if (m_EllipsisInsertionCandidateStack.Count == 0) { i = -1; m_characterCount = 0; characterToSubstitute.index = 0; characterToSubstitute.unicode = 0x03; + m_firstCharacterOfLine = 0; continue; } + var ellipsisState = m_EllipsisInsertionCandidateStack.Pop(); + i = RestoreWordWrappingState(ref ellipsisState); + i -= 1; m_characterCount -= 1; characterToSubstitute.index = m_characterCount; characterToSubstitute.unicode = 0x2026; + + restoreCount += 1; continue; case TextOverflowModes.Linked: @@ -2667,7 +2581,7 @@ protected override void GenerateTextMesh() m_xAdvance = 0 + tag_Indent; m_lineOffset = 0; - m_maxAscender = 0; + m_maxTextAscender = 0; m_PageAscender = 0; m_lineNumber += 1; m_pageNumber += 1; @@ -2697,7 +2611,7 @@ protected override void GenerateTextMesh() float lineOffsetDelta = 0; if (m_lineHeight == TMP_Math.FLOAT_UNSET) { - float ascender = m_textInfo.characterInfo[m_characterCount].ascender - m_textInfo.characterInfo[m_characterCount].baseLine; + float ascender = m_textInfo.characterInfo[m_characterCount].adjustedAscender; lineOffsetDelta = (m_lineOffset > 0 && m_IsDrivenLineSpacing == false ? m_maxLineAscender - m_startOfLineAscender : 0) - m_maxLineDescender + ascender + (lineGap + m_lineSpacingDelta) * baseScale + m_lineSpacing * currentEmScale; } else @@ -2707,7 +2621,7 @@ protected override void GenerateTextMesh() } // Calculate new text height - float newTextHeight = m_maxAscender - m_textInfo.characterInfo[m_characterCount].descender + lineOffsetDelta; + float newTextHeight = m_maxTextAscender + lineOffsetDelta + m_lineOffset - m_textInfo.characterInfo[m_characterCount].adjustedDescender; // Replace Soft Hyphen by Hyphen Minus 0x2D #region Handle Soft Hyphenation @@ -2889,20 +2803,25 @@ protected override void GenerateTextMesh() continue; case TextOverflowModes.Ellipsis: - i = RestoreWordWrappingState(ref m_SavedEllipsisState); - - if (i < 0) + if (m_EllipsisInsertionCandidateStack.Count == 0) { + i = -1; m_characterCount = 0; characterToSubstitute.index = 0; characterToSubstitute.unicode = 0x03; + m_firstCharacterOfLine = 0; continue; } + var ellipsisState = m_EllipsisInsertionCandidateStack.Pop(); + i = RestoreWordWrappingState(ref ellipsisState); + i -= 1; m_characterCount -= 1; characterToSubstitute.index = m_characterCount; characterToSubstitute.unicode = 0x2026; + + restoreCount += 1; continue; case TextOverflowModes.Linked: @@ -2928,7 +2847,7 @@ protected override void GenerateTextMesh() m_startOfLineAscender = 0; m_lineOffset = 0; - m_maxAscender = 0; + m_maxTextAscender = 0; m_PageAscender = 0; m_pageNumber += 1; @@ -3046,20 +2965,25 @@ protected override void GenerateTextMesh() continue; case TextOverflowModes.Ellipsis: - i = RestoreWordWrappingState(ref m_SavedEllipsisState); - - if (i < 0) + if (m_EllipsisInsertionCandidateStack.Count == 0) { + i = -1; m_characterCount = 0; characterToSubstitute.index = 0; characterToSubstitute.unicode = 0x03; + m_firstCharacterOfLine = 0; continue; } + var ellipsisState = m_EllipsisInsertionCandidateStack.Pop(); + i = RestoreWordWrappingState(ref ellipsisState); + i -= 1; m_characterCount -= 1; characterToSubstitute.index = m_characterCount; characterToSubstitute.unicode = 0x2026; + + restoreCount += 1; continue; case TextOverflowModes.Linked: @@ -3185,12 +3109,15 @@ protected override void GenerateTextMesh() marginRight = m_textInfo.lineInfo[m_lineNumber].marginRight; } - float textHeight = m_maxAscender - m_ElementDescender + (m_lineOffset > 0 && m_IsDrivenLineSpacing == false ? m_maxLineAscender - m_startOfLineAscender : 0); + float textHeight = m_maxTextAscender - (m_maxLineDescender - m_lineOffset) + (m_lineOffset > 0 && m_IsDrivenLineSpacing == false ? m_maxLineAscender - m_startOfLineAscender : 0); float textWidth = Mathf.Abs(m_xAdvance) + (!m_isRightToLeft ? m_Ellipsis.character.m_Glyph.metrics.horizontalAdvance : 0) * (1 - m_charWidthAdjDelta) * scale; float widthOfTextAreaForEllipsis = m_width != -1 ? Mathf.Min(marginWidth + 0.0001f - marginLeft - marginRight, m_width) : marginWidth + 0.0001f - marginLeft - marginRight; if (textWidth < widthOfTextAreaForEllipsis * (isJustifiedOrFlush ? 1.05f : 1.0f) && textHeight < marginHeight + 0.0001f) + { SaveWordWrappingState(ref m_SavedEllipsisState, i, m_characterCount); + m_EllipsisInsertionCandidateStack.Push(m_SavedEllipsisState); + } } #endregion @@ -3273,7 +3200,7 @@ protected override void GenerateTextMesh() // Adjust current line spacing (if necessary) before inserting new line float baselineAdjustmentDelta = m_maxLineAscender - m_startOfLineAscender; - if (m_lineOffset > 0 && Math.Abs(baselineAdjustmentDelta) > 0.01f && m_IsDrivenLineSpacing == false && !m_isNewPage) // && isInjectingCharacter == false) + if (m_lineOffset > 0 && Math.Abs(baselineAdjustmentDelta) > 0.01f && m_IsDrivenLineSpacing == false && !m_isNewPage) { //Debug.Log("Line Feed - Adjusting Line Spacing on line #" + m_lineNumber); AdjustLineOffset(m_firstCharacterOfLine, m_characterCount, baselineAdjustmentDelta); @@ -3283,8 +3210,10 @@ protected override void GenerateTextMesh() // Adjust saved ellipsis state only if we are adjusting the same line number if (m_SavedEllipsisState.lineNumber == m_lineNumber) { + m_SavedEllipsisState = m_EllipsisInsertionCandidateStack.Pop(); m_SavedEllipsisState.startOfLineAscender += baselineAdjustmentDelta; m_SavedEllipsisState.lineOffset += baselineAdjustmentDelta; + m_EllipsisInsertionCandidateStack.Push(m_SavedEllipsisState); } } m_isNewPage = false; @@ -3345,7 +3274,7 @@ protected override void GenerateTextMesh() if (m_lineNumber >= m_textInfo.lineInfo.Length) ResizeLineExtents(m_lineNumber); - float lastVisibleAscender = m_textInfo.characterInfo[m_lastVisibleCharacterOfLine].ascender + m_lineOffset; + float lastVisibleAscender = m_textInfo.characterInfo[m_characterCount].adjustedAscender; // Apply Line Spacing with special handling for VT char(11) if (m_lineHeight == TMP_Math.FLOAT_UNSET) @@ -3362,12 +3291,10 @@ protected override void GenerateTextMesh() m_maxLineAscender = k_LargeNegativeFloat; m_maxLineDescender = k_LargePositiveFloat; - m_startOfLineAscender = lastVisibleAscender; // elementAscender; + m_startOfLineAscender = lastVisibleAscender; m_xAdvance = 0 + tag_LineIndent + tag_Indent; - //ellipsisIndex = m_characterCount - 1; - SaveWordWrappingState(ref m_SavedWordWrapState, i, m_characterCount); SaveWordWrappingState(ref m_SavedLastValidState, i, m_characterCount); @@ -3452,7 +3379,7 @@ protected override void GenerateTextMesh() // for Word Wrapping. SaveWordWrappingState(ref m_SavedWordWrapState, i, m_characterCount); isFirstWordOfLine = false; - isLastCharacterCJK = false; + //isLastCharacterCJK = false; // Reset soft line breaking point since we now have a valid hard break point. m_SavedSoftLineBreakState.previous_WordBreak = -1; @@ -3469,12 +3396,12 @@ protected override void GenerateTextMesh() charCode > 0xFE30 && charCode < 0xFE4F || /* CJK Compatibility Forms */ charCode > 0xFF00 && charCode < 0xFFEF))) /* CJK Halfwidth */ { - bool isLeadingCharacter = TMP_Settings.linebreakingRules.leadingCharacters.ContainsKey(charCode); - bool isFollowingCharacter = m_characterCount < totalCharacterCount - 1 && TMP_Settings.linebreakingRules.followingCharacters.ContainsKey(m_textInfo.characterInfo[m_characterCount + 1].character); + bool isCurrentLeadingCharacter = TMP_Settings.linebreakingRules.leadingCharacters.ContainsKey(charCode); + bool isNextFollowingCharacter = m_characterCount < totalCharacterCount - 1 && TMP_Settings.linebreakingRules.followingCharacters.ContainsKey(m_textInfo.characterInfo[m_characterCount + 1].character); - if (isFirstWordOfLine || isLeadingCharacter == false) + if (isCurrentLeadingCharacter == false) { - if (isFollowingCharacter == false) + if (isNextFollowingCharacter == false) { SaveWordWrappingState(ref m_SavedWordWrapState, i, m_characterCount); isFirstWordOfLine = false; @@ -3489,17 +3416,19 @@ protected override void GenerateTextMesh() SaveWordWrappingState(ref m_SavedWordWrapState, i, m_characterCount); } } + else + { + if (isFirstWordOfLine && isFirstCharacterOfLine) + { + // Special handling for non-breaking space and soft line breaks + if (isWhiteSpace) + SaveWordWrappingState(ref m_SavedSoftLineBreakState, i, m_characterCount); - isLastCharacterCJK = true; - } - else if (isLastCharacterCJK) - { - bool isLeadingCharacter = TMP_Settings.linebreakingRules.leadingCharacters.ContainsKey(charCode); - - if (isLeadingCharacter == false) - SaveWordWrappingState(ref m_SavedWordWrapState, i, m_characterCount); + SaveWordWrappingState(ref m_SavedWordWrapState, i, m_characterCount); + } + } - isLastCharacterCJK = false; + //isLastCharacterCJK = true; } else if (isFirstWordOfLine) { @@ -3508,7 +3437,7 @@ protected override void GenerateTextMesh() SaveWordWrappingState(ref m_SavedSoftLineBreakState, i, m_characterCount); SaveWordWrappingState(ref m_SavedWordWrapState, i, m_characterCount); - isLastCharacterCJK = false; + //isLastCharacterCJK = false; } #if TMP_PROFILE_ON @@ -3543,13 +3472,12 @@ protected override void GenerateTextMesh() #endregion End Auto-sizing Check m_IsAutoSizePointSizeSet = true; - //m_isCharacterWrappingEnabled = false; if (m_AutoSizeIterationCount >= m_AutoSizeMaxIterationCount) Debug.Log("Auto Size Iteration Count: " + m_AutoSizeIterationCount + ". Final Point Size: " + m_fontSize); - // If there are no visible characters... no need to continue - if (m_characterCount == 0) // && m_visibleSpriteCount == 0) + // If there are no visible characters or only character is End of Text (0x03)... no need to continue + if (m_characterCount == 0 || (m_characterCount == 1 && charCode == 0x03)) { ClearMesh(); @@ -3582,7 +3510,7 @@ protected override void GenerateTextMesh() // Top Vertically case VerticalAlignmentOptions.Top: if (m_overflowMode != TextOverflowModes.Page) - anchorOffset = corners[1] + new Vector3(0 + margins.x, 0 - m_maxAscender - margins.y, 0); + anchorOffset = corners[1] + new Vector3(0 + margins.x, 0 - m_maxTextAscender - margins.y, 0); else anchorOffset = corners[1] + new Vector3(0 + margins.x, 0 - m_textInfo.pageInfo[pageToDisplay].ascender - margins.y, 0); break; @@ -3590,7 +3518,7 @@ protected override void GenerateTextMesh() // Middle Vertically case VerticalAlignmentOptions.Middle: 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); + anchorOffset = (corners[0] + corners[1]) / 2 + new Vector3(0 + margins.x, 0 - (m_maxTextAscender + 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; @@ -4447,7 +4375,7 @@ protected override void GenerateTextMesh() //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. + // 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)); @@ -4531,6 +4459,16 @@ protected override void SetActiveSubMeshes(bool state) } + /// + /// Destroy Sub Mesh Objects + /// + protected override void DestroySubMeshObjects() + { + for (int i = 1; i < m_subTextObjects.Length && m_subTextObjects[i] != null; i++) + DestroyImmediate(m_subTextObjects[i]); + } + + /// /// Method returning the compound bounds of the text object and child sub objects. /// @@ -4556,6 +4494,14 @@ protected override Bounds GetCompoundBounds() return new Bounds(center, size); } + internal override Rect GetCanvasSpaceClippingRect() + { + Transform rootCanvasTransform = m_canvas.rootCanvas.transform; + Bounds compoundBounds = GetCompoundBounds(); + + return new Rect(rootCanvasTransform.InverseTransformPoint(m_rectTransform.position + compoundBounds.min), compoundBounds.size); + } + //public override void UpdateGeometry() //{ diff --git a/Scripts/Runtime/TextMeshPro.cs b/Scripts/Runtime/TextMeshPro.cs index 7d38e42..1ad157c 100644 --- a/Scripts/Runtime/TextMeshPro.cs +++ b/Scripts/Runtime/TextMeshPro.cs @@ -9,7 +9,6 @@ namespace TMPro [DisallowMultipleComponent] [RequireComponent(typeof(MeshRenderer))] - [RequireComponent(typeof(MeshFilter))] [AddComponentMenu("Mesh/TextMeshPro - Text")] [ExecuteAlways] [HelpURL("https://docs.unity3d.com/Packages/com.unity.textmeshpro@1.5")] @@ -26,17 +25,20 @@ public int sortingLayerID { if (renderer == null) return 0; - + return m_renderer.sortingLayerID; } set { if (renderer == null) return; - + m_renderer.sortingLayerID = value; + _SortingLayerID = value; } } + [SerializeField] + internal int _SortingLayerID; /// /// Sets the Renderer's sorting order within the assigned layer. @@ -47,7 +49,7 @@ public int sortingOrder { if (renderer == null) return 0; - + return m_renderer.sortingOrder; } set @@ -56,8 +58,11 @@ public int sortingOrder return; m_renderer.sortingOrder = value; + _SortingOrder = value; } } + [SerializeField] + internal int _SortingOrder; /// /// Determines if the size of the text container will be adjusted to fit the text object when it is first created. @@ -92,7 +97,7 @@ public TextContainer textContainer { if (m_transform == null) m_transform = GetComponent(); - + return m_transform; } } @@ -125,7 +130,6 @@ public override Mesh mesh { m_mesh = new Mesh(); m_mesh.hideFlags = HideFlags.HideAndDontSave; - this.meshFilter.mesh = m_mesh; } return m_mesh; @@ -140,15 +144,23 @@ public MeshFilter meshFilter get { if (m_meshFilter == null) + { m_meshFilter = GetComponent(); + if (m_meshFilter == null) + { + m_meshFilter = gameObject.AddComponent(); + m_meshFilter.hideFlags = HideFlags.HideInInspector | HideFlags.HideAndDontSave; + } + } + return m_meshFilter; } } // MASKING RELATED PROPERTIES /// - /// Sets the mask type + /// Sets the mask type /// public MaskingTypes maskType { @@ -199,7 +211,7 @@ public override void SetVerticesDirty() /// - /// + /// /// public override void SetLayoutDirty() { @@ -209,6 +221,8 @@ public override void SetLayoutDirty() if (this == null || !this.IsActive()) return; + LayoutRebuilder.MarkLayoutForRebuild(this.rectTransform); + m_isLayoutDirty = true; } @@ -230,7 +244,7 @@ public override void SetMaterialDirty() /// - /// + /// /// public override void SetAllDirty() { @@ -243,7 +257,7 @@ public override void SetAllDirty() /// - /// + /// /// /// public override void Rebuild(CanvasUpdate update) @@ -270,7 +284,7 @@ public override void Rebuild(CanvasUpdate update) /// - /// + /// /// protected override void UpdateMaterial() { @@ -458,102 +472,8 @@ public void UpdateFontAsset() 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); + public void CalculateLayoutInputHorizontal() { } - 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; - } + public void CalculateLayoutInputVertical() { } } } diff --git a/Scripts/Runtime/TextMeshProUGUI.cs b/Scripts/Runtime/TextMeshProUGUI.cs index ecab674..c3af333 100644 --- a/Scripts/Runtime/TextMeshProUGUI.cs +++ b/Scripts/Runtime/TextMeshProUGUI.cs @@ -72,26 +72,12 @@ public override Mesh mesh private Coroutine m_DelayedGraphicRebuild; private Coroutine m_DelayedMaterialRebuild; - /// /// Function called by Unity when the horizontal layout needs to be recalculated. /// 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; - } + //Debug.Log("*** CalculateLayoutHorizontal() on Object ID: " + GetInstanceID() + " at frame: " + Time.frameCount + "***"); } @@ -100,22 +86,7 @@ public void CalculateLayoutInputHorizontal() /// 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; + //Debug.Log("*** CalculateLayoutInputVertical() on Object ID: " + GetInstanceID() + " at frame: " + Time.frameCount + "***"); } @@ -399,14 +370,16 @@ public override void Cull(Rect clipRect, bool validRect) { //Debug.Log("***** Cull (" + clipRect + ", " + validRect + ") Cull: " + m_canvasRenderer.cull + " *****"); - if (validRect && ignoreClipping) + // Get compound rect for the text object and sub text objects in local canvas space. + Rect rect = GetCanvasSpaceClippingRect(); + + var cull = !validRect || !clipRect.Overlaps(rect, true); + if (m_canvasRenderer.cull != cull) { - m_canvasRenderer.cull = false; - CanvasUpdateRegistry.RegisterCanvasElementForGraphicRebuild(this); - return; + m_canvasRenderer.cull = cull; + onCullStateChanged.Invoke(cull); + OnCullingChanged(); } - - base.Cull(clipRect, validRect); } diff --git a/package.json b/package.json index 1e76aa2..7c59e38 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "com.unity.textmeshpro", "displayName": "TextMeshPro", - "version": "1.5.0-preview.8", + "version": "1.5.0-preview.10", "unity": "2018.3", "description": "TextMeshPro is the ultimate text solution for Unity. It's the perfect replacement for Unity's UI Text and the legacy Text Mesh.\n\nPowerful and easy to use, TextMeshPro (also known as TMP) uses Advanced Text Rendering techniques along with a set of custom shaders; delivering substantial visual quality improvements while giving users incredible flexibility when it comes to text styling and texturing.\n\nTextMeshPro provides Improved Control over text formatting and layout with features like character, word, line and paragraph spacing, kerning, justified text, Links, over 30 Rich Text Tags available, support for Multi Font & Sprites, Custom Styles and more.\n\nGreat performance. Since the geometry created by TextMeshPro uses two triangles per character just like Unity's text components, this improved visual quality and flexibility comes at no additional performance cost.", "keywords": [ @@ -14,9 +14,11 @@ "category": "Text Rendering", "dependencies": {}, "repository": { - "footprint": "b7c92170340265a4df546a3cbf496f74800249df", "type": "git", "url": "https://github.cds.internal.unity3d.com/unity/com.unity.textmeshpro.git", - "revision": "5b7f0f0a256afd59f753efd7b22b0a2f8fcc6e85" + "revision": "74330e3ce2eb0fa4718f0a94c392d2ba457f48c0" + }, + "upmCi": { + "footprint": "8a04ffb570a20d31883438eeaa26629c904e0b23" } }