diff --git a/Assets/PerfectWorld/Scripts/Chat/UI/ChatSystemlUI.cs b/Assets/PerfectWorld/Scripts/Chat/UI/ChatSystemlUI.cs index 17d416447b..fdb12bd4b2 100644 --- a/Assets/PerfectWorld/Scripts/Chat/UI/ChatSystemlUI.cs +++ b/Assets/PerfectWorld/Scripts/Chat/UI/ChatSystemlUI.cs @@ -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(); - if (_chatInput != null && _chatInput.inputField != null) + if (focusInputOnOpen && _chatInput != null && _chatInput.inputField != null) _chatInput.inputField.ActivateInputField(); } @@ -392,5 +397,13 @@ namespace BrewMonster.Scripts.ChatUI /// /// Mini chat (hoặc HUD) publish để mở panel chat đầy đủ; subscribe. /// - public struct OpenChatPanelRequestedEvent { } + public struct OpenChatPanelRequestedEvent + { + public bool focusInputOnOpen; + + public OpenChatPanelRequestedEvent(bool focusInputOnOpen) + { + this.focusInputOnOpen = focusInputOnOpen; + } + } } diff --git a/Assets/PerfectWorld/Scripts/Chat/UI/MiniChatUI.cs b/Assets/PerfectWorld/Scripts/Chat/UI/MiniChatUI.cs index 101a4f021e..132a66326a 100644 --- a/Assets/PerfectWorld/Scripts/Chat/UI/MiniChatUI.cs +++ b/Assets/PerfectWorld/Scripts/Chat/UI/MiniChatUI.cs @@ -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) diff --git a/Assets/Prefabs/ChatSystem/prefab_ChatSystemUI.prefab b/Assets/Prefabs/ChatSystem/prefab_ChatSystemUI.prefab index f1481018a6..11af5afd56 100644 --- a/Assets/Prefabs/ChatSystem/prefab_ChatSystemUI.prefab +++ b/Assets/Prefabs/ChatSystem/prefab_ChatSystemUI.prefab @@ -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,11 @@ MonoBehaviour: m_EditorClassIdentifier: inputField: {fileID: 3037472824809581303} chatSystem: {fileID: 11400000, guid: 43f54723aa074c74e83e5be28975bee5, type: 2} + typingPreview: + typingPreviewRoot: {fileID: 4018402239503345071} + typingPreviewText: {fileID: 1616725709352662407} + typingPreviewRect: {fileID: 3282421669272709967} + previewVerticalOffset: 100 _spriteMap: {fileID: 11400000, guid: f634ecf63ca3d004f82af9b17c966fc9, type: 2} channelButtons: - channel: 0 @@ -1110,6 +1116,163 @@ MonoBehaviour: m_OnValueChanged: m_PersistentCalls: m_Calls: [] +--- !u!1 &2902411919389723293 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 9105702282600116830} + - component: {fileID: 8286996527838887664} + - component: {fileID: 1830860820497748550} + - component: {fileID: 4544532093443431984} + m_Layer: 5 + m_Name: Placeholder + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &9105702282600116830 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2902411919389723293} + 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: 3605044702849397252} + 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 &8286996527838887664 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2902411919389723293} + m_CullTransparentMesh: 1 +--- !u!114 &1830860820497748550 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2902411919389723293} + 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: "Nh\u1EADp n\u1ED9i dung..." + 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: 2150773298 + m_fontColor: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 0.5} + 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: 2 + 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: 0 + m_wordWrappingRatios: 0.4 + m_overflowMode: 0 + m_linkedTextComponent: {fileID: 0} + parentLinkedComponent: {fileID: 0} + m_enableKerning: 0 + m_ActiveFontFeatures: 6e72656b + m_enableExtraPadding: 1 + 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!114 &4544532093443431984 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2902411919389723293} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 306cc8c2b49d7114eaa3623786fc2126, type: 3} + m_Name: + m_EditorClassIdentifier: + m_IgnoreLayout: 1 + m_MinWidth: -1 + m_MinHeight: -1 + m_PreferredWidth: -1 + m_PreferredHeight: -1 + m_FlexibleWidth: -1 + m_FlexibleHeight: -1 + m_LayoutPriority: 1 --- !u!1 &3095457626805744279 GameObject: m_ObjectHideFlags: 0 @@ -1493,6 +1656,142 @@ MonoBehaviour: m_OnClick: m_PersistentCalls: m_Calls: [] +--- !u!1 &3569864358642720813 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 6179475653333898123} + - component: {fileID: 3165768457449138107} + - component: {fileID: 4701185761014729773} + m_Layer: 5 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &6179475653333898123 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3569864358642720813} + 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: 3605044702849397252} + 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 &3165768457449138107 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3569864358642720813} + m_CullTransparentMesh: 1 +--- !u!114 &4701185761014729773 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3569864358642720813} + 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: "\u200B" + 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: 4281479730 + m_fontColor: {r: 0.19607843, g: 0.19607843, b: 0.19607843, 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: 3 + m_wordWrappingRatios: 0.4 + m_overflowMode: 0 + m_linkedTextComponent: {fileID: 0} + parentLinkedComponent: {fileID: 0} + m_enableKerning: 0 + m_ActiveFontFeatures: 6e72656b + m_enableExtraPadding: 1 + 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 &3646889256970119758 GameObject: m_ObjectHideFlags: 0 @@ -1671,6 +1970,184 @@ MonoBehaviour: isAlert: 0 m_InputValidator: {fileID: 0} m_ShouldActivateOnSelect: 1 +--- !u!1 &3775041254222000362 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4921631315982434735} + - component: {fileID: 682447300637418577} + - component: {fileID: 7576867370938653448} + - component: {fileID: 1616725709352662407} + m_Layer: 5 + m_Name: InputField (TMP) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &4921631315982434735 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3775041254222000362} + 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: 3605044702849397252} + 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 &682447300637418577 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3775041254222000362} + m_CullTransparentMesh: 1 +--- !u!114 &7576867370938653448 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3775041254222000362} + 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: 10911, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + 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!114 &1616725709352662407 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3775041254222000362} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 2da0c512f12947e489f739169773d7ca, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 1, g: 1, b: 1, a: 1} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 0 + m_TargetGraphic: {fileID: 7576867370938653448} + m_TextViewport: {fileID: 3605044702849397252} + m_TextComponent: {fileID: 4701185761014729773} + m_Placeholder: {fileID: 1830860820497748550} + m_VerticalScrollbar: {fileID: 0} + m_VerticalScrollbarEventHandler: {fileID: 0} + m_LayoutGroup: {fileID: 0} + m_ScrollSensitivity: 1 + m_ContentType: 0 + m_InputType: 0 + m_AsteriskChar: 42 + m_KeyboardType: 0 + m_LineType: 0 + m_HideMobileInput: 0 + m_HideSoftKeyboard: 0 + m_CharacterValidation: 0 + m_RegexValue: + m_GlobalPointSize: 36 + m_CharacterLimit: 0 + m_OnEndEdit: + m_PersistentCalls: + m_Calls: [] + m_OnSubmit: + m_PersistentCalls: + m_Calls: [] + m_OnSelect: + m_PersistentCalls: + m_Calls: [] + m_OnDeselect: + m_PersistentCalls: + m_Calls: [] + m_OnTextSelection: + m_PersistentCalls: + m_Calls: [] + m_OnEndTextSelection: + m_PersistentCalls: + m_Calls: [] + m_OnValueChanged: + m_PersistentCalls: + m_Calls: [] + m_OnTouchScreenKeyboardStatusChanged: + m_PersistentCalls: + m_Calls: [] + m_CaretColor: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} + m_CustomCaretColor: 0 + m_SelectionColor: {r: 0.65882355, g: 0.80784315, b: 1, a: 0.7529412} + m_Text: + m_CaretBlinkRate: 0.85 + m_CaretWidth: 1 + m_ReadOnly: 0 + m_RichText: 1 + m_GlobalFontAsset: {fileID: 11400000, guid: 369c2e14814cc9a4b8e3ad4e37769134, type: 2} + m_OnFocusSelectAll: 1 + m_ResetOnDeActivation: 1 + m_KeepTextSelectionVisible: 0 + m_RestoreOriginalTextOnEscape: 1 + m_isRichTextEditingAllowed: 0 + m_LineLimit: 0 + isAlert: 0 + m_InputValidator: {fileID: 0} + m_ShouldActivateOnSelect: 1 --- !u!1 &3878366334053570553 GameObject: m_ObjectHideFlags: 0 @@ -1869,6 +2346,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: 4921631315982434735} + m_Father: {fileID: 806170753671297629} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 15, y: -100} + m_SizeDelta: {x: -30, y: 450.9249} + m_Pivot: {x: 0.5, y: 1} +--- !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 @@ -4063,6 +4616,58 @@ MonoBehaviour: m_hasFontAssetChanged: 0 m_baseMaterial: {fileID: 0} m_maskOffset: {x: 0, y: 0, z: 0, w: 0} +--- !u!1 &6903426946426451281 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3605044702849397252} + - component: {fileID: 2373715959292045924} + m_Layer: 5 + m_Name: Text Area + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &3605044702849397252 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6903426946426451281} + 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: 9105702282600116830} + - {fileID: 6179475653333898123} + m_Father: {fileID: 4921631315982434735} + 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.50001526} + m_SizeDelta: {x: -20, y: -13} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &2373715959292045924 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6903426946426451281} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 3312d7739989d2b4e91e6319e9a96d76, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Padding: {x: -8, y: -5, z: -8, w: -5} + m_Softness: {x: 0, y: 0} --- !u!1 &6957797720283583814 GameObject: m_ObjectHideFlags: 0 diff --git a/Assets/Scripts/ChatInputHandler.cs b/Assets/Scripts/ChatInputHandler.cs index 88371501f1..af0c5bca5b 100644 --- a/Assets/Scripts/ChatInputHandler.cs +++ b/Assets/Scripts/ChatInputHandler.cs @@ -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,6 +28,9 @@ namespace BrewMonster.Scripts.ChatUI public TMP_InputField inputField; public ChatSystemSO chatSystem; + [Header("Typing Preview")] + [SerializeField] private TypingPreviewController typingPreview = new(); + [Header("Emoji")] [Tooltip("SO ánh xạ emotion → TMP sprite tag. Gán EmotionLibrarySpriteMap đã build từ Emotion Atlas Converter.")] [SerializeField] EmotionLibrarySpriteMap _spriteMap; @@ -110,6 +110,8 @@ namespace BrewMonster.Scripts.ChatUI private float m_dwTickFarCry = 0; private float m_dwTickFarCry2 = 0; + private Vector2 _inputBarBaseAnchoredPos; + private bool _cachedAnchors; private void Awake() { @@ -172,6 +174,14 @@ namespace BrewMonster.Scripts.ChatUI } UpdateChatDropdownInteractable(); + UpdateTypingPreviewFromInput(); + } + + private void Update() + { + // Keyboard open/close does not always trigger input value changed, + // so refresh preview gate every frame. + UpdateTypingPreviewFromInput(); } // ===================================================== @@ -812,6 +822,8 @@ namespace BrewMonster.Scripts.ChatUI { inputField.text = ""; } + + UpdateTypingPreviewFromInput(); } private int GetEmotionSetForCurrentChannel() @@ -877,6 +889,7 @@ namespace BrewMonster.Scripts.ChatUI if (_syncingWireToDisplay) { _lastInputFieldText = newText; + UpdateTypingPreviewFromInput(); return; } @@ -884,6 +897,7 @@ namespace BrewMonster.Scripts.ChatUI { _lastInputFieldText = newText; SyncChatWireBodyFromInput(); + UpdateTypingPreviewFromInput(); return; } @@ -905,11 +919,13 @@ namespace BrewMonster.Scripts.ChatUI inputField.ForceLabelUpdate(); _lastInputFieldText = fixedText; SyncChatWireBodyFromInput(); + UpdateTypingPreviewFromInput(); return; } _lastInputFieldText = newText; SyncChatWireBodyFromInput(); + UpdateTypingPreviewFromInput(); } /// @@ -981,6 +997,15 @@ namespace BrewMonster.Scripts.ChatUI inputField.ActivateInputField(); } + void UpdateTypingPreviewFromInput() + { + if (inputField == null) + return; + + string body = ExtractMessageBodyFromVisual(inputField.text ?? ""); + typingPreview?.UpdatePreview(inputField.isFocused, body); + } + /// /// C++: GetHostPlayer()->GetPack()->GetItemTotalNum(id) — đếm túi chính. /// diff --git a/Assets/Scripts/TypingPreviewController.cs b/Assets/Scripts/TypingPreviewController.cs new file mode 100644 index 0000000000..fe81ac39b4 --- /dev/null +++ b/Assets/Scripts/TypingPreviewController.cs @@ -0,0 +1,76 @@ +using TMPro; +using UnityEngine; + +namespace BrewMonster.Scripts.ChatUI +{ + /// + /// Controls typing preview visibility/content and keyboard-anchored position. + /// Kept standalone so other chat/input UIs can reuse the same behavior. + /// + [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 TMP_InputField 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) + { + if (typingPreviewRoot == null || typingPreviewText == null) + return; + + string previewBody = body ?? string.Empty; + bool keyboardVisible = IsMobileKeyboardVisible(); + bool shouldShow = keyboardVisible && isInputFocused; + typingPreviewRoot.SetActive(shouldShow); + if (shouldShow) + typingPreviewText.text = previewBody; + } + + bool IsMobileKeyboardVisible() + { + if (!Application.isMobilePlatform) + return false; + return GetVisibleKeyboardHeight() > 0f || TouchScreenKeyboard.visible; + } + + float GetVisibleKeyboardHeight() + { + if (!TouchScreenKeyboard.visible) + return 0f; + + Rect area = TouchScreenKeyboard.area; + return area.height > 0f ? area.height : 0f; + } + } +} diff --git a/Assets/Scripts/TypingPreviewController.cs.meta b/Assets/Scripts/TypingPreviewController.cs.meta new file mode 100644 index 0000000000..d2f7801a37 --- /dev/null +++ b/Assets/Scripts/TypingPreviewController.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: a9934ac12c563f746a3ba562e3de5bff \ No newline at end of file diff --git a/Assets/Scripts/typing-preview-summary.md b/Assets/Scripts/typing-preview-summary.md new file mode 100644 index 0000000000..b86b53a2d8 --- /dev/null +++ b/Assets/Scripts/typing-preview-summary.md @@ -0,0 +1,60 @@ +# Typing Preview - Summary + +## Muc tieu da thuc hien + +- Tach logic `typingPreview` ra class rieng de de tai su dung. +- Lien ket lai logic giua `ChatInputHandler` va `TypingPreviewController`. +- Dieu chinh co che hien/an theo yeu cau mobile keyboard. + +## Cac file da cap nhat + +- `Assets/Scripts/TypingPreviewController.cs` +- `Assets/Scripts/ChatInputHandler.cs` + +## Noi dung da lam + +### 1) Tach typing preview thanh class rieng + +- Tao class `TypingPreviewController` de quan ly: + - Hien/an preview (`typingPreviewRoot`) + - Cap nhat text preview realtime (`typingPreviewText`) + - Dinh vi preview theo offset keyboard (`typingPreviewRect`, `previewVerticalOffset`) + +### 2) Lien ket voi ChatInputHandler + +- `ChatInputHandler` giu `SerializeField`: + - `TypingPreviewController typingPreview` +- Moi khi input thay doi text, `ChatInputHandler` lay message body (bo prefix kenh/whisper) roi day sang controller de cap nhat preview. +- Khi clear input, preview duoc cap nhat lai ngay de an dung trang thai. + +### 3) Co che mobile keyboard (cap nhat moi nhat) + +- Da chuyen logic check keyboard vao thang `TypingPreviewController`: + - `IsMobileKeyboardVisible()` + - `GetVisibleKeyboardHeight()` +- `ChatInputHandler` da bo cac ham keyboard-check khong con dung. +- `ChatInputHandler` chi can goi `typingPreview.UpdatePreview(isFocused, body)`. +- `ChatInputHandler.Update()` van refresh moi frame de bat kip luc keyboard mo/dong. +- Rule hien moi nhat: + - Keyboard mobile vua mo la preview show ngay (neu input dang focus), khong can cho co ky tu. + +## Hanh vi hien tai + +- Preview chi hien khi: + - Dang o mobile + - Keyboard dang mo + - Input dang focus +- Khong con yeu cau body phai khong rong (show ngay khi keyboard mo). +- Khi tat keyboard, preview an ngay. +- Khi mo lai keyboard, noi dung da go truoc do van con trong input va preview hien lai dung noi dung. + +## Emoji tren preview + +- Hien tai preview duoc set text truc tiep tu `body`, nen neu `body` co TMP sprite tag thi co the hien emoji. +- Luu y cau hinh component: + - `typingPreviewText` dang de kieu `TMP_InputField` trong `TypingPreviewController`. + - Neu muon on dinh cho muc dich "preview display", nen doi sang `TMP_Text` hoac `TextMeshProUGUI`. + +## Ghi chu + +- Da kiem tra lint sau cac lan sua, khong phat hien loi lint moi tren cac file da chinh. diff --git a/Assets/Scripts/typing-preview-summary.md.meta b/Assets/Scripts/typing-preview-summary.md.meta new file mode 100644 index 0000000000..8304d161ef --- /dev/null +++ b/Assets/Scripts/typing-preview-summary.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 85a85257c23e84e41ba29cbc51455178 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: