add chat typing preview
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user