В редакторе триггеров мне часто доводится использовать гипертекст - когда нажатие на определенное слово приводит к каким-либо действиям. Увы в стандартном арсенале движка нет инструмента для работы с подобным, потому пришлось писать его с нуля.
Хочу заметить, что это немного не тот гипертекст, который вы привыкли видеть в html - это не открытие страниц в браузере, а именно что совершение определенных действий.
Для того чтобы работать с гипертекстом добавьте следующий класс:
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class Hypertext
{
public string link_null = "<null>";
public string color_normal = "black",
color_link = "blue";
public GUIStyle style = new GUIStyle("label") { richText = true, fontSize = 15, fontStyle = FontStyle.Bold };
private readonly List<Text> phrases = new List<Text>();
private struct Text
{
public Action action;
public string text;
public int index;
}
private int GetIndexForLast(string str)
{ return (phrases.Count == 0 ? 0 : phrases[phrases.Count - 1].index) + str.Length; }
public void Add(string str, Action action = null)
{ phrases.Add(new Text { text = str, index = GetIndexForLast(str), action = action}); }
public void AddFormat(string str, params Action[] actions)
{
const char lBracket = '{',
rBracket = '}';
int currAction = 0;
var cursor = 0;
while (true)
{
var rIndex = str.IndexOf(rBracket, cursor);
if (rIndex == -1) break;
var lIndex = str.LastIndexOf(lBracket, rIndex);
if (lIndex == -1) break;
var l1 = lIndex - cursor;
if (l1 != 0) Add(str.Substring(cursor, l1));
var l2 = rIndex - lIndex - 1;
var linktext = (l2 != 0) ? str.Substring(lIndex + 1, l2) : link_null;
if (actions != null && currAction < actions.Length)
Add(linktext, actions[currAction++]);
else
Add(linktext);
cursor = rIndex + 1;
}
if (cursor != str.Length)
Add(str.Substring(cursor, str.Length - cursor));
}
private static string Color(string color, string content)
{ return "<color=" + color + ">" + content + "</color>"; }
private string GetSimpleString()
{ return phrases.Aggregate("", (str, p) => str + (p.action == null || !string.IsNullOrEmpty(p.text) ? p.text : link_null)); }
private string GetRichString()
{
return phrases.Aggregate("", (str, p) => str +
(p.action == null
? Color(color_normal, p.text)
: !string.IsNullOrEmpty(p.text)
? Color(color_link, p.text)
: Color(color_link, link_null)));
}
private Text GetTextFromCursor(int index)
{ return phrases.FirstOrDefault(x => index < x.index); }
public static void DrawLayout(GUIStyle style, string str, params Action[] actions)
{
var h = new Hypertext {style = new GUIStyle(style) { richText = true }};
h.AddFormat(str, actions);
h.DoDrawLayout();
}
public static void DrawLayout(string str, params Action[] actions)
{
var h = new Hypertext();
h.AddFormat(str, actions);
h.DoDrawLayout();
}
public void DoDrawLayout()
{
var textRich = GetRichString();
var textSimple = GetSimpleString();
var rect = GUILayoutUtility.GetRect(new GUIContent(textSimple), style);
var index = style.GetCursorStringIndex(rect, new GUIContent(textSimple), Event.current.mousePosition - new Vector2(3, 0));
var focusText = GetTextFromCursor(index);
GUI.Label(rect, textRich, style);
if (Event.current.type == EventType.MouseDown && focusText.action != null)
{
focusText.action.Invoke();
Event.current.Use();
}
}
}
Примеры использования
Есть несколько способов использовать гипертекст с помощью этого класса - каждый из способов может быть удобен в определенных ситуациях.
- Формирование гипертекста "частями"
var a = new Hypertext();
a.Add("Мама ");
a.Add("мыла", () => Debug.Log("click"));
a.Add(" раму");
a.DoDrawLayout();
В этом случае ключевым является метод Add, и если после текста мы вводим функтор () => { /*Наши действия*/ }, то это слово считается за ссылку.
- Формирование гипертекста в одной строке
var a = new Hypertext();
a.AddFormat("Мама {мыла} раму", () => Debug.Log("click"));
a.DoDrawLayout();
В этом случае ключевым является метод AddFormat, в котором все ссылки мы обособляем фигурными скобками, а после, через запятые, указываем функтор-действие на каждую ссылку.
- Статическим методом
Hypertext.DrawLayout("Мама {мыла} {раму}",
() => Debug.Log("нажато 'мыла'"),
() => Debug.Log("нажато 'раму'"));
Этот метод по сути работает так же как и второй способ, но позволяет сэкономить в писанине.
Скриншот как пример работы 2 способа:
Кроме того в классе есть несколько дополнительных полей, которые позволяют немного разнообразить ссылки:
style - отвечает за стиль, используемый для рисования надписи
link_null - позволяет указать текст, который будет выводиться если ссылка не имеет названия (должна же оставаться возможность нажимать на нее, верно?)
color_normal - указывает цвет обычного текста (в виде строки)
color_link - указывает цвет ссылок (в виде строки)
Указание цвета в виде строки должно соответствовать правилам указанным вот здесь
style - отвечает за стиль, используемый для рисования надписи
link_null - позволяет указать текст, который будет выводиться если ссылка не имеет названия (должна же оставаться возможность нажимать на нее, верно?)
color_normal - указывает цвет обычного текста (в виде строки)
color_link - указывает цвет ссылок (в виде строки)
Указание цвета в виде строки должно соответствовать правилам указанным вот здесь
Ред. alexprey
Ред. MF