Adaug proiectul Unity

This commit is contained in:
2026-11-01 23:39:55 +02:00
parent cb14d9d37d
commit 863eac8f2c
23463 changed files with 5412601 additions and 0 deletions
@@ -0,0 +1,3 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Unity.TextMeshPro.Editor.Tests")]
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 06b06f5f5563e9a43b274eadc5373cc0
@@ -0,0 +1,52 @@
using UnityEngine.TestTools;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace TMPro
{
/// <summary>
/// Imports the TMP Essential Resources into the test project before the test run starts, so that
/// fixtures relying on TMP_Settings and the default font asset have them available.
/// </summary>
/// <remarks>
/// This runs in the Editor for Edit Mode and Play Mode runs alike, and ahead of a player build,
/// which is why it lives in the Play Mode assembly with an editor-only body.
///
/// To reduce boilerplate derive from <see cref="TMPTestSuiteWithEssentialResources"/> rather than referencing this
/// directly.
/// </remarks>
internal class TMPEssentialResourcesImporter : IPrebuildSetup
{
/// <summary>
/// GUID of the "TextMesh Pro" folder that importing the Essential Resources creates.
/// </summary>
const string k_EssentialResourcesFolderGUID = "f54d1bd14bd3ca042bd867b519fee8cc";
/// <summary>
/// GUID for Roboto-Bold.ttf, which ships in Examples and Extras rather than the Essential Resources.
/// </summary>
internal const string k_ExampleFontGUID = "4beb055f07aaff244873dec698d0363e";
public virtual void Setup()
{
Import(essentials: true, examples: false);
}
protected static void Import(bool essentials, bool examples)
{
#if UNITY_EDITOR
var importEssentials = essentials
&& string.IsNullOrEmpty(AssetDatabase.GUIDToAssetPath(k_EssentialResourcesFolderGUID));
var importExamples = examples
&& string.IsNullOrEmpty(AssetDatabase.GUIDToAssetPath(k_ExampleFontGUID));
if (!importEssentials && !importExamples)
return;
TMP_PackageResourceImporter.ImportResources(importEssentials, importExamples, false);
AssetDatabase.Refresh();
#endif
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 2b3c35f9f9f3cb04b96b5cd06fc548bd
@@ -0,0 +1,25 @@
using NUnit.Framework;
using UnityEngine.TestTools;
namespace TMPro
{
/// <summary>
/// Base fixture for TMP tests that need the TMP Essential Resources.
/// </summary>
[PrebuildSetup(typeof(TMPEssentialResourcesImporter))]
internal abstract class TMPTestSuiteWithEssentialResources
{
/// <summary>
/// Checks that the resources arrived before any test in the fixture runs. Override to add
/// fixture-specific setup, ensuring to call base.OneTimeSetup().
/// </summary>
[OneTimeSetUp]
public virtual void OneTimeSetup()
{
Assert.IsNotNull(TMP_Settings.instance,
$"TMP_Settings could not be loaded, so {nameof(TMPEssentialResourcesImporter)} did not make the TMP Essential Resources available to this fixture.");
Assert.IsNotNull(TMP_Settings.defaultFontAsset,
$"TMP_Settings has no default font asset, so {nameof(TMPEssentialResourcesImporter)} did not make the TMP Essential Resources available to this fixture.");
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 897920160c2045844a3119a3a3d937fb
@@ -0,0 +1,47 @@
using UnityEngine;
using NUnit.Framework;
using System.IO;
namespace TMPro
{
internal class TMP_CanvasTests
{
[OneTimeSetUp]
public void OneTimeSetUp()
{
if (!Directory.Exists(Path.GetFullPath("Assets/TextMesh Pro")))
{
Debug.Log("Skipping over Editor tests as TMP Essential Resources are missing from the current test project.");
Assert.Ignore();
return;
}
}
[Test]
public void EnablingAndDisablingCanvasDoesNotRegenerateText() // (UUM-45320)
{
var go = new GameObject();
var canvas = go.AddComponent<Canvas>();
var goChild = new GameObject();
var text = goChild.AddComponent<TextMeshProUGUI>();
goChild.transform.SetParent(go.transform);
// Force text to be generated
text.text = "Hello World";
text.ForceMeshUpdate();
Assert.IsFalse(text.havePropertiesChanged, "Text should not have changed yet");
canvas.enabled = false;
canvas.enabled = true;
Assert.IsFalse(text.havePropertiesChanged, "Text should not have changed after enabling / disabling canvas");
// Cleanup
Object.DestroyImmediate(go);
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 5348cbfcd4f74699b33fe6b43747a016
timeCreated: 1703006962
@@ -0,0 +1,268 @@
using System.IO;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.TestTools;
using UnityEngine.UI;
namespace TMPro
{
/// <summary>
/// End to end coverage for the reported bug: "TextMeshPro input field doesn't remove
/// characters from the first backspace when variant characters are used with the input
/// field", where a variant character is a base character followed by a variation selector -
/// typically an Ideographic Variation Sequence, whose selector (U+E0100 and up) is a
/// surrogate pair.
///
/// In its default editing mode the input field resolves every delete range and caret step
/// through textInfo.characterInfo[n].index and .stringLength. SetArraySizes used to record
/// the span of the last code point consumed by the variation selector block instead of the
/// span of the whole cluster, so the first Backspace either removed the wrong span or
/// indexed a characterInfo slot past characterCount and removed nothing. The span mapping
/// itself is covered by TMP_CharacterClusterTests; these tests assert the behaviour a user
/// sees.
/// </summary>
[Category("Text Parsing & Layout")]
[UnityPlatform(RuntimePlatform.OSXEditor, RuntimePlatform.WindowsEditor, RuntimePlatform.LinuxEditor)]
internal class TMP_InputFieldClusterTests : TMPTestSuiteWithEssentialResources
{
// U+E0100 VARIATION SELECTOR-17, encoded as a surrogate pair: two UTF-16 code units.
const string k_VariationSelector17 = "\U000E0100";
// U+1F600 GRINNING FACE. A plain surrogate pair, with no variation selector.
const string k_AstralCharacter = "\U0001F600";
GameObject m_CameraObject;
GameObject m_CanvasObject;
GameObject m_InputObject;
GameObject m_EventObject;
TMP_InputField m_InputField;
[SetUp]
public void SetUp()
{
m_CameraObject = new GameObject("Camera Object", typeof(Camera));
m_CanvasObject = new GameObject("Canvas Object", typeof(Canvas), typeof(GraphicRaycaster));
m_CanvasObject.GetComponent<Canvas>().renderMode = RenderMode.ScreenSpaceOverlay;
m_InputObject = new GameObject("Input Object", typeof(TMP_InputField));
m_InputObject.transform.SetParent(m_CanvasObject.transform);
m_InputObject.AddComponent<Image>();
GameObject textAreaObject = new GameObject("Text Area", typeof(TextMeshProUGUI));
textAreaObject.transform.SetParent(m_InputObject.transform);
m_InputField = m_InputObject.GetComponent<TMP_InputField>();
m_InputField.targetGraphic = m_InputObject.GetComponent<Image>();
m_InputField.textViewport = m_InputObject.GetComponent<RectTransform>();
m_InputField.textComponent = textAreaObject.GetComponent<TextMeshProUGUI>();
m_InputField.textComponent.fontSize = 18;
m_InputField.lineType = TMP_InputField.LineType.SingleLine;
// Selecting the whole text on focus would make every delete a selection delete and
// hide the single character behaviour under test.
m_InputField.onFocusSelectAll = false;
m_EventObject = new GameObject("Event Object", typeof(EventSystem), typeof(StandaloneInputModule));
// Address the EventSystem this fixture created rather than EventSystem.current,
// which resolves to the first registered one and could belong to another fixture.
m_EventObject.GetComponent<EventSystem>().SetSelectedGameObject(m_InputObject);
m_InputField.ActivateInputField();
}
[TearDown]
public void TearDown()
{
if (m_InputField != null)
{
m_InputField.DeactivateInputField();
m_InputField = null;
}
// SetUp can bail out through Assert.IsTrue before any of these exist.
if (m_EventObject != null)
{
Object.DestroyImmediate(m_EventObject);
m_EventObject = null;
}
if (m_InputObject != null)
{
Object.DestroyImmediate(m_InputObject);
m_InputObject = null;
}
if (m_CanvasObject != null)
{
Object.DestroyImmediate(m_CanvasObject);
m_CanvasObject = null;
}
if (m_CameraObject != null)
{
Object.DestroyImmediate(m_CameraObject);
m_CameraObject = null;
}
}
/// <summary>
/// Assigns the text and puts the caret at a known position in the raw string. The
/// stringPosition setter derives the caret index from characterInfo synchronously, so the
/// label has to be generated first.
/// </summary>
void SetTextAndStringPosition(string value, int stringPosition)
{
m_InputField.text = value;
m_InputField.textComponent.ForceMeshUpdate();
m_InputField.stringPosition = stringPosition;
}
void PressKey(KeyCode keyCode)
{
m_InputField.ProcessEvent(new Event { type = EventType.KeyDown, keyCode = keyCode, modifiers = EventModifiers.None });
m_InputField.ForceLabelUpdate();
m_InputField.textComponent.ForceMeshUpdate();
}
[Test]
public void Backspace_RemovesTheWholeVariationSelectorCluster_OnTheFirstPress()
{
// "a" | "b" + VS17 - four code units, two rendered characters.
string sourceText = "ab" + k_VariationSelector17;
SetTextAndStringPosition(sourceText, sourceText.Length);
PressKey(KeyCode.Backspace);
// The whole cluster goes in one press. Before the fix this press removed nothing
// (or only part of the cluster), which is the reported symptom.
Assert.AreEqual("a", m_InputField.text, "The first Backspace should remove the whole variant character.");
// And the caret has to land on the cluster boundary that is left behind, otherwise
// the following press works from a stale position.
Assert.AreEqual(1, m_InputField.stringPosition, "The caret should be left at the end of the remaining text.");
PressKey(KeyCode.Backspace);
Assert.AreEqual(string.Empty, m_InputField.text, "The second Backspace should remove the remaining character.");
}
[Test]
public void Backspace_RemovesTheOnlyVariationSelectorCluster_OnTheFirstPress()
{
// The cluster is the entire content, so the field should end up empty.
string sourceText = "a" + k_VariationSelector17;
SetTextAndStringPosition(sourceText, sourceText.Length);
PressKey(KeyCode.Backspace);
Assert.AreEqual(string.Empty, m_InputField.text, "The first Backspace should empty the field.");
}
[Test]
public void Delete_RemovesTheWholeVariationSelectorCluster_OnTheFirstPress()
{
// Caret sits before the cluster, at raw string index 1.
string sourceText = "ab" + k_VariationSelector17;
SetTextAndStringPosition(sourceText, 1);
PressKey(KeyCode.Delete);
Assert.AreEqual("a", m_InputField.text, "The first Delete should remove the whole variant character.");
}
[Test]
public void Backspace_PlainText_RemovesOneCharacter()
{
// Regression guard: nothing about single code unit characters changes.
SetTextAndStringPosition("abc", 3);
PressKey(KeyCode.Backspace);
Assert.AreEqual("ab", m_InputField.text);
}
[Test]
public void Backspace_SurrogatePairWithoutVariationSelector_RemovesBothCodeUnits()
{
// Regression guard: a plain surrogate pair was already removed as a unit and must
// still be, never leaving half a pair behind.
string sourceText = "a" + k_AstralCharacter;
SetTextAndStringPosition(sourceText, sourceText.Length);
PressKey(KeyCode.Backspace);
Assert.AreEqual("a", m_InputField.text);
}
[Test]
public void Arrows_StepOverTheWholeVariationSelectorCluster()
{
// In the default editing mode the arrows move by rendered character, so one press
// has to cross the whole cluster and land on a cluster boundary - otherwise a
// following edit would split the base character from its selector.
string sourceText = "ab" + k_VariationSelector17;
SetTextAndStringPosition(sourceText, sourceText.Length);
PressKey(KeyCode.LeftArrow);
Assert.AreEqual(1, m_InputField.stringPosition, "Arrow-left should land at the start of the cluster.");
PressKey(KeyCode.RightArrow);
Assert.AreEqual(sourceText.Length, m_InputField.stringPosition, "Arrow-right should land at the end of the cluster.");
}
[Test]
public void Arrows_InRichTextEditingMode_StepByCodePoint()
{
// With rich text editing enabled the arrows deliberately move by code point rather
// than by rendered character, so crossing a base character plus a surrogate pair
// selector takes two presses. The raw string positions come straight from
// char.IsLowSurrogate and are unaffected by the cluster mapping; the caret index is
// not - it now stays after the glyph until the raw position has fully left the
// cluster, which is what GetCaretPositionFromStringIndex reports for a position
// inside a cluster. Pinned here so the two are not allowed to drift apart again.
string sourceText = "ab" + k_VariationSelector17;
m_InputField.isRichTextEditingAllowed = true;
SetTextAndStringPosition(sourceText, sourceText.Length);
PressKey(KeyCode.LeftArrow);
Assert.AreEqual(2, m_InputField.stringPosition, "The first arrow-left should skip the surrogate pair selector.");
Assert.AreEqual(2, m_InputField.caretPosition, "The caret should stay after the cluster while the raw position is still inside it.");
PressKey(KeyCode.LeftArrow);
Assert.AreEqual(1, m_InputField.stringPosition, "The second arrow-left should step over the base character.");
Assert.AreEqual(1, m_InputField.caretPosition, "The caret should move once the raw position reaches the cluster boundary.");
}
[Test]
public void Backspace_InRichTextEditingMode_RemovesTheSelectorOnly()
{
// Documented counterpart to the default mode: rich text editing is code point
// granular by design, so Backspace removes the surrogate pair selector and leaves
// the base character. This path reads char.IsLowSurrogate rather than characterInfo,
// so it is unaffected by the cluster mapping fix - pinned so the two modes cannot
// quietly converge.
string sourceText = "ab" + k_VariationSelector17;
m_InputField.isRichTextEditingAllowed = true;
SetTextAndStringPosition(sourceText, sourceText.Length);
PressKey(KeyCode.Backspace);
Assert.AreEqual("ab", m_InputField.text);
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: ea489b4043ccf6e49b2acea3c66595a9
@@ -0,0 +1,337 @@
using UnityEngine;
using NUnit.Framework;
using System.IO;
using System.Collections.Generic;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using UnityEngine.TestTools;
namespace TMPro
{
[Category("Text Parsing & Layout")]
internal class TMP_RuntimeTests
{
private TextMeshPro m_TextComponent;
// Characters: 22 Spaces: 4 Words: 5 Lines:
private const string m_TextBlock_00 = "A simple line of text.";
// Characters: 104 Spaces: 14 Words: 15 Lines:
private const string m_TextBlock_01 = "Unity 2017 introduces new features that help teams of artists and developers build experiences together.";
// Characters: 1500 Spaces: 228 Words: 241
private const string m_TextBlock_02 = "The European languages are members of the same family. Their separate existence is a myth. For science, music, sport, etc, Europe uses the same vocabulary. The languages only differ in their grammar, their pronunciation and their most common words." +
"Everyone realizes why a new common language would be desirable: one could refuse to pay expensive translators.To achieve this, it would be necessary to have uniform grammar, pronunciation and more common words.If several languages coalesce, the grammar of the resulting language is more simple and regular than that of the individual languages." +
"The new common language will be more simple and regular than the existing European languages.It will be as simple as Occidental; in fact, it will be Occidental.To an English person, it will seem like simplified English, as a skeptical Cambridge friend of mine told me what Occidental is. The European languages are members of the same family." +
"Their separate existence is a myth. For science, music, sport, etc, Europe uses the same vocabulary.The languages only differ in their grammar, their pronunciation and their most common words.Everyone realizes why a new common language would be desirable: one could refuse to pay expensive translators.To achieve this, it would be necessary to" +
"have uniform grammar, pronunciation and more common words.If several languages coalesce, the grammar of the resulting language is more simple and regular than that of the individual languages.The new common language will be";
// Characters: 2500 Spaces: 343 Words: 370
private const string m_TextBlock_03 = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. " +
"Nullam dictum felis eu pede mollis pretium.Integer tincidunt.Cras dapibus.Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim.Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus.Phasellus viverra nulla ut metus varius laoreet.Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue.Curabitur ullamcorper ultricies nisi. " +
"Nam eget dui.Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum.Nam quam nunc, blandit vel, luctus pulvinar, hendrerit id, lorem.Maecenas nec odio et ante tincidunt tempus.Donec vitae sapien ut libero venenatis faucibus.Nullam quis ante.Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. " +
"Donec sodales sagittis magna. Sed consequat, leo eget bibendum sodales, augue velit cursus nunc, quis gravida magna mi a libero. Fusce vulputate eleifend sapien. Vestibulum purus quam, scelerisque ut, mollis sed, nonummy id, metus.Nullam accumsan lorem in dui.Cras ultricies mi eu turpis hendrerit fringilla.Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; In ac dui quis mi consectetuer lacinia. Nam pretium turpis et arcu. " +
"Duis arcu tortor, suscipit eget, imperdiet nec, imperdiet iaculis, ipsum. Sed aliquam ultrices mauris.Integer ante arcu, accumsan a, consectetuer eget, posuere ut, mauris.Praesent adipiscing. Phasellus ullamcorper ipsum rutrum nunc.Nunc nonummy metus.Vestibulum volutpat pretium libero. Cras id dui.Aenean ut eros et nisl sagittis vestibulum.Nullam nulla eros, ultricies sit amet, nonummy id, imperdiet feugiat, pede.Sed lectus. Donec mollis hendrerit risus. Phasellus nec sem in justo pellentesque facilisis. " +
"Etiam imperdiet imperdiet orci. Nunc nec neque.Phasellus leo dolor, tempus non, auctor et, hendrerit quis, nisi.Curabitur ligula sapien, tincidunt non, euismod vitae, posuere imperdiet, leo.Maecenas malesuada. Praesent nan. The end of this of this long block of text.";
// Characters: 3423 Spaces: 453 Words: 500
private const string m_TextBlock_04 = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit.Aenean commodo ligula eget dolor.Aenean massa.Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem.Nulla consequat massa quis enim.Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu.In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo.Nullam dictum felis eu pede mollis pretium.Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus." +
"Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim.Aliquam lorem ante, dapibus in, viverra quis, feugiat a, tellus.Phasellus viverra nulla ut metus varius laoreet.Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue.Curabitur ullamcorper ultricies nisi. Nam eget dui.Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem quam semper libero, sit amet adipiscing sem neque sed ipsum.Nam quam nunc, blandit vel, luctus pulvinar, hendrerit id, lorem.Maecenas nec odio et ante tincidunt tempus.Donec vitae sapien ut libero venenatis faucibus.Nullam quis ante." +
"Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales sagittis magna. Sed consequat, leo eget bibendum sodales, augue velit cursus nunc, quis gravida magna mi a libero. Fusce vulputate eleifend sapien. Vestibulum purus quam, scelerisque ut, mollis sed, nonummy id, metus.Nullam accumsan lorem in dui.Cras ultricies mi eu turpis hendrerit fringilla.Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; In ac dui quis mi consectetuer lacinia.Nam pretium turpis et arcu." +
"Duis arcu tortor, suscipit eget, imperdiet nec, imperdiet iaculis, ipsum. Sed aliquam ultrices mauris.Integer ante arcu, accumsan a, consectetuer eget, posuere ut, mauris.Praesent adipiscing. Phasellus ullamcorper ipsum rutrum nunc.Nunc nonummy metus.Vestibulum volutpat pretium libero. Cras id dui.Aenean ut eros et nisl sagittis vestibulum.Nullam nulla eros, ultricies sit amet, nonummy id, imperdiet feugiat, pede.Sed lectus. Donec mollis hendrerit risus. Phasellus nec sem in justo pellentesque facilisis.Etiam imperdiet imperdiet orci. Nunc nec neque." +
"Phasellus leo dolor, tempus non, auctor et, hendrerit quis, nisi.Curabitur ligula sapien, tincidunt non, euismod vitae, posuere imperdiet, leo.Maecenas malesuada. Praesent congue erat at massa.Sed cursus turpis vitae tortor.Donec posuere vulputate arcu. Phasellus accumsan cursus velit. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed aliquam, nisi quis porttitor congue, elit erat euismod orci, ac placerat dolor lectus quis orci.Phasellus consectetuer vestibulum elit.Aenean tellus metus, bibendum sed, posuere ac, mattis non, nunc.Vestibulum fringilla pede sit amet augue." +
"In turpis. Pellentesque posuere. Praesent turpis. Aenean posuere, tortor sed cursus feugiat, nunc augue blandit nunc, eu sollicitudin urna dolor sagittis lacus. Donec elit libero, sodales nec, volutpat a, suscipit non, turpis.Nullam sagittis. Suspendisse pulvinar, augue ac venenatis condimentum, sem libero volutpat nibh, nec pellentesque velit pede quis nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Fusce id purus.Ut varius tincidunt libero.Phasellus dolor.Maecenas vestibulum mollis";
//
private const string m_TextBlock_05 = "This block of text contains <b>bold</b> and <i>italicized</i> characters.";
private const string m_TextBlock_06 = "<align=center><style=H1><#ffffff><u>Multiple<#80f0ff> Alignment</color> per text object</u></color></style></align><line-height=2em>\n" +
"</line-height> The <<#ffffa0>align</color>> tag in TextMesh<#40a0ff>Pro</color> provides the ability to control the alignment of lines and paragraphs which is essential when working with text.\n" +
"<align=left> You may want some block of text to be<#80f0ff>left aligned</color> <<#ffffa0>align=<#80f0ff>left</color></color>> which is sort of the standard.</align>\n" +
"<style=Quote><#ffffa0>\"Using <#80f0ff>Center Alignment</color> <<#ffffa0>align=<#80f0ff>center</color></color>> for a title or displaying a quote is another good example of text alignment.\"</color></style>\n" +
"<align=right><#80f0ff>Right Alignment</color> <<#ffffa0>align=<#80f0ff>right</color></color>> can be useful to create contrast between lines and paragraphs of text.\n" +
"<align=justified><#80f0ff>Justified Alignment</color> <<#ffffa0>align=<#80f0ff>justified</color></color>> results in text that is flush on both the left and right margins. Used well, justified type can look clean and classy.\n" +
"<style=Quote><align=left><#ffffa0>\"Text formatting and alignment has a huge impact on how people will read and perceive your text.\"</color>\n" +
"<size=65%><align=right> -Stephan Bouchard</style>";
private readonly string[] testStrings = new string[] { m_TextBlock_00, m_TextBlock_01, m_TextBlock_02, m_TextBlock_03, m_TextBlock_04, m_TextBlock_05, m_TextBlock_06 };
[OneTimeSetUp]
public void Setup()
{
if (Directory.Exists(Path.GetFullPath("Assets/TextMesh Pro")) || Directory.Exists(Path.GetFullPath("Packages/com.unity.textmeshpro.tests/TextMesh Pro")))
{
GameObject textObject = new GameObject("Text Object");
m_TextComponent = textObject.AddComponent<TextMeshPro>();
m_TextComponent.fontSize = 18;
}
else
{
Debug.Log("Skipping over Editor tests as TMP Essential Resources are missing from the current test project.");
Assert.Ignore();
return;
}
}
public static IEnumerable<object[]> TestCases_Parsing_TextInfo_WordWrapDisabled()
{
yield return new object[] { 0, 22, 4, 5, 1 };
yield return new object[] { 1, 104, 14, 15, 1 };
yield return new object[] { 2, 1500, 228, 241, 1 };
yield return new object[] { 3, 2500, 343, 370, 1 };
yield return new object[] { 4, 3423, 453, 500, 1 };
}
[Test, TestCaseSource("TestCases_Parsing_TextInfo_WordWrapDisabled")]
public void Parsing_TextInfo_WordWrapDisabled(int sourceTextIndex, int characterCount, int spaceCount, int wordCount, int lineCount)
{
m_TextComponent.text = testStrings[sourceTextIndex];
m_TextComponent.textWrappingMode = TextWrappingModes.NoWrap;
m_TextComponent.alignment = TextAlignmentOptions.TopLeft;
// Size the RectTransform
m_TextComponent.rectTransform.sizeDelta = new Vector2(50, 5);
// Force text generation to populate the TextInfo data structure.
m_TextComponent.ForceMeshUpdate();
Assert.AreEqual(m_TextComponent.textInfo.characterCount, characterCount);
Assert.AreEqual(m_TextComponent.textInfo.spaceCount, spaceCount);
Assert.AreEqual(m_TextComponent.textInfo.wordCount, wordCount);
Assert.AreEqual(m_TextComponent.textInfo.lineCount, lineCount);
}
public static IEnumerable<object[]> TestCases_Parsing_TextInfo_WordWrapEnabled()
{
yield return new object[] { 0, 22, 4, 5, 1 };
yield return new object[] { 1, 104, 14, 15, 1 };
yield return new object[] { 2, 1500, 228, 241, 13 };
yield return new object[] { 3, 2500, 343, 370, 21 };
yield return new object[] { 4, 3423, 453, 500, 29 };
}
[Test, TestCaseSource("TestCases_Parsing_TextInfo_WordWrapEnabled")]
public void Parsing_TextInfo_WordWrapEnabled(int sourceTextIndex, int characterCount, int spaceCount, int wordCount, int lineCount)
{
m_TextComponent.text = testStrings[sourceTextIndex];
m_TextComponent.textWrappingMode = TextWrappingModes.Normal;
m_TextComponent.alignment = TextAlignmentOptions.TopLeft;
// Size the RectTransform
m_TextComponent.rectTransform.sizeDelta = new Vector2(100, 50);
// Force text generation to populate the TextInfo data structure.
m_TextComponent.ForceMeshUpdate();
Assert.AreEqual(m_TextComponent.textInfo.characterCount, characterCount);
Assert.AreEqual(m_TextComponent.textInfo.spaceCount, spaceCount);
Assert.AreEqual(m_TextComponent.textInfo.wordCount, wordCount);
Assert.AreEqual(m_TextComponent.textInfo.lineCount, lineCount);
}
public static IEnumerable<object[]> TestCases_Parsing_TextInfo_AlignmentTopJustified()
{
yield return new object[] { 2, 1500, 228, 241, 13 };
yield return new object[] { 3, 2500, 343, 370, 20 };
yield return new object[] { 4, 3423, 453, 500, 27 };
}
[Test, TestCaseSource("TestCases_Parsing_TextInfo_AlignmentTopJustified")]
public void Parsing_TextInfo_AlignmentTopJustified(int sourceTextIndex, int characterCount, int spaceCount, int wordCount, int lineCount)
{
m_TextComponent.text = testStrings[sourceTextIndex];
m_TextComponent.textWrappingMode = TextWrappingModes.Normal;
m_TextComponent.alignment = TextAlignmentOptions.TopJustified;
// Size the RectTransform
m_TextComponent.rectTransform.sizeDelta = new Vector2(100, 50);
// Force text generation to populate the TextInfo data structure.
m_TextComponent.ForceMeshUpdate();
Assert.AreEqual(m_TextComponent.textInfo.characterCount, characterCount);
Assert.AreEqual(m_TextComponent.textInfo.spaceCount, spaceCount);
Assert.AreEqual(m_TextComponent.textInfo.wordCount, wordCount);
Assert.AreEqual(m_TextComponent.textInfo.lineCount, lineCount);
}
public static IEnumerable<object[]> TestCases_Parsing_TextInfo_RichText()
{
yield return new object[] { 5, 59, 8, 9, 1 };
yield return new object[] { 6, 768, 124, 126, 14 };
}
[Test, TestCaseSource("TestCases_Parsing_TextInfo_RichText")]
public void Parsing_TextInfo_RichText(int sourceTextIndex, int characterCount, int spaceCount, int wordCount, int lineCount)
{
m_TextComponent.text = testStrings[sourceTextIndex];
m_TextComponent.textWrappingMode = TextWrappingModes.Normal;
m_TextComponent.alignment = TextAlignmentOptions.TopLeft;
// Size the RectTransform
m_TextComponent.rectTransform.sizeDelta = new Vector2(70, 35);
// Force text generation to populate the TextInfo data structure.
m_TextComponent.ForceMeshUpdate();
Assert.AreEqual(m_TextComponent.textInfo.characterCount, characterCount);
Assert.AreEqual(m_TextComponent.textInfo.spaceCount, spaceCount);
Assert.AreEqual(m_TextComponent.textInfo.wordCount, wordCount);
Assert.AreEqual(m_TextComponent.textInfo.lineCount, lineCount);
}
public static IEnumerable<object[]> TestCases_MultiLineNewline_OnLastLine_WhenPressedEnter_Caret_ShouldNotGoto_NextLine()
{
yield return new object[] { 1, 1 };
yield return new object[] { 2, 2 };
yield return new object[] { 3, 3 };
yield return new object[] { 4, 4 };
yield return new object[] { 5, 5 };
yield return new object[] { 6, 6 };
}
[Test, TestCaseSource(nameof(TestCases_MultiLineNewline_OnLastLine_WhenPressedEnter_Caret_ShouldNotGoto_NextLine))]
public void MultiLineNewline_OnLastLine_WhenPressedEnter_Caret_ShouldNotGoto_NextLine(int lineLimit,
int expectedLineCount)
{
MultiLineNewline_LineLimit_ExpectedLineCount_Logic(lineLimit, lineLimit, 3);
Assert.AreEqual(m_TextComponent.textInfo.lineCount, expectedLineCount);
}
private void MultiLineNewline_LineLimit_ExpectedLineCount_Logic(int lineLimitValue, int lineLimitApplied,
int extraKeyDownEventCount)
{
GameObject cameraObject = new GameObject("Camera Object", typeof(Camera));
GameObject canvasObject = new GameObject("Canvas Object", typeof(Canvas), typeof(GraphicRaycaster));
canvasObject.GetComponent<Canvas>().renderMode = RenderMode.ScreenSpaceOverlay;
GameObject inputObject = new GameObject("Input Object", typeof(TMP_InputField));
inputObject.transform.parent = canvasObject.transform;
inputObject.AddComponent<Image>();
TMP_InputField m_InputField = inputObject.GetComponent<TMP_InputField>();
m_InputField.targetGraphic = inputObject.GetComponent<Image>();
m_InputField.textComponent = m_TextComponent;
m_InputField.lineType = TMP_InputField.LineType.MultiLineNewline;
m_InputField.lineLimit = lineLimitValue;
GameObject eventGameObject = new GameObject("Event Object", typeof(EventSystem), typeof(StandaloneInputModule));
Event enterKeyDownEvent = new Event { type = EventType.KeyDown, keyCode = KeyCode.KeypadEnter, modifiers = EventModifiers.None, character = '\n' };
m_InputField.text = "POTUS";
EventSystem.current.SetSelectedGameObject(inputObject);
m_InputField.ActivateInputField();
int count = lineLimitApplied + extraKeyDownEventCount;
while (count > 0)
{
m_InputField.ProcessEvent(enterKeyDownEvent);
m_InputField.ForceLabelUpdate();
count--;
}
m_InputField.textComponent.ForceMeshUpdate();
CanvasUpdateRegistry.RegisterCanvasElementForGraphicRebuild(m_InputField);
m_InputField.DeactivateInputField();
GameObject.Destroy(eventGameObject);
GameObject.Destroy(inputObject);
GameObject.Destroy(canvasObject);
GameObject.Destroy(cameraObject);
}
public static IEnumerable<object[]> TestCases_MultiLineNewLine_NegativeOrZeroLineLimit_AddsNewLine()
{
yield return new object[] { 0, 0, 1 };
yield return new object[] { 0, 0, 4 };
yield return new object[] { -1, 0, 2 };
}
[Test, TestCaseSource(nameof(TestCases_MultiLineNewLine_NegativeOrZeroLineLimit_AddsNewLine))]
public void MultiLineNewLine_NegativeOrZeroLineLimit_AddsNewLine(int lineLimitValue, int lineLimitApplied,
int extraKeyDownEventCount)
{
MultiLineNewline_LineLimit_ExpectedLineCount_Logic(lineLimitValue, lineLimitApplied, extraKeyDownEventCount);
Assert.AreEqual(m_TextComponent.textInfo.lineCount, extraKeyDownEventCount + 1);
}
[OneTimeTearDown]
public void OneTimeTearDown()
{
if (m_TextComponent != null)
{
Object.DestroyImmediate(m_TextComponent.gameObject);
m_TextComponent = null;
}
// // Remove TMP Essential Resources if they were imported in the project as a result of running tests.
// if (TMPro_EventManager.temporaryResourcesImported == true)
// {
// string testResourceFolderPath = Path.GetFullPath("Assets/TextMesh Pro");
// if (Directory.Exists(testResourceFolderPath))
// {
// Directory.Delete(testResourceFolderPath);
// File.Delete(Path.GetFullPath("Assets/TextMesh Pro.meta"));
// }
// TMPro_EventManager.temporaryResourcesImported = false;
// }
}
[Test]
public void SettingIsTextObjectScaleStatic_OnDisabledObject_DoesntProduceErrors()
{
// Reset the text component to before its OnEnable
var textObject = m_TextComponent.gameObject;
Object.DestroyImmediate(m_TextComponent);
textObject.SetActive(false);
m_TextComponent = textObject.AddComponent<TextMeshPro>();
// UUM-92041: verify that we only register if enabled
m_TextComponent.isTextObjectScaleStatic = true; // Make sure we're not starting false
m_TextComponent.isTextObjectScaleStatic = false;
Canvas.ForceUpdateCanvases();
LogAssert.NoUnexpectedReceived();
}
[Test]
public void SettingIsTextObjectScaleStatic_OnDisabledObject_IsAppliedCorrectlyAfterObjectIsEnabled()
{
m_TextComponent.text = "Test";
// Reset the text component to before its OnEnable
var textObject = m_TextComponent.gameObject;
Object.DestroyImmediate(m_TextComponent);
textObject.SetActive(false);
m_TextComponent = textObject.AddComponent<TextMeshPro>();
// Set isTextObjectScaleStatic to false while the object is disabled
m_TextComponent.isTextObjectScaleStatic = true;
m_TextComponent.isTextObjectScaleStatic = false; // Make sure we're not starting false
Canvas.ForceUpdateCanvases();
m_TextComponent.gameObject.SetActive(true);
Canvas.ForceUpdateCanvases();
var w = m_TextComponent.textInfo.meshInfo[0].uvs0[0].w;
// When isTextObjectScaleStatic is false, scale is updated in mesh uv
m_TextComponent.transform.localScale = Vector3.one * 2;
Canvas.ForceUpdateCanvases();
Assert.AreEqual(w * 2, m_TextComponent.textInfo.meshInfo[0].uvs0[0].w);
// Setting isTextObjectScaleStatic to true shouldn't change mesh
m_TextComponent.isTextObjectScaleStatic = true;
Canvas.ForceUpdateCanvases();
Assert.AreEqual(w * 2, m_TextComponent.textInfo.meshInfo[0].uvs0[0].w);
// When isTextObjectScaleStatic is true, scale is NOT updated in mesh
m_TextComponent.transform.localScale = Vector3.one;
Canvas.ForceUpdateCanvases();
Assert.AreEqual(w * 2, m_TextComponent.textInfo.meshInfo[0].uvs0[0].w);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9de24983a2c6cbe4f925c3e98a79b804
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,14 @@
{
"name": "Unity.TextMeshPro.Tests",
"references": [
"Unity.TextMeshPro"
],
"optionalUnityReferences": [
"TestAssemblies"
],
"defineConstraints": [
"UNITY_INCLUDE_TESTS"
],
"includePlatforms": [],
"excludePlatforms": []
}
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: bb05cab7d802aa5468f8f2f86840d984
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant: