2534 lines
97 KiB
C#
2534 lines
97 KiB
C#
// Copyright (c) 2024 Augie R. Maddox, Guavaman Enterprises. All rights reserved.
|
|
|
|
#if UNITY_2020 || UNITY_2021 || UNITY_2022 || UNITY_2023 || UNITY_6000 || UNITY_6000_0_OR_NEWER
|
|
#define UNITY_2020_PLUS
|
|
#endif
|
|
|
|
#if UNITY_2019 || UNITY_2020_PLUS
|
|
#define UNITY_2019_PLUS
|
|
#endif
|
|
|
|
#if UNITY_2018 || UNITY_2019_PLUS
|
|
#define UNITY_2018_PLUS
|
|
#endif
|
|
|
|
#if UNITY_2018_PLUS
|
|
#define REWIRED_SUPPORTS_TMPRO
|
|
#endif
|
|
|
|
#if REWIRED_SUPPORTS_TMPRO
|
|
|
|
#if UNITY_2020_PLUS
|
|
// A version of Text Mesh Pro or Unity UI that uses asset version 1.1.0
|
|
// may or may not be installed in this version of Unity.
|
|
// Since there are no preprocessor conditionals defined for the
|
|
// Unity UI / TMPro version, the only choice is to use reflection
|
|
// to support classes that may or may not exist in some projects.
|
|
#define MAY_SUPPORT_TMPRO_ASSET_V_1_1_0
|
|
#endif
|
|
|
|
#if UNITY_6000_0_OR_NEWER
|
|
// This Unity version is guaranteed to support at least Unity UI 2.0,
|
|
// which uses asset version 1.1.0. Avoid most of the reflection nonsense.
|
|
// However, some reflection is still required because
|
|
// TMPro.TMP_SpriteAsset now has internal setters for the tables and
|
|
// version in Unity UI 2.0. This effectively prevents scripts from
|
|
// modfying the Sprite Asset tables at runtime.
|
|
#define SUPPORTS_AT_LEAST_TMPRO_ASSET_V_1_1_0
|
|
#endif
|
|
|
|
#pragma warning disable 0649
|
|
|
|
namespace Rewired.Glyphs.UnityUI {
|
|
using System.Reflection;
|
|
using Rewired;
|
|
using System;
|
|
using System.Text;
|
|
using System.Collections.Generic;
|
|
|
|
/// <summary>
|
|
/// A helper class to display Rewired glyphs and display names inline in TMPro Text (Unity UI).
|
|
/// Parses tags within text and replaces them with Sprites or text.
|
|
/// Enter text in this component's text field, not in the TMPro Text field. Text entered here
|
|
/// will be parsed, then the Text Mesh Pro Text component will be updated with the text, including Sprite tags.
|
|
/// </summary>
|
|
[UnityEngine.AddComponentMenu("Rewired/Glyphs/Unity UI/Unity UI Text Mesh Pro Glyph Helper")]
|
|
[UnityEngine.RequireComponent(typeof(TMPro.TextMeshProUGUI))]
|
|
public class UnityUITextMeshProGlyphHelper : UnityEngine.MonoBehaviour {
|
|
|
|
[UnityEngine.Tooltip("Enter text into this field and not in the TMPro Text field directly. " +
|
|
"Text will be parsed for special tags, and the final result will be passed on to the Text Mesh Pro Text component. " +
|
|
"See the documentation for special tag format.")]
|
|
[UnityEngine.SerializeField]
|
|
[UnityEngine.TextArea(3, 10)]
|
|
private string _text;
|
|
|
|
[UnityEngine.Tooltip("Optional reference to an object that defines options. If blank, the global default options will be used.")]
|
|
[UnityEngine.SerializeField]
|
|
private ControllerElementGlyphSelectorOptionsSOBase _options;
|
|
[UnityEngine.Tooltip("Options that control how Text Mesh Pro displays Sprites.")]
|
|
[UnityEngine.SerializeField]
|
|
private TMProSpriteOptions _spriteOptions = TMProSpriteOptions.Default;
|
|
[UnityEngine.Tooltip("Optional material for Sprites. If blank, the default material will be used.\n" +
|
|
"Material is instantiated for each Sprite Asset, so making changes to values in the base material later will not affect Sprites. " +
|
|
"Changing the base material at runtime will copy only certain properties from the new material to Sprite materials."
|
|
)]
|
|
[UnityEngine.SerializeField]
|
|
private UnityEngine.Material _baseSpriteMaterial;
|
|
[UnityEngine.Tooltip("If enabled, local values such as Sprite color will be used instead of the value on the base material.")]
|
|
[UnityEngine.SerializeField]
|
|
private bool _overrideSpriteMaterialProperties = true;
|
|
[UnityEngine.Tooltip("These properties will override the properties on the Sprite material if Override Sprite Material Properties is enabled.")]
|
|
[UnityEngine.SerializeField]
|
|
private SpriteMaterialProperties _spriteMaterialProperties = SpriteMaterialProperties.Default;
|
|
|
|
[NonSerialized]
|
|
private TMPro.TextMeshProUGUI _tmProText;
|
|
[NonSerialized]
|
|
private string _textPrev;
|
|
[NonSerialized]
|
|
private readonly StringBuilder _processTagSb = new StringBuilder();
|
|
[NonSerialized]
|
|
private readonly StringBuilder _tempSb = new StringBuilder();
|
|
[NonSerialized]
|
|
private readonly StringBuilder _tempSb2 = new StringBuilder();
|
|
[NonSerialized]
|
|
private Asset _primaryAsset;
|
|
[NonSerialized]
|
|
private readonly List<Asset> _assignedAssets = new List<Asset>();
|
|
[NonSerialized]
|
|
private readonly List<Asset> _assetsPool = new List<Asset>();
|
|
[NonSerialized]
|
|
private readonly List<ActionElementMap> _tempAems = new List<ActionElementMap>();
|
|
[NonSerialized]
|
|
private readonly List<UnityEngine.Sprite> _tempGlyphs = new List<UnityEngine.Sprite>();
|
|
[NonSerialized]
|
|
private readonly List<Asset> _dirtyAssets = new List<Asset>();
|
|
[NonSerialized]
|
|
private readonly List<string> _tempKeys = new List<string>();
|
|
[NonSerialized]
|
|
private readonly List<GlyphOrText> _glyphsOrTextTemp = new List<GlyphOrText>();
|
|
[NonSerialized]
|
|
private readonly List<Asset> _currentlyUsedAssets = new List<Asset>();
|
|
[NonSerialized]
|
|
private readonly List<Tag> _currentTags = new List<Tag>();
|
|
[NonSerialized]
|
|
private Dictionary<string, string> _tempStringDictionary = new Dictionary<string, string>();
|
|
[NonSerialized]
|
|
private bool _initialized;
|
|
[NonSerialized]
|
|
private bool _rebuildRequired;
|
|
[NonSerialized]
|
|
private UnityEngine.Texture2D _stubTexture;
|
|
|
|
private Tag.Pool<ControllerElementTag> __controllerElementTagPool;
|
|
private Tag.Pool<ControllerElementTag> controllerElementTagPool {
|
|
get {
|
|
return __controllerElementTagPool != null ? __controllerElementTagPool : (__controllerElementTagPool = new Tag.Pool<ControllerElementTag>());
|
|
}
|
|
}
|
|
|
|
private Tag.Pool<ActionTag> __actionTagPool;
|
|
private Tag.Pool<ActionTag> actionTagPool {
|
|
get {
|
|
return __actionTagPool != null ? __actionTagPool : (__actionTagPool = new Tag.Pool<ActionTag>());
|
|
}
|
|
}
|
|
|
|
private Tag.Pool<PlayerTag> __playerTagPool;
|
|
private Tag.Pool<PlayerTag> playerTagPool {
|
|
get {
|
|
return __playerTagPool != null ? __playerTagPool : (__playerTagPool = new Tag.Pool<PlayerTag>());
|
|
}
|
|
}
|
|
|
|
[NonSerialized]
|
|
private Dictionary<string, ParseTagAttributesHandler> __tagHandlers;
|
|
private Dictionary<string, ParseTagAttributesHandler> tagHandlers {
|
|
get {
|
|
return __tagHandlers != null ? __tagHandlers : (__tagHandlers = new Dictionary<string, ParseTagAttributesHandler>() {
|
|
{ "rewiredelement", ProcessTag_ControllerElement },
|
|
{ "rewiredaction", ProcessTag_Action },
|
|
{ "rewiredplayer", ProcessTag_Player }
|
|
});
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Text will be parsed for special tags, and the final result will be passed on to the Text Mesh Pro Text component.
|
|
/// </summary>
|
|
/// <example>
|
|
/// <![CDATA[
|
|
/// Tag Format:
|
|
///
|
|
/// Display glyph / text for a controller element bound to an Action for a Player:
|
|
/// <rewiredElement [attributes]>
|
|
/// Attributes:
|
|
/// type="[glyphOrText|glyph|text]" (optional, default: glyphOrText)
|
|
/// playerId=[player_id] (id or name required)
|
|
/// playerName="[player_name]" (id or name required)
|
|
/// actionId=[action_id] (id or name required)
|
|
/// actionName="[action_name]" (id or name required)
|
|
/// actionRange="[full|positive|negative]" (optional, default: full)
|
|
/// Example: <rewiredElement type="glyphOrText" playerId=5 actionName="MoveHorizontal" actionRange="full">
|
|
///
|
|
/// Display the name of an Action:
|
|
/// <rewiredAction [attributes]>
|
|
/// Attributes:
|
|
/// id=[action_id] (id or name required)
|
|
/// name=[action_name] (id or name required)
|
|
/// range="[full|positive|negative]" (optional, default: full)
|
|
/// Example: <rewiredAction name="MoveHorizontal" range="positive">
|
|
///
|
|
/// Display the name of a Player:
|
|
/// <rewiredPlayer [attributes]>
|
|
/// Attributes:
|
|
/// id=[action_id] (id or name required)
|
|
/// name=[action_name] (id or name required)
|
|
/// Example: <rewiredPlayer id=0>
|
|
/// ]]>
|
|
/// </example>
|
|
public virtual string text {
|
|
get {
|
|
return _text;
|
|
}
|
|
set {
|
|
_text = value;
|
|
RequireRebuild();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Optional reference to an object that defines options.
|
|
/// If blank, the global default options will be used.
|
|
/// </summary>
|
|
public virtual ControllerElementGlyphSelectorOptionsSOBase options {
|
|
get {
|
|
return _options;
|
|
}
|
|
set {
|
|
_options = value;
|
|
RequireRebuild();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Options that control how Text Mesh Pro displays Sprites.
|
|
/// </summary>
|
|
public virtual TMProSpriteOptions spriteOptions {
|
|
get {
|
|
return _spriteOptions;
|
|
}
|
|
set {
|
|
_spriteOptions = value;
|
|
|
|
// Set values in all sprites in all assets
|
|
int count = _assignedAssets.Count;
|
|
int spriteCount;
|
|
ITMProSprite sprite;
|
|
UnityEngine.Rect rect;
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
spriteCount = _assignedAssets[i].spriteAsset.spriteCount;
|
|
for (int j = 0; j < spriteCount; j++) {
|
|
sprite = _assignedAssets[i].spriteAsset.GetSprite(j);
|
|
if (sprite == null || sprite.sprite == null) continue;
|
|
rect = sprite.sprite.rect;
|
|
sprite.xOffset = rect.width * _spriteOptions.offsetSizeMultiplier.x + _spriteOptions.extraOffset.x;
|
|
sprite.yOffset = rect.height * _spriteOptions.offsetSizeMultiplier.y + _spriteOptions.extraOffset.y;
|
|
sprite.xAdvance = rect.width * _spriteOptions.xAdvanceWidthMultiplier + _spriteOptions.extraXAdvance;
|
|
sprite.scale = _spriteOptions.scale;
|
|
}
|
|
// Make TMPro update the Sprite scale
|
|
TMPro.TMPro_EventManager.ON_SPRITE_ASSET_PROPERTY_CHANGED(true, _assignedAssets[i].spriteAsset.GetSpriteAsset());
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Optional material for Sprites. If blank, the default material will be used.
|
|
/// Material is instantiated for each Sprite Asset, so making changes to values in the base material later will not affect Sprites.
|
|
/// Changing the base material at runtime will copy only certain properties from the new material to Sprite materials.
|
|
/// </summary>
|
|
public virtual UnityEngine.Material baseSpriteMaterial {
|
|
get {
|
|
return _baseSpriteMaterial;
|
|
}
|
|
set {
|
|
_baseSpriteMaterial = value;
|
|
|
|
UnityEngine.Material sourceMaterial = _baseSpriteMaterial != null ? _baseSpriteMaterial : _primaryAsset.material;
|
|
|
|
ForEachAsset(asset => {
|
|
CopyMaterialProperties(sourceMaterial, asset.material);
|
|
if (_overrideSpriteMaterialProperties) {
|
|
CopySpriteMaterialPropertiesToMaterial(_spriteMaterialProperties, asset.material);
|
|
}
|
|
TMPro.TMPro_EventManager.ON_MATERIAL_PROPERTY_CHANGED(true, asset.material);
|
|
});
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// If enabled, local values such as Sprite color will be used instead of the value on the base material.
|
|
/// </summary>
|
|
public virtual bool overrideSpriteMaterialProperties {
|
|
get {
|
|
return _overrideSpriteMaterialProperties;
|
|
}
|
|
set {
|
|
_overrideSpriteMaterialProperties = value;
|
|
if (value) {
|
|
ForEachAsset(asset => {
|
|
CopySpriteMaterialPropertiesToMaterial(_spriteMaterialProperties, asset.material);
|
|
TMPro.TMPro_EventManager.ON_MATERIAL_PROPERTY_CHANGED(true, asset.material);
|
|
});
|
|
} else {
|
|
UnityEngine.Material sourceMaterial = _baseSpriteMaterial != null ? _baseSpriteMaterial : _primaryAsset.material;
|
|
ForEachAsset(asset => {
|
|
CopyMaterialProperties(sourceMaterial, asset.material);
|
|
TMPro.TMPro_EventManager.ON_MATERIAL_PROPERTY_CHANGED(true, asset.material);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// These properties will override the properties on the Sprite material if <see cref="overrideSpriteMaterialProperties"/> is true.
|
|
/// </summary>
|
|
public virtual SpriteMaterialProperties spriteMaterialProperties {
|
|
get {
|
|
return _spriteMaterialProperties;
|
|
}
|
|
set {
|
|
_spriteMaterialProperties = value;
|
|
if (!_overrideSpriteMaterialProperties) return;
|
|
ForEachAsset(asset => {
|
|
CopySpriteMaterialPropertiesToMaterial(_spriteMaterialProperties, asset.material);
|
|
TMPro.TMPro_EventManager.ON_MATERIAL_PROPERTY_CHANGED(true, asset.material);
|
|
});
|
|
}
|
|
}
|
|
|
|
protected virtual void OnEnable() {
|
|
Initialize();
|
|
}
|
|
|
|
protected virtual void Start() {
|
|
MainUpdate();
|
|
}
|
|
|
|
protected virtual void Update() {
|
|
if (!ReInput.isReady) return;
|
|
|
|
#if UNITY_EDITOR
|
|
// Handle recompile in Play mode
|
|
if (!Initialize()) return;
|
|
|
|
if (UnityEngine.Input.GetKeyDown(UnityEngine.KeyCode.M)) {
|
|
baseSpriteMaterial = _baseSpriteMaterial;
|
|
}
|
|
|
|
// Handle inspector value changes
|
|
if (_editorInspectorChanged) {
|
|
spriteOptions = _spriteOptions;
|
|
baseSpriteMaterial = _baseSpriteMaterial;
|
|
overrideSpriteMaterialProperties = _overrideSpriteMaterialProperties;
|
|
spriteMaterialProperties = _spriteMaterialProperties;
|
|
RequireRebuild();
|
|
_editorInspectorChanged = false;
|
|
}
|
|
#endif
|
|
|
|
MainUpdate();
|
|
}
|
|
|
|
protected virtual void OnDestroy() {
|
|
// Clean up all assets
|
|
if (_primaryAsset != null) {
|
|
if (_tmProText != null) {
|
|
if (_tmProText.spriteAsset == _primaryAsset.spriteAsset.GetSpriteAsset()) {
|
|
_tmProText.spriteAsset = null;
|
|
}
|
|
}
|
|
_primaryAsset.Destroy();
|
|
_primaryAsset = null;
|
|
}
|
|
for (int i = 0; i < _assignedAssets.Count; i++) {
|
|
if (_assignedAssets[i] == null) continue;
|
|
_assignedAssets[i].Destroy();
|
|
}
|
|
_assignedAssets.Clear();
|
|
for (int i = 0; i < _assetsPool.Count; i++) {
|
|
if (_assetsPool[i] == null) continue;
|
|
_assetsPool[i].Destroy();
|
|
}
|
|
_assetsPool.Clear();
|
|
if (_stubTexture != null) {
|
|
Destroy(_stubTexture);
|
|
_stubTexture = null;
|
|
}
|
|
for (int i = 0; i < _currentTags.Count; i++) {
|
|
_currentTags[i].ReturnToPool();
|
|
}
|
|
}
|
|
|
|
#if UNITY_EDITOR
|
|
|
|
[NonSerialized]
|
|
private bool _editorInspectorChanged;
|
|
|
|
protected virtual void OnValidate() {
|
|
_editorInspectorChanged = true;
|
|
}
|
|
|
|
#endif
|
|
|
|
/// <summary>
|
|
/// Updates glyphs / text and TMPro text value immedately.
|
|
/// Normally, this happens automatically in the Update loop.
|
|
/// </summary>
|
|
public virtual void ForceUpdate() {
|
|
if (!ReInput.isReady) return;
|
|
_rebuildRequired = true;
|
|
Update();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the Controller Element Glyph Options if set, otherwise the default options.
|
|
/// </summary>
|
|
/// <returns>The Controller Element Glyph Options if set, otherwise the default options.</returns>
|
|
protected virtual ControllerElementGlyphSelectorOptions GetOptionsOrDefault() {
|
|
if (_options != null && _options.options == null) {
|
|
UnityEngine.Debug.LogError("Rewired: Options missing on " + typeof(ControllerElementGlyphSelectorOptions).Name + ". Global default options will be used instead.");
|
|
return ControllerElementGlyphSelectorOptions.defaultOptions;
|
|
}
|
|
return _options != null ? _options.options : ControllerElementGlyphSelectorOptions.defaultOptions;
|
|
}
|
|
|
|
private bool Initialize() {
|
|
if (_initialized) return true;
|
|
#if UNITY_EDITOR
|
|
if (UnityEditor.EditorApplication.isCompiling) return false;
|
|
#endif
|
|
_tmProText = GetComponent<TMPro.TextMeshProUGUI>();
|
|
_stubTexture = new UnityEngine.Texture2D(1, 1);
|
|
CreatePrimaryAsset();
|
|
_initialized = true;
|
|
return true;
|
|
}
|
|
|
|
private void MainUpdate() {
|
|
|
|
bool changed = false;
|
|
|
|
// Monitor changes to glyph / text results.
|
|
// This is necessary because controller element glyph changes may occur when
|
|
// last active controller changes, when glyph sets change, when glyphs are
|
|
// disabled / enabled, when mappings change, or in the case of text,
|
|
// when localization changes.
|
|
{
|
|
Tag tag;
|
|
int count = _currentTags.Count;
|
|
for (int i = 0; i < count; i++) {
|
|
tag = _currentTags[i];
|
|
switch (tag.tagType) {
|
|
case Tag.TagType.ControllerElement: {
|
|
// Monitor controller element glyph changes
|
|
ControllerElementTag tTag = (ControllerElementTag)tag;
|
|
_glyphsOrTextTemp.Clear();
|
|
TryGetControllerElementGlyphsOrText((ControllerElementTag)tag, _glyphsOrTextTemp);
|
|
if (!IsEqual(_glyphsOrTextTemp, tTag.glyphsOrText)) {
|
|
changed = true;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case Tag.TagType.Action: {
|
|
// Monitor Action changes
|
|
ActionTag tTag = (ActionTag)tag;
|
|
string displayName;
|
|
TryGetActionDisplayName(tTag, out displayName);
|
|
if (!string.Equals(tTag.displayName, displayName, StringComparison.Ordinal)) {
|
|
changed = true;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case Tag.TagType.Player: {
|
|
// Monitor Player changes
|
|
PlayerTag tTag = (PlayerTag)tag;
|
|
string displayName;
|
|
TryGetPlayerDisplayName(tTag, out displayName);
|
|
if (!string.Equals(tTag.displayName, displayName, StringComparison.Ordinal)) {
|
|
changed = true;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
throw new NotImplementedException();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Monitor text changes
|
|
if (!string.Equals(_text, _textPrev, StringComparison.Ordinal)) {
|
|
_textPrev = _text;
|
|
changed = true;
|
|
}
|
|
|
|
// Update text on change
|
|
if (changed || _rebuildRequired) {
|
|
// Parse text and replace Rewired tags with TMPro sprite tags
|
|
string newText;
|
|
if (ParseText(_textPrev, out newText)) {
|
|
_tmProText.text = newText;
|
|
}
|
|
}
|
|
|
|
// Process Sprite Assets that changed
|
|
{
|
|
int count = _dirtyAssets.Count;
|
|
if (count > 0) {
|
|
for (int i = 0; i < count; i++) {
|
|
_dirtyAssets[i].spriteAsset.UpdateLookupTables();
|
|
}
|
|
_dirtyAssets.Clear();
|
|
}
|
|
}
|
|
}
|
|
|
|
private bool ParseText(string text, out string newText) {
|
|
newText = null;
|
|
|
|
// Clear current tags and build new onesfrom the text
|
|
Tag.Clear(_currentTags);
|
|
|
|
// Clear list so used assets can be detected after parsing
|
|
_currentlyUsedAssets.Clear();
|
|
|
|
bool changed = false;
|
|
|
|
// Controller Element
|
|
while (ProcessNextTag(ref text, _processTagSb)) {
|
|
changed = true;
|
|
newText = text;
|
|
}
|
|
|
|
RemoveUnusedAssets();
|
|
|
|
if (_rebuildRequired) {
|
|
_rebuildRequired = false;
|
|
}
|
|
|
|
return changed;
|
|
}
|
|
|
|
private bool ProcessNextTag(ref string text, StringBuilder sb) {
|
|
const char tagStart = '<';
|
|
const char tagEnd = '>';
|
|
|
|
const int state_searchForStartTag = 0;
|
|
const int state_searchForTagName = 1;
|
|
const int state_parseAttributes = 2;
|
|
|
|
int state = state_searchForStartTag;
|
|
|
|
ParseTagAttributesHandler parseTagAttributesHandler = null;
|
|
string replacementString;
|
|
char c;
|
|
int tagStartIndex = -1;
|
|
|
|
try {
|
|
for (int i = 0; i < text.Length; i++) {
|
|
c = text[i];
|
|
|
|
switch (state) {
|
|
case state_searchForStartTag:
|
|
if (c == tagStart) {
|
|
tagStartIndex = i;
|
|
sb.Length = 0;
|
|
state = state_searchForTagName;
|
|
}
|
|
break;
|
|
case state_searchForTagName:
|
|
if (IsValidTagNameChar(c)) {
|
|
sb.Append(char.ToLowerInvariant(c));
|
|
} else if (char.IsWhiteSpace(c)) {
|
|
if (sb.Length > 0) {
|
|
if (tagHandlers.TryGetValue(sb.ToString(), out parseTagAttributesHandler)) {
|
|
sb.Length = 0;
|
|
state = state_parseAttributes;
|
|
} else { // invalid tag name
|
|
state = state_searchForStartTag; // start over
|
|
i -= 1;
|
|
}
|
|
}
|
|
} else { // error
|
|
state = state_searchForStartTag; // start over
|
|
i -= 1;
|
|
}
|
|
break;
|
|
case state_parseAttributes: {
|
|
int tagEndIndex = text.IndexOf(tagEnd, i); // TODO: This is possibly unsafe. If '>' is contained within any attribute string, it will fail.
|
|
if (tagEndIndex < 0) throw new Exception("Malformed tag."); // error
|
|
if (parseTagAttributesHandler(text, i, tagEndIndex - i, out replacementString)) {
|
|
// replace string
|
|
sb.Length = 0;
|
|
if (tagStartIndex > 0) {
|
|
sb.Append(text, 0, tagStartIndex);
|
|
}
|
|
sb.Append(replacementString);
|
|
int tailStartIndex = tagEndIndex + 1;
|
|
if (tailStartIndex < text.Length) {
|
|
sb.Append(text, tailStartIndex, text.Length - tailStartIndex);
|
|
}
|
|
text = sb.ToString();
|
|
return true;
|
|
} else { // error
|
|
throw new Exception("Error parsing attributes.");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
} catch (Exception ex) {
|
|
UnityEngine.Debug.LogError(ex);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private bool ProcessTag_ControllerElement(string text, int startIndex, int count, out string replacement) {
|
|
ControllerElementTag tag;
|
|
if (!ControllerElementTag.TryParseString(text, startIndex, count, _tempSb, _tempSb2, _tempStringDictionary, controllerElementTagPool, out tag)) {
|
|
replacement = null;
|
|
return false; // only fail if parsing fails
|
|
}
|
|
|
|
_currentTags.Add(tag);
|
|
|
|
// Replace text with glyph / text
|
|
|
|
tag.glyphsOrText.Clear();
|
|
if (!TryGetControllerElementGlyphsOrText(tag, tag.glyphsOrText)) {
|
|
replacement = null;
|
|
return true;
|
|
}
|
|
|
|
TryCreateTMProString(tag.glyphsOrText, out replacement);
|
|
|
|
return true;
|
|
}
|
|
|
|
private bool ProcessTag_Action(string text, int startIndex, int count, out string replacement) {
|
|
ActionTag tag;
|
|
if (!ActionTag.TryParseString(text, startIndex, count, _tempSb, _tempSb2, _tempStringDictionary, actionTagPool, out tag)) {
|
|
replacement = null;
|
|
return false; // only fail if parsing fails
|
|
}
|
|
|
|
_currentTags.Add(tag);
|
|
|
|
TryGetActionDisplayName(tag, out replacement);
|
|
|
|
return true;
|
|
}
|
|
|
|
private bool ProcessTag_Player(string text, int startIndex, int count, out string replacement) {
|
|
PlayerTag tag;
|
|
if (!PlayerTag.TryParseString(text, startIndex, count, _tempSb, _tempSb2, _tempStringDictionary, playerTagPool, out tag)) {
|
|
replacement = null;
|
|
return false; // only fail if parsing fails
|
|
}
|
|
|
|
_currentTags.Add(tag);
|
|
|
|
TryGetPlayerDisplayName(tag, out replacement);
|
|
|
|
return true;
|
|
}
|
|
|
|
private bool TryCreateTMProString(List<GlyphOrText> glyphs, out string result) {
|
|
|
|
// Replace text with glyph / text
|
|
|
|
StringBuilder sb = _tempSb;
|
|
sb.Length = 0;
|
|
string spriteKey;
|
|
|
|
int resultCount = glyphs.Count;
|
|
for (int i = 0; i < resultCount; i++) {
|
|
spriteKey = glyphs[i].glyphKey;
|
|
|
|
// Prioritize glyphs
|
|
if (glyphs[i].sprite != null &&
|
|
!string.IsNullOrEmpty(spriteKey) &&
|
|
TryAssignSprite(glyphs[i].sprite, spriteKey)) {
|
|
WriteSpriteKey(sb, spriteKey);
|
|
} else {
|
|
// Fall back to text
|
|
sb.Append(glyphs[i].name);
|
|
}
|
|
|
|
if (i < resultCount - 1) sb.Append(" ");
|
|
}
|
|
|
|
result = sb.ToString();
|
|
|
|
return !string.IsNullOrEmpty(result);
|
|
}
|
|
|
|
private bool TryGetControllerElementGlyphsOrText(ControllerElementTag tag, List<GlyphOrText> results) {
|
|
if (tag == null) return false;
|
|
|
|
ActionElementMap aemResult1;
|
|
ActionElementMap aemResult2;
|
|
|
|
_tempAems.Clear();
|
|
if (!GlyphTools.TryGetActionElementMaps(tag.playerId, tag.actionId, tag.actionRange, GetOptionsOrDefault(), _tempAems, out aemResult1, out aemResult2)) {
|
|
return false;
|
|
}
|
|
|
|
object glyph;
|
|
string glyphKey;
|
|
string name;
|
|
|
|
// Two split axis bindings
|
|
if (aemResult1 != null && aemResult2 != null) {
|
|
|
|
GlyphOrText r = new GlyphOrText();
|
|
|
|
// Handle special combined glyphs (D-Pad Left + D-Pad Right = D-Pad Horizontal)
|
|
_tempAems.Clear();
|
|
_tempAems.Add(aemResult1);
|
|
_tempAems.Add(aemResult2);
|
|
if (IsGlyphAllowed(tag.type) && ActionElementMap.TryGetCombinedElementIdentifierGlyph(_tempAems, out glyph) &&
|
|
ActionElementMap.TryGetCombinedElementIdentifierFinalGlyphKey(_tempAems, out glyphKey)) {
|
|
r.glyphKey = glyphKey;
|
|
r.sprite = glyph as UnityEngine.Sprite;
|
|
results.Add(r);
|
|
return true;
|
|
} else if (IsTextAllowed(tag.type) && ActionElementMap.TryGetCombinedElementIdentifierName(_tempAems, out name)) {
|
|
r.name = name;
|
|
results.Add(r);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
bool result = false;
|
|
|
|
// Positive and negative bindings
|
|
_tempGlyphs.Clear();
|
|
_tempKeys.Clear();
|
|
result |= TryGetGlyphsOrText(aemResult1, tag.type, _tempGlyphs, _tempKeys, results);
|
|
_tempGlyphs.Clear();
|
|
_tempKeys.Clear();
|
|
result |= TryGetGlyphsOrText(aemResult2, tag.type, _tempGlyphs, _tempKeys, results);
|
|
|
|
return result;
|
|
}
|
|
|
|
private bool TryGetActionDisplayName(ActionTag tag, out string result) {
|
|
if (tag == null) {
|
|
result = null;
|
|
return false;
|
|
}
|
|
|
|
InputAction action = ReInput.mapping.GetAction(tag.actionId);
|
|
if (action == null) {
|
|
result = null;
|
|
return false;
|
|
}
|
|
|
|
result = action.GetDisplayName(tag.actionRange);
|
|
|
|
tag.displayName = result;
|
|
|
|
return true;
|
|
}
|
|
|
|
private bool TryGetPlayerDisplayName(PlayerTag tag, out string result) {
|
|
if (tag == null) {
|
|
result = null;
|
|
return false;
|
|
}
|
|
|
|
var player = ReInput.players.GetPlayer(tag.playerId);
|
|
if (player == null) {
|
|
result = null;
|
|
return false;
|
|
}
|
|
|
|
result = player.descriptiveName;
|
|
|
|
tag.displayName = result;
|
|
|
|
return true;
|
|
}
|
|
|
|
private bool TryAssignSprite(UnityEngine.Sprite sprite, string key) {
|
|
|
|
Asset asset = GetOrCreateAsset(sprite);
|
|
if (asset == null) return false;
|
|
|
|
ITMProSpriteAsset spriteAsset = asset.spriteAsset;
|
|
|
|
if (!spriteAsset.Contains(key)) {
|
|
|
|
UnityEngine.Rect rect = sprite.rect;
|
|
|
|
ITMProSprite tmpSprite = TMProAssetVersionHelper.CreateSprite();
|
|
// id is set in AddSprite
|
|
tmpSprite.width = rect.width;
|
|
tmpSprite.height = rect.height;
|
|
tmpSprite.position = new UnityEngine.Vector2(rect.x, rect.y);
|
|
tmpSprite.xOffset = rect.width * _spriteOptions.offsetSizeMultiplier.x + _spriteOptions.extraOffset.x;
|
|
tmpSprite.yOffset = rect.height * _spriteOptions.offsetSizeMultiplier.y + _spriteOptions.extraOffset.y;
|
|
tmpSprite.xAdvance = rect.width * _spriteOptions.xAdvanceWidthMultiplier + _spriteOptions.extraXAdvance;
|
|
tmpSprite.scale = _spriteOptions.scale;
|
|
tmpSprite.pivot = new UnityEngine.Vector2(
|
|
rect.width * -0.5f,
|
|
rect.height * 0.5f
|
|
);
|
|
tmpSprite.name = key;
|
|
tmpSprite.hashCode = TMPro.TMP_TextUtilities.GetSimpleHashCode(key);
|
|
tmpSprite.sprite = sprite;
|
|
|
|
spriteAsset.AddSprite(tmpSprite);
|
|
SetDirty(asset);
|
|
}
|
|
|
|
// Add to currently used assets list
|
|
if (!_currentlyUsedAssets.Contains(asset)) {
|
|
_currentlyUsedAssets.Add(asset);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private void RequireRebuild() {
|
|
_rebuildRequired = true;
|
|
}
|
|
|
|
#region Sprite Assets
|
|
|
|
private void CreatePrimaryAsset() {
|
|
if (_primaryAsset != null) return;
|
|
_primaryAsset = new Asset(null); // always create with default material, not user-defined base material
|
|
_tmProText.spriteAsset = _primaryAsset.spriteAsset.GetSpriteAsset();
|
|
}
|
|
|
|
private Asset GetOrCreateAsset(UnityEngine.Sprite sprite) {
|
|
if (sprite == null || sprite.texture == null) return null;
|
|
|
|
// Search assigned list first
|
|
{
|
|
int count = _assignedAssets.Count;
|
|
for (int i = 0; i < count; i++) {
|
|
if (_assignedAssets[i] == null) continue;
|
|
if (_assignedAssets[i].spriteAsset.spriteSheet == sprite.texture) {
|
|
return _assignedAssets[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
Asset asset = null;
|
|
|
|
// Not found
|
|
// Check pool for an unused asset
|
|
{
|
|
int count = _assetsPool.Count;
|
|
for (int i = 0; i < count; i++) {
|
|
if (_assetsPool[i] == null) continue;
|
|
asset = _assetsPool[i];
|
|
_assetsPool.RemoveAt(i); // remove from pool
|
|
break;
|
|
}
|
|
}
|
|
if (asset == null) { // No asset found. Create one.
|
|
asset = CreateAsset();
|
|
}
|
|
|
|
// Set texture in Sprite Asset and Material
|
|
asset.spriteAsset.spriteSheet = sprite.texture;
|
|
asset.material.SetTexture(TMPro.ShaderUtilities.ID_MainTex, sprite.texture);
|
|
|
|
// Assign the Sprite Asset to the primary as a fallback
|
|
|
|
List<TMPro.TMP_SpriteAsset> fallbacks = _primaryAsset.spriteAsset.GetSpriteAsset().fallbackSpriteAssets;
|
|
if (fallbacks == null) {
|
|
fallbacks = new List<TMPro.TMP_SpriteAsset>();
|
|
_primaryAsset.spriteAsset.GetSpriteAsset().fallbackSpriteAssets = fallbacks;
|
|
}
|
|
fallbacks.Add(asset.spriteAsset.GetSpriteAsset());
|
|
|
|
_assignedAssets.Add(asset);
|
|
|
|
return asset;
|
|
}
|
|
|
|
private Asset CreateAsset() {
|
|
Asset asset = new Asset(_baseSpriteMaterial);
|
|
if (_overrideSpriteMaterialProperties) {
|
|
CopySpriteMaterialPropertiesToMaterial(_spriteMaterialProperties, asset.material);
|
|
}
|
|
return asset;
|
|
}
|
|
|
|
private void RemoveUnusedAssets() {
|
|
|
|
// Allow a few assets to remain assigned to avoid wasted resources swapping
|
|
// assets back and forth when changing between last active controllers.
|
|
const int allowedUnusedAssetCount = 2;
|
|
|
|
// Find assets that are unused and remove them
|
|
|
|
Asset asset;
|
|
int unusedAssetCount = 0;
|
|
int count = _assignedAssets.Count;
|
|
|
|
for (int i = count - 1; i >= 0; i--) { // go backwards so newest unused assets are preserved first
|
|
asset = _assignedAssets[i];
|
|
if (asset == null) continue;
|
|
if (!_currentlyUsedAssets.Contains(asset)) { // not used anymore
|
|
if (unusedAssetCount >= allowedUnusedAssetCount) { // no more extras allowed
|
|
// Remove from primary sprite asset fallback list
|
|
_primaryAsset.spriteAsset.GetSpriteAsset().fallbackSpriteAssets.Remove(asset.spriteAsset.GetSpriteAsset());
|
|
// Clean up values when adding to pool
|
|
asset.spriteAsset.spriteSheet = null;
|
|
asset.spriteAsset.Clear();
|
|
asset.material.SetTexture(TMPro.ShaderUtilities.ID_MainTex, _stubTexture); // set to stub to prevent TMPro from throwing null reference exceptions
|
|
_assetsPool.Add(asset); // add to pool
|
|
_assignedAssets.RemoveAt(i); // remove from assigned assets
|
|
} else {
|
|
unusedAssetCount += 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void SetDirty(Asset asset) {
|
|
if (_dirtyAssets.Contains(asset)) return;
|
|
_dirtyAssets.Add(asset);
|
|
}
|
|
|
|
private void ForEachAsset(Action<Asset> callback) {
|
|
if (callback == null) return;
|
|
|
|
int count;
|
|
|
|
// Set values in all sprites in all assets
|
|
count = _assignedAssets.Count;
|
|
for (int i = 0; i < count; i++) {
|
|
if (_assignedAssets[i] == null) continue;
|
|
callback(_assignedAssets[i]);
|
|
|
|
}
|
|
|
|
// Material properties need to be set in pool as well
|
|
count = _assetsPool.Count;
|
|
for (int i = 0; i < count; i++) {
|
|
if (_assetsPool[i] == null) continue;
|
|
callback(_assetsPool[i]);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Static
|
|
|
|
private static int shaderPropertyId_color { get { return UnityEngine.Shader.PropertyToID("_Color"); } }
|
|
|
|
private static string[] __s_displayTypeNames;
|
|
private static string[] s_displayTypeNames { get { return __s_displayTypeNames != null ? __s_displayTypeNames : (__s_displayTypeNames = Enum.GetNames(typeof(DisplayType))); } }
|
|
|
|
private static DisplayType[] __s_displayTypeValues;
|
|
private static DisplayType[] s_displayTypeValues { get { return __s_displayTypeValues != null ? __s_displayTypeValues : (__s_displayTypeValues = (DisplayType[])Enum.GetValues(typeof(DisplayType))); } }
|
|
|
|
private static string[] __s_axisRangeNames;
|
|
private static string[] s_axisRangeNames { get { return __s_axisRangeNames != null ? __s_axisRangeNames : (__s_axisRangeNames = Enum.GetNames(typeof(AxisRange))); } }
|
|
|
|
private static AxisRange[] __s_axisRangeValues;
|
|
private static AxisRange[] s_axisRangeValues { get { return __s_axisRangeValues != null ? __s_axisRangeValues : (__s_axisRangeValues = (AxisRange[])Enum.GetValues(typeof(AxisRange))); } }
|
|
|
|
private static void ParseAttributes(string text, int startIndex, int count, StringBuilder sbKey, StringBuilder sbValue, Dictionary<string, string> results) {
|
|
if (string.IsNullOrEmpty(text) || startIndex < 0 || startIndex >= text.Length) {
|
|
return;
|
|
}
|
|
|
|
results.Clear();
|
|
|
|
const char doubleQuote = '\"';
|
|
|
|
const int state_searchForKey = 0;
|
|
const int state_parsingKey = 1;
|
|
const int state_searchForValue = 2;
|
|
const int state_parsingValue = 3;
|
|
|
|
sbKey.Length = 0;
|
|
sbValue.Length = 0;
|
|
bool isQuotedValue = true;
|
|
char c;
|
|
int lastIndex = startIndex + count - 1;
|
|
|
|
int state = state_searchForKey;
|
|
|
|
try {
|
|
|
|
for (int i = startIndex; i < startIndex + count; i++) {
|
|
c = text[i];
|
|
switch (state) {
|
|
case state_searchForKey:
|
|
if (IsValidKeyChar(c)) {
|
|
state = state_parsingKey;
|
|
i -= 1;
|
|
sbKey.Length = 0;
|
|
}
|
|
break;
|
|
case state_parsingKey:
|
|
if (c == '=') {
|
|
if (sbKey.Length == 0) throw new Exception("Key was blank."); // no key
|
|
state = state_searchForValue;
|
|
} else if (IsValidKeyChar(c)) {
|
|
sbKey.Append(char.ToLowerInvariant(c));
|
|
} else if (char.IsWhiteSpace(c)) {
|
|
// skip
|
|
} else { // parsing error
|
|
throw new Exception("Error parsing key.");
|
|
}
|
|
break;
|
|
case state_searchForValue: {
|
|
if ((isQuotedValue = c == doubleQuote) || IsValidNonQuotedValueChar(c)) { // allow quoted and non-quoted values
|
|
if (!isQuotedValue) {
|
|
i -= 1;
|
|
}
|
|
sbValue.Length = 0;
|
|
state = state_parsingValue;
|
|
}
|
|
}
|
|
break;
|
|
case state_parsingValue:
|
|
{
|
|
if ((isQuotedValue && c == doubleQuote) || (!isQuotedValue && (i == lastIndex || char.IsWhiteSpace(c)))) { // end of value
|
|
// Append value on last index for non-quoted values, otherwise it will fail if no space before closing tag
|
|
if (!isQuotedValue && i == lastIndex) sbValue.Append(c);
|
|
if (sbValue.Length == 0) throw new Exception("Value was blank."); // no value
|
|
if (results == null) results = new Dictionary<string, string>();
|
|
results.Add(sbKey.ToString(), sbValue.ToString());
|
|
state = state_searchForKey;
|
|
} else {
|
|
sbValue.Append(c);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
} catch (Exception ex) {
|
|
UnityEngine.Debug.LogError(ex);
|
|
}
|
|
}
|
|
|
|
private static bool IsValidKeyChar(char c) {
|
|
return char.IsLetterOrDigit(c) || c == '_';
|
|
}
|
|
|
|
private static bool IsValidTagNameChar(char c) {
|
|
return char.IsLetterOrDigit(c) || c == '_';
|
|
}
|
|
|
|
private static bool IsValidNonQuotedValueChar(char c) {
|
|
return char.IsDigit(c);
|
|
}
|
|
|
|
private static bool IsEqual(List<GlyphOrText> a, List<GlyphOrText> b) {
|
|
if (a.Count != b.Count) return false;
|
|
for (int i = 0; i < a.Count; i++) {
|
|
if (a[i] != b[i]) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private static void WriteSpriteKey(StringBuilder sb, string key) {
|
|
sb.Append("<sprite name=\"");
|
|
sb.Append(key);
|
|
sb.Append("\">");
|
|
}
|
|
|
|
private static bool TryGetGlyphsOrText(ActionElementMap aem, DisplayType displayType, List<UnityEngine.Sprite> glyphs, List<string> keys, List<GlyphOrText> results) {
|
|
if (aem == null || glyphs == null || results == null) return false;
|
|
|
|
GlyphOrText r;
|
|
|
|
if (IsGlyphAllowed(displayType) && aem.GetElementIdentifierGlyphs(glyphs) > 0) {
|
|
aem.GetElementIdentifierFinalGlyphKeys(keys);
|
|
if (keys.Count != glyphs.Count) {
|
|
UnityEngine.Debug.LogError("Rewired: Glyph key count does not match glyph count.");
|
|
goto TextFallback;
|
|
}
|
|
int count = glyphs.Count;
|
|
for (int i = 0; i < count; i++) {
|
|
r = new GlyphOrText();
|
|
r.glyphKey = keys[i];
|
|
r.sprite = glyphs[i];
|
|
results.Add(r);
|
|
}
|
|
if (count > 0) return true;
|
|
}
|
|
|
|
TextFallback:
|
|
|
|
if (IsTextAllowed(displayType)) {
|
|
r = new GlyphOrText();
|
|
r.name = aem.elementIdentifierName;
|
|
results.Add(r);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private static bool IsGlyphAllowed(DisplayType displayType) {
|
|
return displayType == DisplayType.Glyph || displayType == DisplayType.GlyphOrText;
|
|
}
|
|
|
|
private static bool IsTextAllowed(DisplayType displayType) {
|
|
return displayType == DisplayType.Text || displayType == DisplayType.GlyphOrText;
|
|
}
|
|
|
|
private static void CopyMaterialProperties(UnityEngine.Material source, UnityEngine.Material destination) {
|
|
if (source == null || destination == null) return;
|
|
destination.shader = source.shader;
|
|
if (source.shaderKeywords != null) {
|
|
string[] keywords = new string[source.shaderKeywords.Length];
|
|
Array.Copy(source.shaderKeywords, keywords, source.shaderKeywords.Length);
|
|
destination.shaderKeywords = keywords;
|
|
} else {
|
|
destination.shaderKeywords = null;
|
|
}
|
|
if (source.HasProperty(shaderPropertyId_color) && destination.HasProperty(shaderPropertyId_color)) {
|
|
destination.color = source.color;
|
|
}
|
|
destination.renderQueue = source.renderQueue;
|
|
destination.globalIlluminationFlags = source.globalIlluminationFlags;
|
|
}
|
|
|
|
private static void CopySpriteMaterialPropertiesToMaterial(SpriteMaterialProperties properties, UnityEngine.Material material) {
|
|
if (material == null) return;
|
|
if (material.HasProperty(shaderPropertyId_color)) {
|
|
material.color = properties.color;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Classes
|
|
|
|
private delegate bool ParseTagAttributesHandler(string text, int startIndex, int count, out string replacement);
|
|
|
|
private abstract class Tag {
|
|
|
|
public readonly TagType tagType;
|
|
private Pool _pool;
|
|
|
|
protected Tag(TagType tagType) {
|
|
this.tagType = tagType;
|
|
}
|
|
|
|
protected Pool pool {
|
|
get {
|
|
return _pool;
|
|
}
|
|
set {
|
|
_pool = value;
|
|
}
|
|
}
|
|
|
|
public void ReturnToPool() {
|
|
if (_pool == null) return;
|
|
_pool.Return(this);
|
|
}
|
|
|
|
protected abstract void Clear();
|
|
|
|
public static void Clear(List<Tag> list) {
|
|
int count = list.Count;
|
|
for (int i = 0; i < count; i++) {
|
|
if (list[i] == null) continue;
|
|
list[i].ReturnToPool();
|
|
}
|
|
list.Clear();
|
|
}
|
|
|
|
public enum TagType {
|
|
ControllerElement,
|
|
Action,
|
|
Player
|
|
}
|
|
|
|
public abstract class Pool {
|
|
|
|
public abstract bool Return(Tag obj);
|
|
}
|
|
|
|
public sealed class Pool<T> : Pool where T : Tag, new() {
|
|
|
|
private readonly List<T> _list;
|
|
|
|
public Pool() : base() {
|
|
_list = new List<T>();
|
|
}
|
|
|
|
public T Get() {
|
|
T obj;
|
|
if (_list.Count == 0) {
|
|
obj = new T();
|
|
if (obj != null) {
|
|
obj.pool = this;
|
|
}
|
|
return obj;
|
|
}
|
|
int index = _list.Count - 1;
|
|
obj = _list[index];
|
|
_list.RemoveAt(index);
|
|
return obj;
|
|
}
|
|
|
|
public override bool Return(Tag obj) {
|
|
T tObj = obj as T;
|
|
if (tObj == null || tObj.pool != this) return false;
|
|
tObj.Clear();
|
|
if (_list.Contains(tObj)) return false;
|
|
_list.Add(tObj);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
private sealed class ControllerElementTag : Tag {
|
|
|
|
public DisplayType type;
|
|
public int playerId;
|
|
public int actionId;
|
|
public AxisRange actionRange;
|
|
|
|
private readonly List<GlyphOrText> _glyphsOrText;
|
|
|
|
public List<GlyphOrText> glyphsOrText { get { return _glyphsOrText; } }
|
|
|
|
public override string ToString() {
|
|
StringBuilder sb = new StringBuilder();
|
|
sb.Append(typeof(ControllerElementTag).Name);
|
|
sb.Append(": ");
|
|
sb.Append("type = ");
|
|
sb.Append(type);
|
|
sb.Append(", playerId = ");
|
|
sb.Append(playerId);
|
|
sb.Append(", actionId = ");
|
|
sb.Append(actionId);
|
|
sb.Append(", actionRange = ");
|
|
sb.Append(actionRange);
|
|
return sb.ToString();
|
|
}
|
|
|
|
public ControllerElementTag() : base(TagType.ControllerElement) {
|
|
_glyphsOrText = new List<GlyphOrText>();
|
|
Clear();
|
|
}
|
|
|
|
protected override void Clear() {
|
|
type = DisplayType.GlyphOrText;
|
|
playerId = -1;
|
|
actionId = -1;
|
|
actionRange = AxisRange.Full;
|
|
_glyphsOrText.Clear();
|
|
}
|
|
|
|
// Static
|
|
|
|
public static bool TryParseString(string text, int startIndex, int count, StringBuilder sb1, StringBuilder sb2, Dictionary<string, string> workDictionary, Pool<ControllerElementTag> pool, out ControllerElementTag result) {
|
|
result = null;
|
|
if (string.IsNullOrEmpty(text) || startIndex < 0 || startIndex + count >= text.Length) return false;
|
|
|
|
ParseAttributes(text, startIndex, count, sb1, sb2, workDictionary);
|
|
if (workDictionary.Count == 0) return false;
|
|
|
|
string value;
|
|
result = pool.Get();
|
|
|
|
try {
|
|
|
|
// Type -- optional
|
|
if (workDictionary.TryGetValue("type", out value)) {
|
|
bool found = false;
|
|
for (int i = 0; i < s_displayTypeNames.Length; i++) {
|
|
if (string.Equals(value, s_displayTypeNames[i], StringComparison.OrdinalIgnoreCase)) {
|
|
result.type = s_displayTypeValues[i];
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!found) throw new Exception("Invalid type: " + value);
|
|
} else {
|
|
result.type = DisplayType.GlyphOrText; // default
|
|
}
|
|
|
|
// Player name or id
|
|
if (workDictionary.TryGetValue("playerid", out value)) {
|
|
result.playerId = int.Parse(value);
|
|
if (ReInput.players.GetPlayer(result.playerId) == null) throw new Exception("Invalid Player Id: " + result.playerId);
|
|
} else if (workDictionary.TryGetValue("playername", out value)) {
|
|
var player = ReInput.players.GetPlayer(value);
|
|
if (player == null) throw new Exception("Invalid Player name: " + value);
|
|
result.playerId = player.id;
|
|
} else { // error
|
|
throw new Exception("Player name/id missing.");
|
|
}
|
|
|
|
// Action name or id
|
|
if (workDictionary.TryGetValue("actionid", out value)) {
|
|
result.actionId = int.Parse(value);
|
|
if (ReInput.mapping.GetAction(result.actionId) == null) throw new Exception("Invalid Action Id: " + result.actionId);
|
|
} else if (workDictionary.TryGetValue("actionname", out value)) {
|
|
var action = ReInput.mapping.GetAction(value);
|
|
if (action == null) throw new Exception("Invalid Action name: " + value);
|
|
result.actionId = action.id;
|
|
} else { // error
|
|
throw new Exception("Action name/id missing.");
|
|
}
|
|
|
|
// Action Range -- optional
|
|
if (workDictionary.TryGetValue("actionrange", out value)) {
|
|
bool found = false;
|
|
for (int i = 0; i < s_axisRangeNames.Length; i++) {
|
|
if (string.Equals(value, s_axisRangeNames[i], StringComparison.OrdinalIgnoreCase)) {
|
|
result.actionRange = s_axisRangeValues[i];
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!found) throw new Exception("Invalid Action Range: " + value);
|
|
} else {
|
|
result.actionRange = AxisRange.Full;
|
|
}
|
|
|
|
return true;
|
|
|
|
} catch (Exception ex) {
|
|
UnityEngine.Debug.LogError(ex);
|
|
result.ReturnToPool();
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
private sealed class ActionTag : Tag {
|
|
|
|
public int actionId;
|
|
public AxisRange actionRange;
|
|
|
|
private string _displayName;
|
|
|
|
public string displayName {
|
|
get {
|
|
return _displayName;
|
|
}
|
|
set {
|
|
_displayName = value;
|
|
}
|
|
}
|
|
|
|
public override string ToString() {
|
|
StringBuilder sb = new StringBuilder();
|
|
sb.Append(typeof(ControllerElementTag).Name);
|
|
sb.Append(": ");
|
|
sb.Append("actionId = ");
|
|
sb.Append(actionId);
|
|
sb.Append(", actionRange = ");
|
|
sb.Append(actionRange);
|
|
return sb.ToString();
|
|
}
|
|
|
|
public ActionTag() : base(TagType.Action) {
|
|
Clear();
|
|
}
|
|
|
|
protected override void Clear() {
|
|
actionId = -1;
|
|
actionRange = AxisRange.Full;
|
|
_displayName = null;
|
|
}
|
|
|
|
// Static
|
|
|
|
public static bool TryParseString(string text, int startIndex, int count, StringBuilder sb1, StringBuilder sb2, Dictionary<string, string> workDictionary, Pool<ActionTag> pool, out ActionTag result) {
|
|
result = null;
|
|
if (string.IsNullOrEmpty(text) || startIndex < 0 || startIndex + count >= text.Length) return false;
|
|
|
|
ParseAttributes(text, startIndex, count, sb1, sb2, workDictionary);
|
|
if (workDictionary.Count == 0) return false;
|
|
|
|
string value;
|
|
result = pool.Get();
|
|
|
|
try {
|
|
|
|
// Action name or id
|
|
if (workDictionary.TryGetValue("id", out value) || workDictionary.TryGetValue("actionid", out value)) {
|
|
result.actionId = int.Parse(value);
|
|
if (ReInput.mapping.GetAction(result.actionId) == null) throw new Exception("Invalid Action Id: " + result.actionId);
|
|
} else if (workDictionary.TryGetValue("name", out value) || workDictionary.TryGetValue("actionname", out value)) {
|
|
var action = ReInput.mapping.GetAction(value);
|
|
if (action == null) throw new Exception("Invalid Action name: " + value);
|
|
result.actionId = action.id;
|
|
} else { // error
|
|
throw new Exception("Action name/id missing.");
|
|
}
|
|
|
|
// Action Range -- optional
|
|
if (workDictionary.TryGetValue("range", out value) || workDictionary.TryGetValue("actionrange", out value)) {
|
|
bool found = false;
|
|
for (int i = 0; i < s_axisRangeNames.Length; i++) {
|
|
if (string.Equals(value, s_axisRangeNames[i], StringComparison.OrdinalIgnoreCase)) {
|
|
result.actionRange = s_axisRangeValues[i];
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!found) throw new Exception("Invalid Action Range: " + value);
|
|
} else {
|
|
result.actionRange = AxisRange.Full;
|
|
}
|
|
|
|
return true;
|
|
|
|
} catch (Exception ex) {
|
|
UnityEngine.Debug.LogError(ex);
|
|
result.ReturnToPool();
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
private sealed class PlayerTag : Tag {
|
|
|
|
public int playerId;
|
|
|
|
private string _displayName;
|
|
|
|
public string displayName {
|
|
get {
|
|
return _displayName;
|
|
}
|
|
set {
|
|
_displayName = value;
|
|
}
|
|
}
|
|
|
|
public override string ToString() {
|
|
StringBuilder sb = new StringBuilder();
|
|
sb.Append(typeof(ControllerElementTag).Name);
|
|
sb.Append(": ");
|
|
sb.Append("playerId = ");
|
|
sb.Append(playerId);
|
|
return sb.ToString();
|
|
}
|
|
|
|
public PlayerTag() : base(TagType.Player) {
|
|
Clear();
|
|
}
|
|
|
|
protected override void Clear() {
|
|
playerId = -1;
|
|
_displayName = null;
|
|
}
|
|
|
|
// Static
|
|
|
|
public static bool TryParseString(string text, int startIndex, int count, StringBuilder sb1, StringBuilder sb2, Dictionary<string, string> workDictionary, Pool<PlayerTag> pool, out PlayerTag result) {
|
|
result = null;
|
|
if (string.IsNullOrEmpty(text) || startIndex < 0 || startIndex + count >= text.Length) return false;
|
|
|
|
ParseAttributes(text, startIndex, count, sb1, sb2, workDictionary);
|
|
if (workDictionary.Count == 0) return false;
|
|
|
|
string value;
|
|
result = pool.Get();
|
|
|
|
try {
|
|
|
|
// Action name or id
|
|
if (workDictionary.TryGetValue("id", out value) || workDictionary.TryGetValue("playerid", out value)) {
|
|
result.playerId = int.Parse(value);
|
|
if (ReInput.players.GetPlayer(result.playerId) == null) throw new Exception("Invalid Player Id: " + result.playerId);
|
|
} else if (workDictionary.TryGetValue("name", out value) || workDictionary.TryGetValue("playername", out value)) {
|
|
var player = ReInput.players.GetPlayer(value);
|
|
if (player == null) throw new Exception("Invalid Player name: " + value);
|
|
result.playerId = player.id;
|
|
} else { // error
|
|
throw new Exception("Player name/id missing.");
|
|
}
|
|
|
|
return true;
|
|
|
|
} catch (Exception ex) {
|
|
UnityEngine.Debug.LogError(ex);
|
|
result.ReturnToPool();
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
private struct GlyphOrText : IEquatable<GlyphOrText> {
|
|
|
|
public string glyphKey;
|
|
public UnityEngine.Sprite sprite;
|
|
public string name;
|
|
|
|
public override bool Equals(object obj) {
|
|
if (!(obj is GlyphOrText)) return false;
|
|
GlyphOrText item = (GlyphOrText)obj;
|
|
return string.Equals(item.glyphKey, glyphKey, StringComparison.Ordinal) &&
|
|
item.sprite == sprite &&
|
|
string.Equals(item.name, name, StringComparison.Ordinal);
|
|
}
|
|
|
|
public override int GetHashCode() {
|
|
int hash = 17;
|
|
hash = hash * 29 + glyphKey.GetHashCode();
|
|
hash = hash * 29 + sprite.GetHashCode();
|
|
hash = hash * 29 + name.GetHashCode();
|
|
return hash;
|
|
}
|
|
|
|
public bool Equals(GlyphOrText other) {
|
|
return string.Equals(other.glyphKey, glyphKey, StringComparison.Ordinal) &&
|
|
other.sprite == sprite &&
|
|
string.Equals(other.name, name, StringComparison.Ordinal);
|
|
}
|
|
|
|
public static bool operator ==(GlyphOrText a, GlyphOrText b) {
|
|
return string.Equals(a.glyphKey, b.glyphKey, StringComparison.Ordinal) &&
|
|
a.sprite == b.sprite &&
|
|
string.Equals(a.name, b.name, StringComparison.Ordinal);
|
|
}
|
|
|
|
public static bool operator !=(GlyphOrText a, GlyphOrText b) {
|
|
return !(a == b);
|
|
}
|
|
}
|
|
|
|
private class Asset {
|
|
|
|
public readonly uint id;
|
|
|
|
private ITMProSpriteAsset _spriteAsset;
|
|
private UnityEngine.Material _material;
|
|
|
|
public ITMProSpriteAsset spriteAsset { get { return _spriteAsset; } }
|
|
public UnityEngine.Material material { get { return _material; } }
|
|
|
|
public Asset(UnityEngine.Material baseMaterial) {
|
|
id = s_idCounter++;
|
|
_spriteAsset = TMProAssetVersionHelper.CreateSpriteAsset();
|
|
TMPro.TMP_SpriteAsset spriteAsset = _spriteAsset.GetSpriteAsset();
|
|
spriteAsset.name = typeof(UnityUITextMeshProGlyphHelper).Name + " SpriteAsset " + id;
|
|
spriteAsset.hashCode = TMPro.TMP_TextUtilities.GetSimpleHashCode(spriteAsset.name);
|
|
_material = CreateMaterial(baseMaterial, id);
|
|
if (_spriteAsset != null) {
|
|
spriteAsset.material = material;
|
|
spriteAsset.materialHashCode = TMPro.TMP_TextUtilities.GetSimpleHashCode(material.name);
|
|
}
|
|
}
|
|
|
|
public static UnityEngine.Material CreateMaterial(UnityEngine.Material baseMaterial, uint id) {
|
|
UnityEngine.Material material;
|
|
material = baseMaterial != null ? new UnityEngine.Material(baseMaterial) : new UnityEngine.Material(tmProShader);
|
|
material.name = typeof(UnityUITextMeshProGlyphHelper).Name + " Material " + id;
|
|
material.hideFlags = UnityEngine.HideFlags.HideInHierarchy;
|
|
return material;
|
|
}
|
|
|
|
public void Destroy() {
|
|
if (_spriteAsset != null) {
|
|
_spriteAsset.Destroy();
|
|
_spriteAsset = null;
|
|
}
|
|
if (_material != null) {
|
|
UnityEngine.Object.Destroy(_material);
|
|
_material = null;
|
|
}
|
|
}
|
|
|
|
// Static
|
|
|
|
private static uint s_idCounter;
|
|
|
|
private static UnityEngine.Shader __tmProShader;
|
|
private static UnityEngine.Shader tmProShader {
|
|
get {
|
|
if (__tmProShader == null) {
|
|
TMPro.ShaderUtilities.GetShaderPropertyIDs();
|
|
__tmProShader = UnityEngine.Shader.Find("TextMeshPro/Sprite");
|
|
}
|
|
return __tmProShader;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Options for TMPro Sprite rendering.
|
|
/// </summary>
|
|
[Serializable]
|
|
public struct TMProSpriteOptions : IEquatable<TMProSpriteOptions> {
|
|
|
|
[UnityEngine.Tooltip("Scale.")]
|
|
[UnityEngine.SerializeField]
|
|
private float _scale;
|
|
[UnityEngine.Tooltip("This value will be multiplied by the Sprite width and height and applied to offset.")]
|
|
[UnityEngine.SerializeField]
|
|
private UnityEngine.Vector2 _offsetSizeMultiplier;
|
|
[UnityEngine.Tooltip("An extra offset that is cumulative with Offset Size Multiplier.")]
|
|
[UnityEngine.SerializeField]
|
|
private UnityEngine.Vector2 _extraOffset;
|
|
[UnityEngine.Tooltip("This value will be multiplied by the Sprite width applied to X Advance.")]
|
|
[UnityEngine.SerializeField]
|
|
private float _xAdvanceWidthMultiplier;
|
|
[UnityEngine.Tooltip("An extra offset that is cumulative with X Advance Width Multiplier.")]
|
|
[UnityEngine.SerializeField]
|
|
private float _extraXAdvance;
|
|
|
|
/// <summary>
|
|
/// Sprites will be scaled by this value.
|
|
/// </summary>
|
|
public float scale {
|
|
get {
|
|
return _scale;
|
|
}
|
|
set {
|
|
_scale = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// This value will be multiplied by the Sprite width and height and applied to offset.
|
|
/// </summary>
|
|
public UnityEngine.Vector2 offsetSizeMultiplier {
|
|
get {
|
|
return _offsetSizeMultiplier;
|
|
}
|
|
set {
|
|
_offsetSizeMultiplier = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// An extra offset that is cumulative with <see cref="offsetSizeMultiplier"/>.
|
|
/// </summary>
|
|
public UnityEngine.Vector2 extraOffset {
|
|
get {
|
|
return _extraOffset;
|
|
}
|
|
set {
|
|
_extraOffset = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// This value will be multiplied by the Sprite width applied to X Advance.
|
|
/// </summary>
|
|
public float xAdvanceWidthMultiplier {
|
|
get {
|
|
return _xAdvanceWidthMultiplier;
|
|
}
|
|
set {
|
|
_xAdvanceWidthMultiplier = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// An extra offset that is cumulative with <see cref="xAdvanceWidthMultiplier"/>.
|
|
/// </summary>
|
|
public float extraXAdvance {
|
|
get {
|
|
return _extraXAdvance;
|
|
}
|
|
set {
|
|
_extraXAdvance = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates an instance with the default values.
|
|
/// </summary>
|
|
/// <returns>An instance with the default values.</returns>
|
|
public static TMProSpriteOptions Default {
|
|
get {
|
|
TMProSpriteOptions o = new TMProSpriteOptions();
|
|
o.scale = 1.5f;
|
|
o.extraOffset = new UnityEngine.Vector2();
|
|
o.offsetSizeMultiplier = new UnityEngine.Vector2(0f, 0.75f);
|
|
o.xAdvanceWidthMultiplier = 1f;
|
|
return o;
|
|
}
|
|
}
|
|
|
|
public override bool Equals(object obj) {
|
|
if (!(obj is TMProSpriteOptions)) return false;
|
|
TMProSpriteOptions item = (TMProSpriteOptions)obj;
|
|
return item._scale == _scale &&
|
|
item._offsetSizeMultiplier == _offsetSizeMultiplier &&
|
|
item._extraOffset == _extraOffset &&
|
|
item._xAdvanceWidthMultiplier == _xAdvanceWidthMultiplier &&
|
|
item._extraXAdvance == _extraXAdvance;
|
|
}
|
|
|
|
public override int GetHashCode() {
|
|
int hash = 17;
|
|
hash = hash * 29 + _scale.GetHashCode();
|
|
hash = hash * 29 + _offsetSizeMultiplier.GetHashCode();
|
|
hash = hash * 29 + _extraOffset.GetHashCode();
|
|
hash = hash * 29 + _xAdvanceWidthMultiplier.GetHashCode();
|
|
hash = hash * 29 + _extraXAdvance.GetHashCode();
|
|
return hash;
|
|
}
|
|
|
|
public bool Equals(TMProSpriteOptions other) {
|
|
return other._scale == _scale &&
|
|
other._offsetSizeMultiplier == _offsetSizeMultiplier &&
|
|
other._extraOffset == _extraOffset &&
|
|
other._xAdvanceWidthMultiplier == _xAdvanceWidthMultiplier &&
|
|
other._extraXAdvance == _extraXAdvance;
|
|
}
|
|
|
|
public static bool operator ==(TMProSpriteOptions a, TMProSpriteOptions b) {
|
|
return a._scale == b._scale &&
|
|
a._offsetSizeMultiplier == b._offsetSizeMultiplier &&
|
|
a._extraOffset == b._extraOffset &&
|
|
a._xAdvanceWidthMultiplier == b._xAdvanceWidthMultiplier &&
|
|
a._extraXAdvance == b._extraXAdvance;
|
|
}
|
|
|
|
public static bool operator !=(TMProSpriteOptions a, TMProSpriteOptions b) {
|
|
return !(a == b);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sprite material properties.
|
|
/// </summary>
|
|
[Serializable]
|
|
public struct SpriteMaterialProperties {
|
|
|
|
[UnityEngine.Tooltip("Sprite material color.")]
|
|
[UnityEngine.SerializeField]
|
|
private UnityEngine.Color _color;
|
|
public UnityEngine.Color color { get { return _color; } set { _color = value; } }
|
|
|
|
public static SpriteMaterialProperties Default {
|
|
get {
|
|
SpriteMaterialProperties o = new SpriteMaterialProperties();
|
|
o._color = UnityEngine.Color.white;
|
|
return o;
|
|
}
|
|
}
|
|
}
|
|
|
|
#region Text Mesh Pro Asset Version Support
|
|
|
|
// These wrapper classes are required to support both Unity UI 1.0 and
|
|
// Unity UI 2.0, or mores specifically, versions of Text Mesh Pro pre
|
|
// and post asset v1.1.0 breaking structural changes were made.
|
|
|
|
private interface ITMProSprite {
|
|
uint id { get; set; }
|
|
float width { get; set; }
|
|
float height { get; set; }
|
|
float xOffset { get; set; }
|
|
float yOffset { get; set; }
|
|
float xAdvance { get; set; }
|
|
UnityEngine.Vector2 position { get; set; }
|
|
UnityEngine.Vector2 pivot { get; set; }
|
|
float scale { get; set; }
|
|
string name { get; set; }
|
|
uint unicode { get; set; }
|
|
int hashCode { get; set; }
|
|
UnityEngine.Sprite sprite { get; set; }
|
|
}
|
|
|
|
private interface ITMProSpriteAsset {
|
|
int spriteCount { get; }
|
|
UnityEngine.Texture spriteSheet { get; set; }
|
|
TMPro.TMP_SpriteAsset GetSpriteAsset();
|
|
ITMProSprite GetSprite(int index);
|
|
void AddSprite(ITMProSprite sprite);
|
|
bool Contains(string spriteName);
|
|
void Clear();
|
|
void UpdateLookupTables();
|
|
void Destroy();
|
|
}
|
|
|
|
private static class TMProAssetVersionHelper {
|
|
|
|
#if MAY_SUPPORT_TMPRO_ASSET_V_1_1_0
|
|
private static bool _isVersionSupportedChecked;
|
|
|
|
private static bool CheckVersionSupported() {
|
|
bool isVersionSupported = TMProSprite_AssetV1_1_0.CheckVersionSupported();
|
|
if (_isVersionSupportedChecked) return isVersionSupported;
|
|
if (!isVersionSupported) {
|
|
#if UNITY_EDITOR
|
|
//UnityEngine.Debug.LogWarning(typeof(UnityUITextMeshProGlyphHelper).Name + ": Unity UI 2.0 is not supported.");
|
|
#endif
|
|
}
|
|
_isVersionSupportedChecked = true;
|
|
return isVersionSupported;
|
|
}
|
|
#endif
|
|
|
|
public static ITMProSprite CreateSprite() {
|
|
#if MAY_SUPPORT_TMPRO_ASSET_V_1_1_0
|
|
return CheckVersionSupported() ? (ITMProSprite)new TMProSprite_AssetV1_1_0() : (ITMProSprite)new TMProSprite_AssetV1_0_0();
|
|
#else
|
|
return new TMProSprite_AssetV1_0_0();
|
|
#endif
|
|
}
|
|
|
|
public static ITMProSpriteAsset CreateSpriteAsset() {
|
|
#if MAY_SUPPORT_TMPRO_ASSET_V_1_1_0
|
|
return CheckVersionSupported() ? (ITMProSpriteAsset)new TMProSprite_AssetV1_1_0.TMPro_SpriteAsset() : (ITMProSpriteAsset)new TMProSprite_AssetV1_0_0.TMPro_SpriteAsset();
|
|
#else
|
|
return new TMProSprite_AssetV1_0_0.TMPro_SpriteAsset();
|
|
#endif
|
|
}
|
|
}
|
|
|
|
private class TMProSprite_AssetV1_0_0 : ITMProSprite {
|
|
|
|
public TMPro.TMP_Sprite spriteInfo;
|
|
|
|
public TMProSprite_AssetV1_0_0() {
|
|
spriteInfo = new TMPro.TMP_Sprite();
|
|
}
|
|
|
|
public uint id {
|
|
get {
|
|
return (uint)spriteInfo.id;
|
|
}
|
|
set {
|
|
spriteInfo.id = (int)value;
|
|
}
|
|
}
|
|
public float width {
|
|
get {
|
|
return spriteInfo.width;
|
|
}
|
|
set {
|
|
spriteInfo.width = value;
|
|
}
|
|
}
|
|
|
|
public float height {
|
|
get {
|
|
return spriteInfo.height;
|
|
}
|
|
set {
|
|
spriteInfo.height = value;
|
|
}
|
|
}
|
|
|
|
public float xOffset {
|
|
get {
|
|
return spriteInfo.xOffset;
|
|
}
|
|
set {
|
|
spriteInfo.xOffset = value;
|
|
}
|
|
}
|
|
|
|
public float yOffset {
|
|
get {
|
|
return spriteInfo.yOffset;
|
|
}
|
|
set {
|
|
spriteInfo.yOffset = value;
|
|
}
|
|
}
|
|
|
|
public float xAdvance {
|
|
get {
|
|
return spriteInfo.xAdvance;
|
|
}
|
|
set {
|
|
spriteInfo.xAdvance = value;
|
|
}
|
|
}
|
|
|
|
public UnityEngine.Vector2 position {
|
|
get {
|
|
return new UnityEngine.Vector2(spriteInfo.x, spriteInfo.y);
|
|
}
|
|
set {
|
|
spriteInfo.x = value.x;
|
|
spriteInfo.y = value.y;
|
|
}
|
|
}
|
|
|
|
public UnityEngine.Vector2 pivot {
|
|
get {
|
|
return spriteInfo.pivot;
|
|
}
|
|
set {
|
|
spriteInfo.pivot = value;
|
|
}
|
|
}
|
|
|
|
public float scale {
|
|
get {
|
|
return spriteInfo.scale;
|
|
}
|
|
set {
|
|
spriteInfo.scale = value;
|
|
}
|
|
}
|
|
|
|
public string name {
|
|
get {
|
|
return spriteInfo.name;
|
|
}
|
|
set {
|
|
spriteInfo.name = value;
|
|
}
|
|
}
|
|
|
|
public uint unicode {
|
|
get {
|
|
return (uint)spriteInfo.unicode;
|
|
}
|
|
set {
|
|
spriteInfo.unicode = (int)value;
|
|
}
|
|
}
|
|
|
|
public int hashCode {
|
|
get {
|
|
return spriteInfo.hashCode;
|
|
}
|
|
set {
|
|
spriteInfo.hashCode = value;
|
|
}
|
|
}
|
|
|
|
public UnityEngine.Sprite sprite {
|
|
get {
|
|
return spriteInfo.sprite;
|
|
}
|
|
set {
|
|
spriteInfo.sprite = value;
|
|
}
|
|
}
|
|
|
|
public class TMPro_SpriteAsset : ITMProSpriteAsset {
|
|
|
|
private TMPro.TMP_SpriteAsset _spriteAsset;
|
|
private readonly List<TMProSprite_AssetV1_0_0> _sprites;
|
|
|
|
public int spriteCount { get { return _sprites.Count; } }
|
|
|
|
public UnityEngine.Texture spriteSheet {
|
|
get {
|
|
return _spriteAsset.spriteSheet;
|
|
}
|
|
set {
|
|
_spriteAsset.spriteSheet = value;
|
|
}
|
|
}
|
|
|
|
public TMPro_SpriteAsset() {
|
|
_spriteAsset = UnityEngine.ScriptableObject.CreateInstance<TMPro.TMP_SpriteAsset>();
|
|
_spriteAsset.hideFlags = UnityEngine.HideFlags.DontSave;
|
|
if (_spriteAsset.spriteInfoList == null) _spriteAsset.spriteInfoList = new List<TMPro.TMP_Sprite>();
|
|
_sprites = new List<TMProSprite_AssetV1_0_0>();
|
|
}
|
|
|
|
public TMPro.TMP_SpriteAsset GetSpriteAsset() {
|
|
return _spriteAsset;
|
|
}
|
|
|
|
public ITMProSprite GetSprite(int index) {
|
|
if ((uint)index >= (uint)_sprites.Count) return null;
|
|
return _sprites[index];
|
|
}
|
|
|
|
public void AddSprite(ITMProSprite sprite) {
|
|
TMProSprite_AssetV1_0_0 tSprite = sprite as TMProSprite_AssetV1_0_0;
|
|
if (sprite == null) throw new ArgumentException();
|
|
tSprite.spriteInfo.id = _spriteAsset.spriteInfoList.Count;
|
|
_spriteAsset.spriteInfoList.Add(tSprite.spriteInfo);
|
|
_sprites.Add(tSprite);
|
|
}
|
|
|
|
public void Clear() {
|
|
_spriteAsset.spriteInfoList.Clear();
|
|
_sprites.Clear();
|
|
}
|
|
|
|
public bool Contains(string spriteName) {
|
|
int count = _sprites.Count;
|
|
for (int i = 0; i < count; i++) {
|
|
if (string.Equals(_sprites[i].name, spriteName, StringComparison.Ordinal)) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public void UpdateLookupTables() {
|
|
_spriteAsset.UpdateLookupTables();
|
|
}
|
|
|
|
public void Destroy() {
|
|
if (_spriteAsset == null) return;
|
|
UnityEngine.Object.Destroy(_spriteAsset);
|
|
_spriteAsset = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
#if MAY_SUPPORT_TMPRO_ASSET_V_1_1_0
|
|
|
|
private class TMProSprite_AssetV1_1_0 : ITMProSprite {
|
|
|
|
private readonly TMPro_SpriteGlyph _spriteGlyph;
|
|
private readonly TMPro_SpriteCharacter _spriteCharacter;
|
|
|
|
public TMProSprite_AssetV1_1_0() {
|
|
_spriteGlyph = new TMPro_SpriteGlyph();
|
|
_spriteCharacter = new TMPro_SpriteCharacter();
|
|
_spriteCharacter.glyph = _spriteGlyph.source;
|
|
}
|
|
|
|
public TMPro_SpriteGlyph spriteGlyph {
|
|
get {
|
|
return _spriteGlyph;
|
|
}
|
|
}
|
|
|
|
public TMPro_SpriteCharacter spriteCharacter {
|
|
get {
|
|
return _spriteCharacter;
|
|
}
|
|
}
|
|
|
|
public uint id {
|
|
get {
|
|
return _spriteGlyph.source.index;
|
|
}
|
|
set {
|
|
_spriteGlyph.source.index = value;
|
|
_spriteCharacter.glyphIndex = value;
|
|
}
|
|
}
|
|
|
|
public float width {
|
|
get {
|
|
return _spriteGlyph.source.metrics.width;
|
|
}
|
|
set {
|
|
UnityEngine.TextCore.GlyphMetrics m = _spriteGlyph.source.metrics;
|
|
m.width = value;
|
|
_spriteGlyph.source.metrics = m;
|
|
UnityEngine.TextCore.GlyphRect r = _spriteGlyph.source.glyphRect;
|
|
r.width = (int)value;
|
|
_spriteGlyph.source.glyphRect = r;
|
|
}
|
|
}
|
|
|
|
public float height {
|
|
get {
|
|
return _spriteGlyph.source.metrics.height;
|
|
}
|
|
set {
|
|
UnityEngine.TextCore.GlyphMetrics m = _spriteGlyph.source.metrics;
|
|
m.height = value;
|
|
_spriteGlyph.source.metrics = m;
|
|
UnityEngine.TextCore.GlyphRect r = _spriteGlyph.source.glyphRect;
|
|
r.height = (int)value;
|
|
_spriteGlyph.source.glyphRect = r;
|
|
}
|
|
}
|
|
|
|
public float xOffset {
|
|
get {
|
|
return _spriteGlyph.source.metrics.horizontalBearingX;
|
|
}
|
|
set {
|
|
UnityEngine.TextCore.GlyphMetrics m = _spriteGlyph.source.metrics;
|
|
m.horizontalBearingX = value;
|
|
_spriteGlyph.source.metrics = m;
|
|
}
|
|
}
|
|
|
|
public float yOffset {
|
|
get {
|
|
return _spriteGlyph.source.metrics.horizontalBearingY;
|
|
}
|
|
set {
|
|
UnityEngine.TextCore.GlyphMetrics m = _spriteGlyph.source.metrics;
|
|
m.horizontalBearingY = value;
|
|
_spriteGlyph.source.metrics = m;
|
|
}
|
|
}
|
|
|
|
public float xAdvance {
|
|
get {
|
|
return _spriteGlyph.source.metrics.horizontalAdvance;
|
|
}
|
|
set {
|
|
UnityEngine.TextCore.GlyphMetrics m = _spriteGlyph.source.metrics;
|
|
m.horizontalAdvance = value;
|
|
_spriteGlyph.source.metrics = m;
|
|
}
|
|
}
|
|
|
|
public UnityEngine.Vector2 position {
|
|
get {
|
|
var rect = _spriteGlyph.source.glyphRect;
|
|
return new UnityEngine.Vector2(rect.x, rect.y);
|
|
}
|
|
set {
|
|
UnityEngine.TextCore.GlyphRect r = _spriteGlyph.source.glyphRect;
|
|
r.x = (int)value.x;
|
|
r.y = (int)value.y;
|
|
_spriteGlyph.source.glyphRect = r;
|
|
}
|
|
}
|
|
|
|
public UnityEngine.Vector2 pivot {
|
|
get {
|
|
return new UnityEngine.Vector2();
|
|
}
|
|
set {
|
|
}
|
|
}
|
|
|
|
public float scale {
|
|
get {
|
|
return _spriteCharacter.scale;
|
|
}
|
|
set {
|
|
_spriteCharacter.scale = value;
|
|
}
|
|
}
|
|
|
|
public string name {
|
|
get {
|
|
return _spriteCharacter.name;
|
|
}
|
|
set {
|
|
_spriteCharacter.name = value;
|
|
}
|
|
}
|
|
|
|
public uint unicode {
|
|
get {
|
|
return _spriteCharacter.unicode;
|
|
}
|
|
set {
|
|
_spriteCharacter.unicode = value;
|
|
}
|
|
}
|
|
|
|
public int hashCode {
|
|
get {
|
|
return 0;
|
|
}
|
|
set {
|
|
}
|
|
}
|
|
|
|
public UnityEngine.Sprite sprite {
|
|
get {
|
|
return _spriteGlyph.sprite;
|
|
}
|
|
set {
|
|
_spriteGlyph.sprite = value;
|
|
}
|
|
}
|
|
|
|
// Static
|
|
|
|
private static bool? s_isVersionSupported;
|
|
|
|
public static bool CheckVersionSupported() {
|
|
if (s_isVersionSupported != null) return s_isVersionSupported.Value;
|
|
try {
|
|
#if !SUPPORTS_AT_LEAST_TMPRO_ASSET_V_1_1_0
|
|
var a = new TMPro_SpriteCharacter();
|
|
var b = new TMPro_SpriteGlyph();
|
|
#endif
|
|
var c = new TMPro_SpriteAsset();
|
|
s_isVersionSupported = true;
|
|
} catch {
|
|
s_isVersionSupported = false;
|
|
}
|
|
return s_isVersionSupported.Value;
|
|
}
|
|
|
|
// Classes
|
|
|
|
public class TMPro_SpriteCharacter {
|
|
|
|
#if SUPPORTS_AT_LEAST_TMPRO_ASSET_V_1_1_0
|
|
|
|
private readonly TMPro.TMP_SpriteCharacter _source;
|
|
|
|
public TMPro.TMP_SpriteCharacter source {
|
|
get {
|
|
return _source;
|
|
}
|
|
}
|
|
|
|
public UnityEngine.TextCore.Glyph glyph {
|
|
get {
|
|
return _source.glyph;
|
|
}
|
|
set {
|
|
_source.glyph = value;
|
|
}
|
|
}
|
|
|
|
public uint unicode {
|
|
get {
|
|
return _source.unicode;
|
|
}
|
|
set {
|
|
if (value == 0x0) value = 0xFFFE;
|
|
_source.unicode = value;
|
|
}
|
|
}
|
|
|
|
public string name {
|
|
get {
|
|
return _source.name;
|
|
}
|
|
set {
|
|
_source.name = value;
|
|
}
|
|
}
|
|
|
|
public float scale {
|
|
get {
|
|
return _source.scale;
|
|
}
|
|
set {
|
|
_source.scale = value;
|
|
}
|
|
}
|
|
|
|
public uint glyphIndex {
|
|
get {
|
|
return _source.glyphIndex;
|
|
}
|
|
set {
|
|
_source.glyphIndex = value;
|
|
}
|
|
}
|
|
|
|
public TMPro_SpriteCharacter() {
|
|
_source = new TMPro.TMP_SpriteCharacter();
|
|
}
|
|
|
|
#else
|
|
|
|
private const string typeFullName = "TMPro.TMP_SpriteCharacter";
|
|
|
|
private readonly object _source;
|
|
private readonly PropertyInfo _glyph;
|
|
private readonly PropertyInfo _unicode;
|
|
private readonly PropertyInfo _name;
|
|
private readonly PropertyInfo _scale;
|
|
private readonly PropertyInfo _glyphIndex;
|
|
|
|
public object source {
|
|
get {
|
|
return _source;
|
|
}
|
|
}
|
|
|
|
public UnityEngine.TextCore.Glyph glyph {
|
|
get {
|
|
return (UnityEngine.TextCore.Glyph)_glyph.GetValue(_source);
|
|
}
|
|
set {
|
|
_glyph.SetValue(_source, value);
|
|
}
|
|
}
|
|
|
|
public uint unicode {
|
|
get {
|
|
return (uint)_unicode.GetValue(_source);
|
|
}
|
|
set {
|
|
if (value == 0x0) value = 0xFFFE;
|
|
_unicode.SetValue(_source, value);
|
|
}
|
|
}
|
|
|
|
public string name {
|
|
get {
|
|
return (string)_name.GetValue(_source);
|
|
}
|
|
set {
|
|
_name.SetValue(_source, value);
|
|
}
|
|
}
|
|
|
|
public float scale {
|
|
get {
|
|
return (float)_scale.GetValue(_source);
|
|
}
|
|
set {
|
|
_scale.SetValue(_source, value);
|
|
}
|
|
}
|
|
|
|
public uint glyphIndex {
|
|
get {
|
|
return (uint)_glyphIndex.GetValue(_source);
|
|
}
|
|
set {
|
|
_glyphIndex.SetValue(_source, value);
|
|
}
|
|
}
|
|
|
|
public TMPro_SpriteCharacter() {
|
|
System.Type type = GetReflectedType();
|
|
if (type == null) throw new ArgumentNullException("type");
|
|
_source = System.Activator.CreateInstance(type);
|
|
if (_source == null) throw new ArgumentNullException("source");
|
|
_glyph = type.GetProperty("glyph", BindingFlags.Public | BindingFlags.Instance);
|
|
if (_glyph == null) throw new ArgumentNullException("glyph");
|
|
_unicode = type.GetProperty("unicode", BindingFlags.Public | BindingFlags.Instance);
|
|
if (_unicode == null) throw new ArgumentNullException("unicode");
|
|
_name = type.GetProperty("name", BindingFlags.Public | BindingFlags.Instance);
|
|
if (_name == null) throw new ArgumentNullException("name");
|
|
_scale = type.GetProperty("scale", BindingFlags.Public | BindingFlags.Instance);
|
|
if (_scale == null) throw new ArgumentNullException("scale");
|
|
_glyphIndex = type.GetProperty("glyphIndex", BindingFlags.Public | BindingFlags.Instance);
|
|
if (_glyphIndex == null) throw new ArgumentNullException("glyphIndex");
|
|
}
|
|
|
|
// Static
|
|
|
|
private static System.Type s_type;
|
|
|
|
private static System.Type GetReflectedType() {
|
|
if (s_type != null) return s_type;
|
|
System.Type[] types = typeof(TMPro.TMP_SpriteAsset).Assembly.GetTypes();
|
|
if (types == null) return null;
|
|
for(int i = 0; i < types.Length; i++) {
|
|
if (string.Equals(types[i].FullName, typeFullName)) {
|
|
s_type = types[i];
|
|
break;
|
|
}
|
|
}
|
|
return s_type;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
public class TMPro_SpriteGlyph {
|
|
|
|
#if SUPPORTS_AT_LEAST_TMPRO_ASSET_V_1_1_0
|
|
|
|
private readonly TMPro.TMP_SpriteGlyph _source;
|
|
|
|
public TMPro.TMP_SpriteGlyph source {
|
|
get {
|
|
return _source;
|
|
}
|
|
}
|
|
|
|
public UnityEngine.Sprite sprite {
|
|
get {
|
|
return _source.sprite;
|
|
}
|
|
set {
|
|
_source.sprite = value;
|
|
}
|
|
}
|
|
|
|
public TMPro_SpriteGlyph() {
|
|
_source = new TMPro.TMP_SpriteGlyph();
|
|
Initialize(_source);
|
|
}
|
|
|
|
#else
|
|
|
|
private const string typeFullName = "TMPro.TMP_SpriteGlyph";
|
|
|
|
private readonly UnityEngine.TextCore.Glyph _source;
|
|
|
|
private readonly FieldInfo _sprite;
|
|
|
|
public UnityEngine.TextCore.Glyph source {
|
|
get {
|
|
return _source;
|
|
}
|
|
}
|
|
|
|
public UnityEngine.Sprite sprite {
|
|
get {
|
|
return (UnityEngine.Sprite)_sprite.GetValue(_source);
|
|
}
|
|
set {
|
|
_sprite.SetValue(_source, value);
|
|
}
|
|
}
|
|
|
|
public TMPro_SpriteGlyph() {
|
|
System.Type type = GetReflectedType();
|
|
if (type == null) throw new ArgumentNullException("type");
|
|
_source = (UnityEngine.TextCore.Glyph)System.Activator.CreateInstance(type);
|
|
if (_source == null) throw new ArgumentNullException("glyph");
|
|
_sprite = type.GetField("sprite", BindingFlags.Public | BindingFlags.Instance);
|
|
if (_sprite == null) throw new ArgumentNullException("sprite");
|
|
Initialize(_source);
|
|
}
|
|
|
|
// Static
|
|
|
|
private static System.Type s_type;
|
|
|
|
private static System.Type GetReflectedType() {
|
|
if (s_type != null) return s_type;
|
|
System.Type[] types = typeof(TMPro.TMP_SpriteAsset).Assembly.GetTypes();
|
|
if (types == null) return null;
|
|
for (int i = 0; i < types.Length; i++) {
|
|
if (string.Equals(types[i].FullName, typeFullName)) {
|
|
s_type = types[i];
|
|
break;
|
|
}
|
|
}
|
|
return s_type;
|
|
}
|
|
|
|
#endif
|
|
private static void Initialize(UnityEngine.TextCore.Glyph glyph) {
|
|
glyph.scale = 1.0f;
|
|
glyph.atlasIndex = 0;
|
|
}
|
|
}
|
|
|
|
public class TMPro_SpriteAsset : ITMProSpriteAsset {
|
|
|
|
// Cannot avoid using reflection in this class because TMPro.TMP_SpriteAsset
|
|
// has internal setters for the tables and version in Unity UI 2.0. This
|
|
// effectively prevents scripts from modfying the Sprite Asset tables at runtime.
|
|
// This needs be changed back in a future version of Unity UI / TMPro.
|
|
|
|
private readonly PropertyInfo _spriteCharacterTable;
|
|
private readonly PropertyInfo _spriteGlyphTable;
|
|
private readonly System.Collections.IList _spriteCharacterTableList;
|
|
private readonly System.Collections.IList _spriteGlyphTableList;
|
|
private readonly List<TMProSprite_AssetV1_1_0> _sprites;
|
|
|
|
private TMPro.TMP_SpriteAsset _spriteAsset;
|
|
|
|
public int spriteCount { get { return _sprites.Count; } }
|
|
|
|
public UnityEngine.Texture spriteSheet {
|
|
get {
|
|
return _spriteAsset.spriteSheet;
|
|
}
|
|
set {
|
|
_spriteAsset.spriteSheet = value;
|
|
}
|
|
}
|
|
|
|
public TMPro_SpriteAsset() {
|
|
_spriteAsset = UnityEngine.ScriptableObject.CreateInstance<TMPro.TMP_SpriteAsset>();
|
|
_spriteAsset.hideFlags = UnityEngine.HideFlags.DontSave;
|
|
|
|
Type type = typeof(TMPro.TMP_SpriteAsset);
|
|
if (type == null) throw new ArgumentNullException("type");
|
|
|
|
// Set version to prevent TMPro asset version upgrade logging
|
|
PropertyInfo version = type.GetProperty("version", BindingFlags.Public | BindingFlags.Instance);
|
|
if (version == null) throw new ArgumentNullException("version");
|
|
version.SetValue(_spriteAsset, "1.1.0");
|
|
|
|
_spriteCharacterTable = type.GetProperty("spriteCharacterTable", BindingFlags.Public | BindingFlags.Instance);
|
|
if (_spriteCharacterTable == null) throw new ArgumentNullException("spriteCharacterTable");
|
|
_spriteCharacterTableList = (System.Collections.IList)_spriteCharacterTable.GetValue(_spriteAsset);
|
|
if (_spriteCharacterTableList == null) throw new ArgumentNullException("spriteCharacterTableList");
|
|
_spriteGlyphTable = type.GetProperty("spriteGlyphTable", BindingFlags.Public | BindingFlags.Instance);
|
|
if (_spriteGlyphTable == null) throw new ArgumentNullException("spriteGlyphTable");
|
|
_spriteGlyphTableList = (System.Collections.IList)_spriteGlyphTable.GetValue(_spriteAsset);
|
|
if (_spriteGlyphTableList == null) throw new ArgumentNullException("spriteGlyphTableList");
|
|
_sprites = new List<TMProSprite_AssetV1_1_0>();
|
|
}
|
|
|
|
public TMPro.TMP_SpriteAsset GetSpriteAsset() {
|
|
return _spriteAsset;
|
|
}
|
|
|
|
public ITMProSprite GetSprite(int index) {
|
|
if ((uint)index >= (uint)_sprites.Count) return null;
|
|
return _sprites[index];
|
|
}
|
|
|
|
public void AddSprite(ITMProSprite sprite) {
|
|
TMProSprite_AssetV1_1_0 tSprite = sprite as TMProSprite_AssetV1_1_0;
|
|
if (tSprite == null) throw new ArgumentException();
|
|
tSprite.id = (uint)_spriteCharacterTableList.Count;
|
|
_spriteCharacterTableList.Add(tSprite.spriteCharacter.source);
|
|
_spriteGlyphTableList.Add(tSprite.spriteGlyph.source);
|
|
_sprites.Add(tSprite);
|
|
}
|
|
|
|
public void Clear() {
|
|
_spriteCharacterTableList.Clear();
|
|
_spriteGlyphTableList.Clear();
|
|
_sprites.Clear();
|
|
}
|
|
|
|
public bool Contains(string spriteName) {
|
|
int count = _sprites.Count;
|
|
for (int i = 0; i < count; i++) {
|
|
if (string.Equals(_sprites[i].name, spriteName, StringComparison.Ordinal)) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public void UpdateLookupTables() {
|
|
_spriteAsset.UpdateLookupTables();
|
|
}
|
|
|
|
public void Destroy() {
|
|
if (_spriteAsset == null) return;
|
|
UnityEngine.Object.Destroy(_spriteAsset);
|
|
_spriteAsset = null;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#region Enums
|
|
|
|
private enum DisplayType {
|
|
Glyph,
|
|
Text,
|
|
GlyphOrText
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|
|
|
|
#endif
|