Test preview chat for mobile
This commit is contained in:
@@ -156,7 +156,7 @@ namespace BrewMonster.Scripts.ChatUI
|
||||
void OnOpenChatPanelRequested(OpenChatPanelRequestedEvent _)
|
||||
{
|
||||
if (this == null) return;
|
||||
OpenChatPanel();
|
||||
OpenChatPanel(_.focusInputOnOpen);
|
||||
}
|
||||
|
||||
private bool ShouldShowMessage(ChatMessageData data)
|
||||
@@ -347,13 +347,18 @@ namespace BrewMonster.Scripts.ChatUI
|
||||
}
|
||||
|
||||
public void OpenChatPanel()
|
||||
{
|
||||
OpenChatPanel(true);
|
||||
}
|
||||
|
||||
public void OpenChatPanel(bool focusInputOnOpen)
|
||||
{
|
||||
if (chatPanelUIGO == null) return;
|
||||
SetChatPanelAndBgVisible(true);
|
||||
RefreshVisible();
|
||||
|
||||
_chatInput ??= GetComponent<ChatInputHandler>();
|
||||
if (_chatInput != null && _chatInput.inputField != null)
|
||||
if (focusInputOnOpen && _chatInput != null && _chatInput.inputField != null)
|
||||
_chatInput.inputField.ActivateInputField();
|
||||
}
|
||||
|
||||
@@ -392,5 +397,13 @@ namespace BrewMonster.Scripts.ChatUI
|
||||
/// <summary>
|
||||
/// Mini chat (hoặc HUD) publish để mở panel chat đầy đủ; <see cref="ChatSystemlUI"/> subscribe.
|
||||
/// </summary>
|
||||
public struct OpenChatPanelRequestedEvent { }
|
||||
public struct OpenChatPanelRequestedEvent
|
||||
{
|
||||
public bool focusInputOnOpen;
|
||||
|
||||
public OpenChatPanelRequestedEvent(bool focusInputOnOpen)
|
||||
{
|
||||
this.focusInputOnOpen = focusInputOnOpen;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ namespace BrewMonster.Scripts.ChatUI
|
||||
if (!string.IsNullOrEmpty(chatDialogName) && CECUIManager.Instance != null)
|
||||
CECUIManager.Instance.ShowUI(chatDialogName);
|
||||
|
||||
EventBus.Publish(new OpenChatPanelRequestedEvent());
|
||||
EventBus.Publish(new OpenChatPanelRequestedEvent(false));
|
||||
}
|
||||
|
||||
void OnChannelFilterChanged(ChatChannelFilterChangedEvent e)
|
||||
|
||||
@@ -187,6 +187,7 @@ RectTransform:
|
||||
m_Children:
|
||||
- {fileID: 2739455247741079987}
|
||||
- {fileID: 1473246120053017968}
|
||||
- {fileID: 3282421669272709967}
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
@@ -234,6 +235,13 @@ MonoBehaviour:
|
||||
m_EditorClassIdentifier:
|
||||
inputField: {fileID: 3037472824809581303}
|
||||
chatSystem: {fileID: 11400000, guid: 43f54723aa074c74e83e5be28975bee5, type: 2}
|
||||
inputBarRoot: {fileID: 6545370471477453367}
|
||||
keyboardBottomPadding: 0
|
||||
followMobileKeyboard: 1
|
||||
typingPreviewRoot: {fileID: 4018402239503345071}
|
||||
typingPreviewText: {fileID: 5673195965320428126}
|
||||
typingPreviewRect: {fileID: 3282421669272709967}
|
||||
previewVerticalOffset: 8
|
||||
_spriteMap: {fileID: 11400000, guid: f634ecf63ca3d004f82af9b17c966fc9, type: 2}
|
||||
channelButtons:
|
||||
- channel: 0
|
||||
@@ -1110,6 +1118,142 @@ MonoBehaviour:
|
||||
m_OnValueChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
--- !u!1 &2898638287858915607
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 2130739312226787242}
|
||||
- component: {fileID: 9133278188770223934}
|
||||
- component: {fileID: 5673195965320428126}
|
||||
m_Layer: 5
|
||||
m_Name: Text (TMP)
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &2130739312226787242
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2898638287858915607}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 3282421669272709967}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 1, y: 1}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 0, y: 0}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &9133278188770223934
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2898638287858915607}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &5673195965320428126
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2898638287858915607}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_text: New Text
|
||||
m_isRightToLeft: 0
|
||||
m_fontAsset: {fileID: 11400000, guid: 369c2e14814cc9a4b8e3ad4e37769134, type: 2}
|
||||
m_sharedMaterial: {fileID: 9092487103257209053, guid: 369c2e14814cc9a4b8e3ad4e37769134, type: 2}
|
||||
m_fontSharedMaterials: []
|
||||
m_fontMaterial: {fileID: 0}
|
||||
m_fontMaterials: []
|
||||
m_fontColor32:
|
||||
serializedVersion: 2
|
||||
rgba: 4294967295
|
||||
m_fontColor: {r: 0, g: 0, b: 0, a: 1}
|
||||
m_enableVertexGradient: 0
|
||||
m_colorMode: 3
|
||||
m_fontColorGradient:
|
||||
topLeft: {r: 1, g: 1, b: 1, a: 1}
|
||||
topRight: {r: 1, g: 1, b: 1, a: 1}
|
||||
bottomLeft: {r: 1, g: 1, b: 1, a: 1}
|
||||
bottomRight: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_fontColorGradientPreset: {fileID: 0}
|
||||
m_spriteAsset: {fileID: 0}
|
||||
m_tintAllSprites: 0
|
||||
m_StyleSheet: {fileID: 0}
|
||||
m_TextStyleHashCode: -1183493901
|
||||
m_overrideHtmlColors: 0
|
||||
m_faceColor:
|
||||
serializedVersion: 2
|
||||
rgba: 4294967295
|
||||
m_fontSize: 36
|
||||
m_fontSizeBase: 36
|
||||
m_fontWeight: 400
|
||||
m_enableAutoSizing: 0
|
||||
m_fontSizeMin: 18
|
||||
m_fontSizeMax: 72
|
||||
m_fontStyle: 0
|
||||
m_HorizontalAlignment: 1
|
||||
m_VerticalAlignment: 256
|
||||
m_textAlignment: 65535
|
||||
m_characterSpacing: 0
|
||||
m_wordSpacing: 0
|
||||
m_lineSpacing: 0
|
||||
m_lineSpacingMax: 0
|
||||
m_paragraphSpacing: 0
|
||||
m_charWidthMaxAdj: 0
|
||||
m_TextWrappingMode: 1
|
||||
m_wordWrappingRatios: 0.4
|
||||
m_overflowMode: 0
|
||||
m_linkedTextComponent: {fileID: 0}
|
||||
parentLinkedComponent: {fileID: 0}
|
||||
m_enableKerning: 0
|
||||
m_ActiveFontFeatures: 6e72656b
|
||||
m_enableExtraPadding: 0
|
||||
checkPaddingRequired: 0
|
||||
m_isRichText: 1
|
||||
m_EmojiFallbackSupport: 1
|
||||
m_parseCtrlCharacters: 1
|
||||
m_isOrthographic: 1
|
||||
m_isCullingEnabled: 0
|
||||
m_horizontalMapping: 0
|
||||
m_verticalMapping: 0
|
||||
m_uvLineOffset: 0
|
||||
m_geometrySortingOrder: 0
|
||||
m_IsTextObjectScaleStatic: 0
|
||||
m_VertexBufferAutoSizeReduction: 0
|
||||
m_useMaxVisibleDescender: 1
|
||||
m_pageToDisplay: 1
|
||||
m_margin: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_isUsingLegacyAnimationComponent: 0
|
||||
m_isVolumetricText: 0
|
||||
m_hasFontAssetChanged: 0
|
||||
m_baseMaterial: {fileID: 0}
|
||||
m_maskOffset: {x: 0, y: 0, z: 0, w: 0}
|
||||
--- !u!1 &3095457626805744279
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -1869,6 +2013,82 @@ MonoBehaviour:
|
||||
m_OnClick:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
--- !u!1 &4018402239503345071
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 3282421669272709967}
|
||||
- component: {fileID: 1622721548801034628}
|
||||
- component: {fileID: 7848833373218203551}
|
||||
m_Layer: 5
|
||||
m_Name: Typing Preview
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 0
|
||||
--- !u!224 &3282421669272709967
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 4018402239503345071}
|
||||
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 2130739312226787242}
|
||||
m_Father: {fileID: 806170753671297629}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0.5}
|
||||
m_AnchorMax: {x: 1, y: 0.5}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 0, y: 100}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &1622721548801034628
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 4018402239503345071}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &7848833373218203551
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 4018402239503345071}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_Sprite: {fileID: 0}
|
||||
m_Type: 0
|
||||
m_PreserveAspect: 0
|
||||
m_FillCenter: 1
|
||||
m_FillMethod: 4
|
||||
m_FillAmount: 1
|
||||
m_FillClockwise: 1
|
||||
m_FillOrigin: 0
|
||||
m_UseSpriteMesh: 0
|
||||
m_PixelsPerUnitMultiplier: 1
|
||||
--- !u!1 &4042949282556349356
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
|
||||
@@ -31,6 +31,24 @@ 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;
|
||||
|
||||
[Header("Emoji")]
|
||||
[Tooltip("SO ánh xạ emotion → TMP sprite tag. Gán EmotionLibrarySpriteMap đã build từ Emotion Atlas Converter.")]
|
||||
[SerializeField] EmotionLibrarySpriteMap _spriteMap;
|
||||
@@ -110,6 +128,9 @@ 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()
|
||||
{
|
||||
@@ -172,6 +193,13 @@ namespace BrewMonster.Scripts.ChatUI
|
||||
}
|
||||
|
||||
UpdateChatDropdownInteractable();
|
||||
CacheInitialAnchors();
|
||||
UpdateTypingPreviewFromInput();
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
UpdateKeyboardAnchoredLayout();
|
||||
}
|
||||
|
||||
// =====================================================
|
||||
@@ -812,6 +840,8 @@ namespace BrewMonster.Scripts.ChatUI
|
||||
{
|
||||
inputField.text = "";
|
||||
}
|
||||
|
||||
UpdateTypingPreviewFromInput();
|
||||
}
|
||||
|
||||
private int GetEmotionSetForCurrentChannel()
|
||||
@@ -877,6 +907,7 @@ namespace BrewMonster.Scripts.ChatUI
|
||||
if (_syncingWireToDisplay)
|
||||
{
|
||||
_lastInputFieldText = newText;
|
||||
UpdateTypingPreviewFromInput();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -884,6 +915,7 @@ namespace BrewMonster.Scripts.ChatUI
|
||||
{
|
||||
_lastInputFieldText = newText;
|
||||
SyncChatWireBodyFromInput();
|
||||
UpdateTypingPreviewFromInput();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -905,11 +937,13 @@ namespace BrewMonster.Scripts.ChatUI
|
||||
inputField.ForceLabelUpdate();
|
||||
_lastInputFieldText = fixedText;
|
||||
SyncChatWireBodyFromInput();
|
||||
UpdateTypingPreviewFromInput();
|
||||
return;
|
||||
}
|
||||
|
||||
_lastInputFieldText = newText;
|
||||
SyncChatWireBodyFromInput();
|
||||
UpdateTypingPreviewFromInput();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -981,6 +1015,61 @@ 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)
|
||||
return 0f;
|
||||
|
||||
Rect area = TouchScreenKeyboard.area;
|
||||
return area.height > 0f ? area.height : 0f;
|
||||
}
|
||||
|
||||
void UpdateTypingPreviewFromInput()
|
||||
{
|
||||
if (typingPreviewRoot == null || typingPreviewText == null || inputField == null)
|
||||
return;
|
||||
|
||||
string body = ExtractMessageBodyFromVisual(inputField.text ?? "")?.Trim() ?? "";
|
||||
bool shouldShow = inputField.isFocused && !string.IsNullOrEmpty(body);
|
||||
typingPreviewRoot.SetActive(shouldShow);
|
||||
if (shouldShow)
|
||||
typingPreviewText.text = body;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// C++: GetHostPlayer()->GetPack()->GetItemTotalNum(id) — đếm túi chính.
|
||||
/// </summary>
|
||||
|
||||
Reference in New Issue
Block a user