5using System.Collections.Generic;
7using System.Text.RegularExpressions;
16 [AddComponentMenu(
"UI/Extensions/TextPic")]
19 public class TextPic : Text, IPointerClickHandler, IPointerExitHandler, IPointerEnterHandler, ISelectHandler {
32 [Tooltip(
"Global scaling factor for all images")]
52 get {
return m_OnHrefClick; }
53 set { m_OnHrefClick = value; }
59 private readonly List<Image> m_ImagesPool =
new List<Image>();
60 private readonly List<GameObject> culled_ImagesPool =
new List<GameObject>();
63 private bool clearImages =
false;
71 private readonly List<int> m_ImagesVertexIndex =
new List<int>();
76 private static readonly Regex s_Regex =
77 new Regex(
@"<quad name=(.+?) size=(\d*\.?\d+%?) width=(\d*\.?\d+%?) />", RegexOptions.Singleline);
82 private static readonly Regex s_HrefRegex =
83 new Regex(
@"<a href=([^>\n\s]+)>(.*?)(</a>)", RegexOptions.Singleline);
86 private string fixedString;
89 private bool updateQuad =
false;
94 private string m_OutputText;
99 private bool selected =
false;
103 get {
return selected; }
104 set { selected = value; }
108 private List<Vector2> positions =
new List<Vector2>();
111 private string previousText =
"";
124 public readonly List<Rect>
boxes =
new List<Rect>();
130 private readonly List<HrefInfo> m_HrefInfos =
new List<HrefInfo>();
135 private static readonly StringBuilder s_TextBuilder =
new StringBuilder();
138 private MatchCollection matches;
141 private MatchCollection href_matches;
144 private MatchCollection removeCharacters;
147 private int picIndex;
150 private int vertIndex;
157 private bool usesNewRendering =
false;
159 #if UNITY_2019_1_OR_NEWER
163 private static readonly Regex remove_Regex =
164 new Regex(
@"<b>|</b>|<i>|</i>|<size=.*?>|</size>|<color=.*?>|</color>|<material=.*?>|</material>|<quad name=(.+?) size=(\d*\.?\d+%?) width=(\d*\.?\d+%?) />|<a href=([^>\n\s]+)>|</a>|\s", RegexOptions.Singleline);
167 List<int> indexes =
new List<int>();
170 private int charactersRemoved = 0;
173 private int startCharactersRemoved = 0;
176 private int endCharactersRemoved = 0;
180 private int count = 0;
183 private int indexText = 0;
186 private string originalText;
189 private UIVertex vert;
198 Reset_m_HrefInfos ();
203#if UNITY_EDITOR && !UNITY_2018_3_OR_NEWER
210 matches = s_Regex.Matches(m_OutputText);
212 if (matches !=
null && matches.Count > 0) {
213 for (
int i = 0; i < matches.Count; i++) {
214 m_ImagesPool.RemoveAll(image => image ==
null);
216 if (m_ImagesPool.Count == 0) {
217 GetComponentsInChildren<Image>(
true, m_ImagesPool);
220 if (matches.Count > m_ImagesPool.Count) {
221 DefaultControls.Resources resources =
new DefaultControls.Resources();
222 GameObject go = DefaultControls.CreateImage(resources);
224 go.layer = gameObject.layer;
226 RectTransform rt = go.transform as RectTransform;
229 rt.SetParent(rectTransform);
230 rt.anchoredPosition3D = Vector3.zero;
231 rt.localRotation = Quaternion.identity;
232 rt.localScale = Vector3.one;
235 m_ImagesPool.Add(go.GetComponent<
Image>());
238 string spriteName = matches[i].Groups[1].Value;
240 Image img = m_ImagesPool[i];
242 Vector2 imgoffset = Vector2.zero;
244 if (img.sprite ==
null || img.sprite.name != spriteName) {
249 img.preserveAspect =
true;
262 if (positions.Count > 0 && i < positions.Count) {
263 img.rectTransform.anchoredPosition = positions[i] += imgoffset;
269 for (
int i = m_ImagesPool.Count - 1; i > 0; i--) {
270 if (m_ImagesPool[i]) {
271 if (!culled_ImagesPool.Contains(m_ImagesPool[i].gameObject)) {
272 culled_ImagesPool.Add(m_ImagesPool[i].gameObject);
273 m_ImagesPool.Remove(m_ImagesPool[i]);
280 for (
int i = m_ImagesPool.Count - 1; i >= matches.Count; i--) {
281 if (i >= 0 && m_ImagesPool.Count > 0) {
282 if (m_ImagesPool[i]) {
283 if (!culled_ImagesPool.Contains(m_ImagesPool[i].gameObject)) {
284 culled_ImagesPool.Add(m_ImagesPool[i].gameObject);
285 m_ImagesPool.Remove(m_ImagesPool[i]);
292 if (culled_ImagesPool.Count > 0) {
298 void Reset_m_HrefInfos () {
311 s_TextBuilder.Length = 0;
315 fixedString = this.text;
321 "<quad name=" +
inspectorIconList[i].name +
" size=" + fontSize +
" width=1 />");
328 href_matches = s_HrefRegex.Matches(fixedString);
330 if (href_matches !=
null && href_matches.Count > 0) {
331 for (
int i = 0; i < href_matches.Count; i++ ) {
332 s_TextBuilder.Append(fixedString.Substring(indexText, href_matches[i].Index - indexText));
335 var group = href_matches[i].Groups[1];
340 startIndex = (usesNewRendering ? s_TextBuilder.Length : s_TextBuilder.Length * 4),
341 endIndex = (usesNewRendering ? (s_TextBuilder.Length + href_matches[i].Groups[2].Length - 1) : (s_TextBuilder.Length + href_matches[i].Groups[2].Length - 1) * 4 + 3),
345 m_HrefInfos.Add(hrefInfo);
348 if (count <= m_HrefInfos.Count - 1) {
350 m_HrefInfos[count].
startIndex = (usesNewRendering ? s_TextBuilder.Length : s_TextBuilder.Length * 4);
351 m_HrefInfos[count].endIndex = (usesNewRendering ? (s_TextBuilder.Length + href_matches[i].Groups[2].Length - 1) : (s_TextBuilder.Length + href_matches[i].Groups[2].Length - 1) * 4 + 3);
357 s_TextBuilder.Append(href_matches[i].Groups[2].
Value);
358 s_TextBuilder.Append(
"</color>");
360 indexText = href_matches[i].Index + href_matches[i].Length;
368 s_TextBuilder.Append(fixedString.Substring(indexText, fixedString.Length - indexText));
370 m_OutputText = s_TextBuilder.ToString();
372 m_ImagesVertexIndex.Clear();
374 matches = s_Regex.Matches(m_OutputText);
376 #if UNITY_2019_1_OR_NEWER
377 href_matches = s_HrefRegex.Matches(m_OutputText);
381 for (
int r = 0; r < matches.Count; r++) {
382 indexes.Add(matches[r].Index);
387 if (matches !=
null && matches.Count > 0) {
388 for (
int i = 0; i < matches.Count; i++) {
389 picIndex = matches[i].Index;
391 #if UNITY_2019_1_OR_NEWER
392 if (usesNewRendering) {
393 charactersRemoved = 0;
395 removeCharacters = remove_Regex.Matches(m_OutputText);
397 for (
int r = 0; r < removeCharacters.Count; r++) {
398 if (removeCharacters[r].Index < picIndex && !indexes.Contains(removeCharacters[r].Index)) {
399 charactersRemoved += removeCharacters[r].Length;
403 for (
int r = 0; r < i; r++) {
404 charactersRemoved += (matches[r].Length - 1);
407 picIndex -= charactersRemoved;
411 vertIndex = picIndex * 4 + 3;
413 m_ImagesVertexIndex.Add(vertIndex);
417 #if UNITY_2019_1_OR_NEWER
418 if (usesNewRendering) {
419 if (m_HrefInfos !=
null && m_HrefInfos.Count > 0) {
420 for (
int i = 0; i < m_HrefInfos.Count; i++) {
421 startCharactersRemoved = 0;
422 endCharactersRemoved = 0;
424 removeCharacters = remove_Regex.Matches(m_OutputText);
426 for (
int r = 0; r < removeCharacters.Count; r++) {
427 if (removeCharacters[r].Index < m_HrefInfos[i].startIndex && !indexes.Contains(removeCharacters[r].Index)) {
428 startCharactersRemoved += removeCharacters[r].Length;
430 else if (removeCharacters[r].Index < m_HrefInfos[i].startIndex && indexes.Contains(removeCharacters[r].Index)) {
431 startCharactersRemoved += removeCharacters[r].Length - 1;
434 if (removeCharacters[r].Index < m_HrefInfos[i].endIndex && !indexes.Contains(removeCharacters[r].Index)) {
435 endCharactersRemoved += removeCharacters[r].Length;
437 else if (removeCharacters[r].Index < m_HrefInfos[i].endIndex && indexes.Contains(removeCharacters[r].Index)) {
438 endCharactersRemoved += removeCharacters[r].Length - 1;
442 m_HrefInfos[i].startIndex -= startCharactersRemoved;
444 m_HrefInfos[i].startIndex = m_HrefInfos[i].startIndex * 4;
446 m_HrefInfos[i].endIndex -= endCharactersRemoved;
448 m_HrefInfos[i].endIndex = m_HrefInfos[i].endIndex * 4 + 3;
459 Application.OpenURL(hrefName);
468 originalText = m_Text;
471 base.OnPopulateMesh(toFill);
473 m_DisableFontTextureRebuiltCallback =
true;
475 m_Text = originalText;
479 vert =
new UIVertex();
481 for (
int i = 0; i < m_ImagesVertexIndex.Count; i++) {
482 int endIndex = m_ImagesVertexIndex[i];
484 if (endIndex < toFill.currentVertCount) {
485 toFill.PopulateUIVertex(ref vert, endIndex);
487 positions.Add(
new Vector2((vert.position.x + fontSize / 2), (vert.position.y + fontSize / 2)) +
imageOffset);
490 toFill.PopulateUIVertex(ref vert, endIndex - 3);
492 Vector3 pos = vert.position;
494 for (
int j = endIndex, m = endIndex - 3; j > m; j--) {
495 toFill.PopulateUIVertex(ref vert, endIndex);
497 toFill.SetUIVertex(vert, j);
503 for (
int h = 0; h < m_HrefInfos.Count; h++) {
504 m_HrefInfos[h].boxes.Clear();
506 if (m_HrefInfos[h].startIndex >= toFill.currentVertCount) {
511 toFill.PopulateUIVertex(ref vert, m_HrefInfos[h].startIndex);
513 Vector3 pos = vert.position;
515 Bounds bounds =
new Bounds(pos, Vector3.zero);
517 for (
int i = m_HrefInfos[h].startIndex, m = m_HrefInfos[h].endIndex; i < m; i++) {
518 if (i >= toFill.currentVertCount) {
522 toFill.PopulateUIVertex(ref vert, i);
527 if (pos.x < bounds.min.x) {
528 m_HrefInfos[h].boxes.Add(
new Rect(bounds.min, bounds.size));
529 bounds =
new Bounds(pos, Vector3.zero);
532 bounds.Encapsulate(pos);
536 m_HrefInfos[h].boxes.Add(
new Rect(bounds.min, bounds.size));
542 m_DisableFontTextureRebuiltCallback =
false;
550 RectTransformUtility.ScreenPointToLocalPointInRectangle(
551 rectTransform, eventData.position, eventData.pressEventCamera, out lp);
553 for (
int h = 0; h < m_HrefInfos.Count; h++) {
554 for (
int i = 0; i < m_HrefInfos[h].boxes.Count; ++i) {
555 if (m_HrefInfos[h].boxes[i].Contains(lp)) {
556 m_OnHrefClick.Invoke(m_HrefInfos[h].name);
567 if (m_ImagesPool.Count >= 1) {
568 for (
int i = 0; i < m_ImagesPool.Count; i++) {
569 if (button !=
null && button.isActiveAndEnabled) {
570 m_ImagesPool[i].color = button.colors.highlightedColor;
580 if (m_ImagesPool.Count >= 1) {
581 for (
int i = 0; i < m_ImagesPool.Count; i++) {
582 if (button !=
null && button.isActiveAndEnabled) {
583 m_ImagesPool[i].color = button.colors.normalColor;
586 m_ImagesPool[i].color = color;
596 if (m_ImagesPool.Count >= 1) {
597 for (
int i = 0; i < m_ImagesPool.Count; i++) {
598 if (button !=
null && button.isActiveAndEnabled) {
599 m_ImagesPool[i].color = button.colors.highlightedColor;
609 if (m_ImagesPool.Count >= 1) {
610 for (
int i = 0; i < m_ImagesPool.Count; i++) {
611 if (button !=
null && button.isActiveAndEnabled) {
612 m_ImagesPool[i].color = button.colors.normalColor;
620 base.SetVerticesDirty();
627 protected override void OnValidate() {
644 #if UNITY_2019_1_OR_NEWER
646 usesNewRendering =
false;
648 if (Application.unityVersion.StartsWith(
"2019.1.")) {
649 if (!Char.IsDigit(Application.unityVersion[8])) {
650 int number = Convert.ToInt32(Application.unityVersion[7].ToString());
653 usesNewRendering =
true;
657 usesNewRendering =
true;
661 usesNewRendering =
true;
667 supportRichText =
true;
668 alignByGeometry =
true;
671 if (m_ImagesPool.Count >= 1) {
672 for (
int i = 0; i < m_ImagesPool.Count; i++) {
673 if(m_ImagesPool[i] !=
null) {
674 m_ImagesPool[i].enabled =
true;
689 if (m_ImagesPool.Count >= 1) {
690 for (
int i = 0; i < m_ImagesPool.Count; i++) {
691 if (m_ImagesPool[i] !=
null) {
692 m_ImagesPool[i].enabled =
false;
701 button = GetComponent<Button>();
707 if (previousText != text) {
724 for (
int i = 0; i < culled_ImagesPool.Count; i++) {
725 DestroyImmediate(culled_ImagesPool[i]);
728 culled_ImagesPool.Clear();
UnityEngine.UI.Button Button
System.Drawing.Image Image
readonly List< Rect > boxes
IconName[] inspectorIconList
void OnPointerClick(PointerEventData eventData)
Click event is detected whether to click a hyperlink text
void ResetIconList()
METHODS ///.
void OnPointerExit(PointerEventData eventData)
string GetOutputText()
Finally, the output text hyperlinks get parsed
override void OnPopulateMesh(VertexHelper toFill)
UNITY METHODS ///.
override void OnDisable()
bool isCreating_m_HrefInfos
void OnDeselect(BaseEventData eventData)
override void SetVerticesDirty()
void OnSelect(BaseEventData eventData)
HrefClickEvent onHrefClick
Hyperlink Click Event
void OnPointerEnter(PointerEventData eventData)
virtual void OnHrefClick(string hrefName)
Credit Erdener Gonenc - @PixelEnvision.