add chat typing preview

This commit is contained in:
CuongNV
2026-04-24 15:25:09 +07:00
parent 1c83f867e5
commit b40c9e246f
4 changed files with 81 additions and 66 deletions
@@ -238,10 +238,11 @@ MonoBehaviour:
inputBarRoot: {fileID: 3282421669272709967}
keyboardBottomPadding: 0
followMobileKeyboard: 1
typingPreviewRoot: {fileID: 4018402239503345071}
typingPreviewText: {fileID: 5673195965320428126}
typingPreviewRect: {fileID: 3282421669272709967}
previewVerticalOffset: 8
typingPreview:
typingPreviewRoot: {fileID: 4018402239503345071}
typingPreviewText: {fileID: 5673195965320428126}
typingPreviewRect: {fileID: 3282421669272709967}
previewVerticalOffset: 8
_spriteMap: {fileID: 11400000, guid: f634ecf63ca3d004f82af9b17c966fc9, type: 2}
channelButtons:
- channel: 0
+15 -62
View File
@@ -3,14 +3,11 @@ using UnityEngine;
using TMPro;
using CSNetwork.GPDataType;
using System.Collections.Generic;
using BrewMonster;
using BrewMonster.Network;
using BrewMonster.Scripts.Chat;
using BrewMonster.Scripts.Chat.EmotionData;
using BrewMonster.Scripts.Managers;
using BrewMonster.UI;
using CSNetwork;
using PerfectWorld.Scripts.Managers;
namespace BrewMonster.Scripts.ChatUI
{
@@ -31,23 +28,8 @@ namespace BrewMonster.Scripts.ChatUI
public TMP_InputField inputField;
public ChatSystemSO chatSystem;
[Header("Mobile Keyboard Follow")]
[Tooltip("Rect chứa input/chat composer cần neo lên trên keyboard.")]
[SerializeField] private RectTransform inputBarRoot;
[Tooltip("Padding đáy thêm cho vùng safe-area/gesture.")]
[SerializeField] private float keyboardBottomPadding = 0f;
[Tooltip("Bật để input bar bám theo keyboard khi mobile keyboard mở.")]
[SerializeField] private bool followMobileKeyboard = true;
[Header("Typing Preview")]
[Tooltip("Root của box xem trước nội dung đang gõ (nằm trên input bar).")]
[SerializeField] private GameObject typingPreviewRoot;
[Tooltip("Text hiển thị nội dung đang gõ realtime.")]
[SerializeField] private TMP_Text typingPreviewText;
[Tooltip("Rect của preview box để neo theo input bar.")]
[SerializeField] private RectTransform typingPreviewRect;
[Tooltip("Khoảng cách dọc cộng thêm cho preview box.")]
[SerializeField] private float previewVerticalOffset = 8f;
[SerializeField] private TypingPreviewController typingPreview = new();
[Header("Emoji")]
[Tooltip("SO ánh xạ emotion → TMP sprite tag. Gán EmotionLibrarySpriteMap đã build từ Emotion Atlas Converter.")]
@@ -129,7 +111,6 @@ namespace BrewMonster.Scripts.ChatUI
private float m_dwTickFarCry = 0;
private float m_dwTickFarCry2 = 0;
private Vector2 _inputBarBaseAnchoredPos;
private Vector2 _typingPreviewBaseAnchoredPos;
private bool _cachedAnchors;
private void Awake()
@@ -193,13 +174,14 @@ namespace BrewMonster.Scripts.ChatUI
}
UpdateChatDropdownInteractable();
CacheInitialAnchors();
UpdateTypingPreviewFromInput();
}
private void Update()
{
UpdateKeyboardAnchoredLayout();
// Keyboard open/close does not always trigger input value changed,
// so refresh preview gate every frame.
UpdateTypingPreviewFromInput();
}
// =====================================================
@@ -1015,40 +997,6 @@ namespace BrewMonster.Scripts.ChatUI
inputField.ActivateInputField();
}
void CacheInitialAnchors()
{
if (_cachedAnchors) return;
if (inputBarRoot != null)
_inputBarBaseAnchoredPos = inputBarRoot.anchoredPosition;
if (typingPreviewRect != null)
_typingPreviewBaseAnchoredPos = typingPreviewRect.anchoredPosition;
_cachedAnchors = true;
}
void UpdateKeyboardAnchoredLayout()
{
if (!followMobileKeyboard)
return;
CacheInitialAnchors();
float keyboardHeight = GetVisibleKeyboardHeight();
float yOffset = keyboardHeight + keyboardBottomPadding;
if (inputBarRoot != null)
{
var inputPos = _inputBarBaseAnchoredPos;
inputPos.y += yOffset;
inputBarRoot.anchoredPosition = inputPos;
}
if (typingPreviewRect != null)
{
var previewPos = _typingPreviewBaseAnchoredPos;
previewPos.y += yOffset + previewVerticalOffset;
typingPreviewRect.anchoredPosition = previewPos;
}
}
float GetVisibleKeyboardHeight()
{
if (!TouchScreenKeyboard.visible)
@@ -1060,14 +1008,19 @@ namespace BrewMonster.Scripts.ChatUI
void UpdateTypingPreviewFromInput()
{
if (typingPreviewRoot == null || typingPreviewText == null || inputField == null)
if (inputField == null)
return;
string body = ExtractMessageBodyFromVisual(inputField.text ?? "")?.Trim() ?? "";
bool shouldShow = inputField.isFocused && !string.IsNullOrEmpty(body);
typingPreviewRoot.SetActive(shouldShow);
if (shouldShow)
typingPreviewText.text = body;
string body = ExtractMessageBodyFromVisual(inputField.text ?? "");
typingPreview?.UpdatePreview(inputField.isFocused, body, CanShowTypingPreviewNow());
}
bool CanShowTypingPreviewNow()
{
if (!Application.isMobilePlatform)
return false;
return TouchScreenKeyboard.visible;
}
/// <summary>
+59
View File
@@ -0,0 +1,59 @@
using TMPro;
using UnityEngine;
namespace BrewMonster.Scripts.ChatUI
{
/// <summary>
/// Controls typing preview visibility/content and keyboard-anchored position.
/// Kept standalone so other chat/input UIs can reuse the same behavior.
/// </summary>
[System.Serializable]
public class TypingPreviewController
{
[Tooltip("Root của box xem trước nội dung đang gõ.")]
[SerializeField] private GameObject typingPreviewRoot;
[Tooltip("Text hiển thị nội dung đang gõ realtime.")]
[SerializeField] private TextMeshProUGUI typingPreviewText;
[Tooltip("Rect của preview box để neo theo keyboard.")]
[SerializeField] private RectTransform typingPreviewRect;
[Tooltip("Khoảng cách dọc cộng thêm cho preview box.")]
[SerializeField] private float previewVerticalOffset = 8f;
private Vector2 _baseAnchoredPos;
private bool _cachedAnchor;
public void CacheInitialAnchor()
{
if (_cachedAnchor)
return;
if (typingPreviewRect != null)
_baseAnchoredPos = typingPreviewRect.anchoredPosition;
_cachedAnchor = true;
}
public void ApplyKeyboardOffset(float yOffset)
{
if (typingPreviewRect == null)
return;
CacheInitialAnchor();
var previewPos = _baseAnchoredPos;
previewPos.y += yOffset + previewVerticalOffset;
typingPreviewRect.anchoredPosition = previewPos;
}
public void UpdatePreview(bool isInputFocused, string body, bool canShowPreview)
{
if (typingPreviewRoot == null || typingPreviewText == null)
return;
string trimmedBody = (body ?? string.Empty).Trim();
bool shouldShow = canShowPreview && isInputFocused && !string.IsNullOrEmpty(trimmedBody);
typingPreviewRoot.SetActive(shouldShow);
if (shouldShow)
typingPreviewText.text = trimmedBody;
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: a9934ac12c563f746a3ba562e3de5bff