using System; using ConformalDecals.Text; using ConformalDecals.Util; using TMPro; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; namespace ConformalDecals.UI { public class TextEntryController : MonoBehaviour { [Serializable] public delegate void TextUpdateDelegate(string newText, DecalFont newFont, FontStyles style, bool vertical, float linespacing, float charspacing); [SerializeField] private Selectable _textBox; [SerializeField] private Button _fontButton; [SerializeField] private Slider _lineSpacingSlider; [SerializeField] private Selectable _lineSpacingTextBox; [SerializeField] private Slider _charSpacingSlider; [SerializeField] private Selectable _charSpacingTextBox; [SerializeField] private Toggle _boldButton; [SerializeField] private Toggle _italicButton; [SerializeField] private Toggle _underlineButton; [SerializeField] private Toggle _smallCapsButton; [SerializeField] private Toggle _verticalButton; private string _text; private DecalFont _font; private FontStyles _style; private bool _vertical; private float _lineSpacing; private float _charSpacing; private Vector2 _lineSpacingRange; private Vector2 _charSpacingRange; private TMP_InputField _textBoxTMP; private TextUpdateDelegate _onValueChanged; private FontMenuController _fontMenu; private bool _ignoreUpdates; public static TextEntryController Create( string text, DecalFont font, FontStyles style, bool vertical, float linespacing, float charspacing, Vector2 lineSpacingRange, Vector2 charSpacingRange, TextUpdateDelegate textUpdateCallback) { var window = Instantiate(UILoader.TextEntryPrefab, MainCanvasUtil.MainCanvas.transform, true); window.AddComponent(); MenuNavigation.SpawnMenuNavigation(window, Navigation.Mode.Automatic, true); var controller = window.GetComponent(); controller._text = text; controller._font = font; controller._style = style; controller._vertical = vertical; controller._lineSpacing = linespacing; controller._charSpacing = charspacing; controller._lineSpacingRange = lineSpacingRange; controller._charSpacingRange = charSpacingRange; controller._onValueChanged = textUpdateCallback; return controller; } public void Close() { if (_fontMenu != null) _fontMenu.Close(); Destroy(gameObject); } public void OnTextUpdate(string newText) { this._text = newText; OnValueChanged(); } public void OnFontMenu() { if (_fontMenu == null) _fontMenu = FontMenuController.Create(DecalConfig.Fonts, _font, OnFontUpdate); } public void OnFontUpdate(DecalFont font) { if (_ignoreUpdates) return; _font = font; font.SetupSample(_fontButton.GetComponentInChildren()); _textBoxTMP.text = _text; _textBoxTMP.textComponent.fontStyle = _style | _font.FontStyle & ~_font.FontStyleMask; _textBoxTMP.fontAsset = _font.FontAsset; UpdateStyleButtons(); OnValueChanged(); } public void OnLineSpacingUpdate(float value) { if (_ignoreUpdates) return; _lineSpacing = Mathf.Lerp(_lineSpacingRange.x, _lineSpacingRange.y, value); UpdateLineSpacing(); OnValueChanged(); } public void OnLineSpacingUpdate(string text) { if (_ignoreUpdates) return; if (float.TryParse(text, out var value)) { _lineSpacing = Mathf.Clamp(value, _lineSpacingRange.x, _lineSpacingRange.y); } else { Logging.LogWarning("Line spacing value '{text}' could not be parsed."); } UpdateLineSpacing(); OnValueChanged(); } public void OnCharSpacingUpdate(float value) { if (_ignoreUpdates) return; _charSpacing = Mathf.Lerp(_charSpacingRange.x, _charSpacingRange.y, value); UpdateCharSpacing(); OnValueChanged(); } public void OnCharSpacingUpdate(string text) { if (_ignoreUpdates) return; if (float.TryParse(text, out var value)) { _charSpacing = Mathf.Clamp(value, _charSpacingRange.x, _charSpacingRange.y); } else { Logging.LogWarning("Char spacing value '{text}' could not be parsed."); } UpdateCharSpacing(); OnValueChanged(); } public void OnBoldUpdate(bool state) { if (_ignoreUpdates) return; if (state) _style |= FontStyles.Bold; else _style &= ~FontStyles.Bold; _textBoxTMP.textComponent.fontStyle = _style | _font.FontStyle & ~_font.FontStyleMask; OnValueChanged(); } public void OnItalicUpdate(bool state) { if (_ignoreUpdates) return; if (state) _style |= FontStyles.Italic; else _style &= ~FontStyles.Italic; _textBoxTMP.textComponent.fontStyle = _style | _font.FontStyle & ~_font.FontStyleMask; OnValueChanged(); } public void OnUnderlineUpdate(bool state) { if (_ignoreUpdates) return; if (state) _style |= FontStyles.Underline; else _style &= ~FontStyles.Underline; _textBoxTMP.textComponent.fontStyle = _style | _font.FontStyle & ~_font.FontStyleMask; OnValueChanged(); } public void OnSmallCapsUpdate(bool state) { if (_ignoreUpdates) return; if (state) _style |= FontStyles.SmallCaps; else _style &= ~FontStyles.SmallCaps; _textBoxTMP.textComponent.fontStyle = _style | _font.FontStyle & ~_font.FontStyleMask; OnValueChanged(); } public void OnVerticalUpdate(bool state) { if (_ignoreUpdates) return; _vertical = state; OnValueChanged(); } private void Start() { _textBoxTMP = ((TMP_InputField) _textBox); _textBoxTMP.text = _text; _textBoxTMP.textComponent.fontStyle = _style | _font.FontStyle & ~_font.FontStyleMask; _textBoxTMP.fontAsset = _font.FontAsset; _font.SetupSample(_fontButton.GetComponentInChildren()); UpdateStyleButtons(); UpdateLineSpacing(); UpdateCharSpacing(); } private void OnValueChanged() { _onValueChanged(_text, _font, _style, _vertical, _lineSpacing, _charSpacing); } private void UpdateStyleButtons() { _ignoreUpdates = true; if (_font.Bold) { _boldButton.interactable = false; _boldButton.isOn = true; } else if (_font.BoldMask) { _boldButton.interactable = false; _boldButton.isOn = false; } else { _boldButton.interactable = true; _boldButton.isOn = (_style & FontStyles.Bold) != 0; } if (_font.Italic) { _italicButton.interactable = false; _italicButton.isOn = true; } else if (_font.ItalicMask) { _italicButton.interactable = false; _italicButton.isOn = false; } else { _italicButton.interactable = true; _italicButton.isOn = (_style & FontStyles.Italic) != 0; } if (_font.Underline) { _underlineButton.interactable = false; _underlineButton.isOn = true; } else if (_font.UnderlineMask) { _underlineButton.interactable = false; _underlineButton.isOn = false; } else { _underlineButton.interactable = true; _underlineButton.isOn = (_style & FontStyles.Underline) != 0; } if (_font.SmallCaps) { _smallCapsButton.interactable = false; _smallCapsButton.isOn = true; } else if (_font.SmallCapsMask) { _smallCapsButton.interactable = false; _smallCapsButton.isOn = false; } else { _smallCapsButton.interactable = true; _smallCapsButton.isOn = (_style & FontStyles.SmallCaps) != 0; } _verticalButton.isOn = _vertical; _ignoreUpdates = false; } private void UpdateLineSpacing() { _ignoreUpdates = true; _lineSpacingSlider.value = Mathf.InverseLerp(_lineSpacingRange.x, _lineSpacingRange.y, _lineSpacing); ((TMP_InputField) _lineSpacingTextBox).text = $"{_lineSpacing:F1}"; _ignoreUpdates = false; } private void UpdateCharSpacing() { _ignoreUpdates = true; _charSpacingSlider.value = Mathf.InverseLerp(_charSpacingRange.x, _charSpacingRange.y, _charSpacing); ((TMP_InputField) _charSpacingTextBox).text = $"{_charSpacing:F1}"; _ignoreUpdates = false; } } }