using System.Collections.Generic; using System.Linq; using BrewMonster.Scripts.Chat; using BrewMonster.Scripts.Chat.EmotionData; using UnityEngine; using UnityEngine.UI; namespace BrewMonster.Scripts.ChatUI { /// /// MonoBehaviour gắn trên EmojiPanel — quản lý lưới emoji để người chơi chọn. /// MonoBehaviour placed on EmojiPanel — manages the emoji grid for player selection. /// /// Cách dùng trên Inspector: /// 1) Kéo component này lên EmojiPanel GameObject trong prefab. /// 2) Gán EmotionLibrarySpriteMap SO (đã build từ Emotion Atlas Converter). /// 3) Gán GridContent = child "Context" của EmojiPanel. /// 4) Tạo prefab EmojiButtonCell → gán vào CellPrefab. /// 5) Gán ChatInputHandler và ChatSystemlUI. /// public class EmojiPickerUI : MonoBehaviour { [Header("Emotion Data")] [Tooltip("SO chứa Library + TMP Sprite Asset. Tạo qua Perfect World → Chat → Emotion Atlas Converter.")] [SerializeField] EmotionLibrarySpriteMap _emotionSpriteMap; [Header("Grid")] [Tooltip("RectTransform chứa các ô emoji (child 'Context' của EmojiPanel). GridLayoutGroup được thêm tự động nếu chưa có.")] [SerializeField] RectTransform _gridContent; [Tooltip("Prefab một ô emoji (Button + Image + EmojiButtonCell). Cell prefab.")] [SerializeField] EmojiButtonCell _cellPrefab; [Tooltip("Kích thước mỗi ô (pixel). Cell size in pixels.")] [SerializeField] Vector2 _cellSize = new Vector2(64f, 64f); [Tooltip("Khoảng cách giữa các ô. Spacing between cells.")] [SerializeField] Vector2 _cellSpacing = new Vector2(4f, 4f); [Tooltip("Số cột cố định. Fixed column count.")] [SerializeField] int _columnCount = 8; [Header("Wiring")] [Tooltip("ChatInputHandler để nhét emoji code vào ô nhập liệu.")] [SerializeField] ChatInputHandler _chatInput; [Tooltip("ChatSystemlUI để đóng EmojiPanel sau khi chọn.")] [SerializeField] ChatSystemlUI _chatSystemUI; private readonly List _cells = new(); private bool _built; private void Awake() { TryAutoWireIfNeeded(); } private void Start() { BuildGrid(); } private void OnEnable() { if (!_built) BuildGrid(); } /// /// Xây dựng lưới emoji từ EmotionLibrarySpriteMap. /// Build emoji grid from EmotionLibrarySpriteMap. /// public void BuildGrid() { ClearGrid(); TryAutoWireIfNeeded(); if (_gridContent == null || _cellPrefab == null) { Debug.LogWarning("[Cuong] GridContent hoặc CellPrefab chưa được gán.", this); return; } if (_emotionSpriteMap == null || _emotionSpriteMap.Library == null) { Debug.LogWarning("[Cuong] EmotionLibrarySpriteMap chưa được gán hoặc chưa có Library.", this); return; } EnsureGridLayout(); _gridContent.gameObject.SetActive(true); // Keep UI stable: always render in set index order. foreach (var set in _emotionSpriteMap.Library.Sets.OrderBy(x => x?.EmotionSetIndex ?? int.MaxValue)) { if (set == null) continue; if (set.Entries == null || set.Entries.Count == 0) continue; for (int i = 0; i < set.Entries.Count; i++) { var entry = set.Entries[i]; if (entry == null) continue; Sprite icon = (entry.FrameSprites != null && entry.FrameSprites.Length > 0) ? entry.FrameSprites[0] : null; var cell = Instantiate(_cellPrefab, _gridContent, false); cell.Bind(set.EmotionSetIndex, i, icon, entry.Hint); cell.OnClicked += HandleEmojiClicked; _cells.Add(cell); } } _built = true; } private void ClearGrid() { foreach (var c in _cells) { if (c == null) continue; c.OnClicked -= HandleEmojiClicked; Destroy(c.gameObject); } _cells.Clear(); _built = false; } private void TryAutoWireIfNeeded() { _chatInput ??= GetComponentInParent(); _chatSystemUI ??= GetComponentInParent(); } private void EnsureGridLayout() { var layout = _gridContent.GetComponent(); if (layout == null) layout = _gridContent.gameObject.AddComponent(); layout.cellSize = _cellSize; layout.spacing = _cellSpacing; layout.constraint = GridLayoutGroup.Constraint.FixedColumnCount; layout.constraintCount = _columnCount; layout.startCorner = GridLayoutGroup.Corner.UpperLeft; layout.startAxis = GridLayoutGroup.Axis.Horizontal; layout.childAlignment = TextAnchor.UpperLeft; var fitter = _gridContent.GetComponent(); if (fitter == null) fitter = _gridContent.gameObject.AddComponent(); fitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize; } /// /// Gọi khi người chơi click một ô emoji — Called when player clicks an emoji cell. /// Chèn mã emoji vào input field và đóng panel. /// Inserts the emotion code into the chat input and closes the panel. /// private void HandleEmojiClicked(int emotionSet, int emotionIndex) { TryAutoWireIfNeeded(); if (_chatInput != null) { // Keep wire body as source of truth to avoid TMP tag race/corruption // when player taps emoji repeatedly. _chatInput.InsertEmoji(emotionSet, emotionIndex); Debug.Log("[Cuong] HandleEmojiClicked."); } else { Debug.LogWarning("[Cuong] Không tìm thấy ChatInputHandler để insert emoji.", this); } /*if (_chatSystemUI != null) { _chatSystemUI.CloseEmojiPanel(); }*/ } private void OnDestroy() { ClearGrid(); } } }