using System; using BriarQueen.Data.Identifiers; using TMPro; using UnityEngine; using UnityEngine.UI; namespace BriarQueen.UI.Codex { [ExecuteAlways] public class CodexLocationButton : MonoBehaviour { private const float MIN_WIDTH = 300f; private const float MAX_WIDTH = 400f; private const float FIXED_HEIGHT = 70f; [SerializeField] private Button _button; [SerializeField] private TextMeshProUGUI _label; [SerializeField] private float _leftPadding = 24f; [SerializeField] private float _rightPadding = 24f; [SerializeField] private float _extraWidthSafety = 20f; [SerializeField] private float _autoSizeMinFontSize = 18f; [SerializeField] private bool _debugInEditor = true; private string _lastText; private float _lastWidth = -1f; public Location Location { get; private set; } private void Awake() { AddButtonListener(); RefreshVisuals(); } #if UNITY_EDITOR private void Update() { if (Application.isPlaying) return; if (_label == null) return; if (_lastText != _label.text) RefreshVisuals(); } #endif private void OnEnable() { AddButtonListener(); RefreshVisuals(); } private void OnDestroy() { RemoveButtonListener(); } private void OnValidate() { RefreshVisuals(); } public event Action OnLocationClicked; public void Initialize(Location location, string displayText = null) { Location = location; if (_label != null) _label.text = string.IsNullOrWhiteSpace(displayText) ? location.ToString() : displayText; RefreshVisuals(); } private void HandleClicked() { OnLocationClicked?.Invoke(Location); } private void RefreshVisuals() { ResizeToFitLabel(); } private void ResizeToFitLabel() { if (_button == null || _label == null) return; var buttonRect = _button.GetComponent(); var labelRect = _label.rectTransform; if (buttonRect == null || labelRect == null) return; StretchLabelToButton(labelRect); _label.margin = new Vector4(_leftPadding, _label.margin.y, _rightPadding, _label.margin.w); _label.textWrappingMode = TextWrappingModes.NoWrap; _label.overflowMode = TextOverflowModes.Overflow; _label.enableAutoSizing = false; _label.ForceMeshUpdate(); var preferredSize = _label.GetPreferredValues(_label.text, Mathf.Infinity, Mathf.Infinity); var desiredWidth = preferredSize.x + _leftPadding + _rightPadding + _extraWidthSafety; var targetWidth = Mathf.Clamp(desiredWidth, MIN_WIDTH, MAX_WIDTH); var needsAutoSizing = desiredWidth > MAX_WIDTH; if (needsAutoSizing) { _label.enableAutoSizing = true; _label.fontSizeMin = _autoSizeMinFontSize; _label.fontSizeMax = _label.fontSize; _label.overflowMode = TextOverflowModes.Truncate; } buttonRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, targetWidth); buttonRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, FIXED_HEIGHT); #if UNITY_EDITOR if (_debugInEditor && (!Mathf.Approximately(targetWidth, _lastWidth) || _lastText != _label.text)) Debug.Log( $"[CodexLocationButton] '{_label.text}'\n" + $"Preferred: {preferredSize.x:F1} | Desired: {desiredWidth:F1} | Final: {targetWidth:F1}\n" + $"AutoSize: {needsAutoSizing}", this); #endif _lastText = _label.text; _lastWidth = targetWidth; LayoutRebuilder.ForceRebuildLayoutImmediate(buttonRect); _label.ForceMeshUpdate(); } private void StretchLabelToButton(RectTransform labelRect) { labelRect.anchorMin = new Vector2(0f, 0f); labelRect.anchorMax = new Vector2(1f, 1f); labelRect.offsetMin = Vector2.zero; labelRect.offsetMax = Vector2.zero; labelRect.pivot = new Vector2(0.5f, 0.5f); } private void AddButtonListener() { if (_button == null) return; _button.onClick.RemoveListener(HandleClicked); _button.onClick.AddListener(HandleClicked); } private void RemoveButtonListener() { if (_button == null) return; _button.onClick.RemoveListener(HandleClicked); } } }