Skip to content

Instantly share code, notes, and snippets.

@JimmyCushnie
Created October 30, 2020 06:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JimmyCushnie/fdbae61f28bed0d92c1f2dc239619e70 to your computer and use it in GitHub Desktop.
Save JimmyCushnie/fdbae61f28bed0d92c1f2dc239619e70 to your computer and use it in GitHub Desktop.
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using JimmysUnityUtilities;
using FancyInput;
using TMPro;
namespace LogicUI.TextMeshProUtilities
{
/// <summary>
/// Allows links in TextMeshPro text objects to be clicked on, and gives them custom colors when they are hovered or clicked.
/// </summary>
[RequireComponent(typeof(TMP_Text))]
public class TextLinkHelper : MonoBehaviour, IPointerClickHandler, IPointerDownHandler, IPointerUpHandler, IPointerEnterHandler, IPointerExitHandler
{
private TMP_Text _Text;
public TMP_Text Text
{
get
{
if (_Text == null)
_Text = GetComponent<TMP_Text>();
return _Text;
}
}
private void OnValidate()
{
SetAllLinksToNormalColor();
}
void Start()
{
// I've no idea why the frame delay is necessary, but it is.
CoroutineUtility.RunAfterOneFrame(SetAllLinksToNormalColor);
}
[ColorUsage(showAlpha: false), SerializeField]
Color32 LinkNormalColor = new Color32(0, 0, 255, 255);
[ColorUsage(showAlpha: false), SerializeField]
Color32 LinkHoveredColor = new Color32(50, 50, 255, 255);
[ColorUsage(showAlpha: false), SerializeField]
Color32 LinkClickColor = new Color32(80, 80, 255, 255);
public void SetColors(Color32 linkNormalColor, Color32 linkHoveredColor, Color32 linkClickColor)
{
LinkNormalColor = linkNormalColor;
LinkHoveredColor = linkHoveredColor;
LinkClickColor = linkClickColor;
SetAllLinksToNormalColor();
}
private Dictionary<string, Action> CustomLinks = new Dictionary<string, Action>();
/// <summary>
/// Sets some code to be run when a link is clicked on.
/// If a link doesn't have a custom action set, we will use <see cref="Application.OpenURL(string)"/> on it.
/// </summary>
/// <param name="linkID"></param>
/// <param name="linkAction"></param>
public void SetCustomLink(string linkID, Action linkAction)
{
CustomLinks[linkID] = linkAction;
}
void IPointerClickHandler.OnPointerClick(PointerEventData eventData)
{
int linkIndex = TMP_TextUtilities.FindIntersectingLink(Text, eventData.pressPosition, eventData.pressEventCamera);
if (linkIndex > -1)
{
var link = Text.textInfo.linkInfo[linkIndex];
ClickOnLink(link.GetLinkID());
}
void ClickOnLink(string linkID)
{
if (CustomLinks.TryGetValue(linkID, out var action))
action?.Invoke();
else
Application.OpenURL(linkID);
Debug.Log($"clicked on link: {linkID}");
}
}
void IPointerDownHandler.OnPointerDown(PointerEventData eventData)
{
PointerIsDown = true;
SetLinkToColor(PreviouslyHoveredLink, LinkClickColor);
}
void IPointerUpHandler.OnPointerUp(PointerEventData eventData)
{
PointerIsDown = false;
SetLinkToColor(PreviouslyHoveredLink, LinkNormalColor);
}
void IPointerEnterHandler.OnPointerEnter(PointerEventData eventData)
{
CurrentlyHoveredOver = true;
PointerIsDown = false;
cachedCamera = eventData.enterEventCamera;
}
void IPointerExitHandler.OnPointerExit(PointerEventData eventData)
{
CurrentlyHoveredOver = false;
}
Camera cachedCamera;
bool CurrentlyHoveredOver;
bool PointerIsDown;
TMP_LinkInfo? PreviouslyHoveredLink = null;
void Update()
{
if (!CurrentlyHoveredOver)
return;
if (PointerIsDown)
return;
SetLinkToColor(PreviouslyHoveredLink, LinkNormalColor);
int linkIndex = TMP_TextUtilities.FindIntersectingLink(Text, GameInput.MousePosition, cachedCamera);
if (linkIndex > -1)
{
var link = Text.textInfo.linkInfo[linkIndex];
SetLinkToColor(link, LinkHoveredColor);
PreviouslyHoveredLink = link;
}
else
{
PreviouslyHoveredLink = null;
}
}
public void SetAllLinksToNormalColor()
{
if (Text.textInfo == null) // Text object isn't initialized yet; required as of TMP 2.1
return;
foreach (var link in Text.textInfo.linkInfo.OrEmptyIfNull())
SetLinkToColor(link, LinkNormalColor);
}
private void SetLinkToColor(TMP_LinkInfo? linkInfo, Color32 color)
{
if (linkInfo == null)
return;
var link = linkInfo.Value;
if (link.linkTextfirstCharacterIndex + link.linkTextLength - 1 >= Text.textInfo.characterInfo.Length)
return;
for (int i = 0; i < link.linkTextLength; i++)
{
int characterIndex = link.linkTextfirstCharacterIndex + i;
var charInfo = Text.textInfo.characterInfo[characterIndex];
int meshIndex = charInfo.materialReferenceIndex;
int vertexIndex = charInfo.vertexIndex;
var characterVertexColors = Text.textInfo.meshInfo[meshIndex].colors32;
if (charInfo.isVisible)
{
characterVertexColors[vertexIndex + 0] = color;
characterVertexColors[vertexIndex + 1] = color;
characterVertexColors[vertexIndex + 2] = color;
characterVertexColors[vertexIndex + 3] = color;
}
}
Text.UpdateVertexData(TMP_VertexDataUpdateFlags.All);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment