Files
whale-town-front/scenes/prefabs/ui/ChatMessage.gd
WhaleTown Developer 603e7d9fc6 修bug
2026-01-20 20:11:07 +08:00

220 lines
7.1 KiB
GDScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
extends PanelContainer
# ============================================================================
# ChatMessage.gd - 聊天消息气泡组件
# ============================================================================
# 显示单条聊天消息的 UI 组件
#
# 核心职责:
# - 显示消息发送者、内容、时间戳
# - 区分自己和他人的消息样式
# - 自动格式化时间戳
#
# 使用方式:
# var message := chat_message_scene.instantiate()
# message.set_message("PlayerName", "Hello!", timestamp, false)
#
# 注意事项:
# - 使用 @onready 缓存节点引用
# - 最大宽度限制为 400 像素
# ============================================================================
class_name ChatMessage
# ============================================================================
# 导出参数
# ============================================================================
# 最大宽度(像素)
@export var max_width: int = 400
# ============================================================================
# 节点引用
# ============================================================================
# 用户名标签
var username_label: Label
# 时间戳标签
var timestamp_label: Label
# 内容标签
var content_label: RichTextLabel
# 用户信息容器
var user_info_container: HBoxContainer
# ============================================================================
# 生命周期方法
# ============================================================================
func _ready() -> void:
_cache_node_refs()
func _cache_node_refs() -> void:
if not username_label:
username_label = get_node_or_null("VBoxContainer/UserInfoContainer/UsernameLabel")
if not timestamp_label:
timestamp_label = get_node_or_null("VBoxContainer/UserInfoContainer/TimestampLabel")
if not content_label:
content_label = get_node_or_null("VBoxContainer/ContentLabel")
if not user_info_container:
user_info_container = get_node_or_null("VBoxContainer/UserInfoContainer")
# 内容换行与自适应高度
if content_label:
content_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
content_label.size_flags_vertical = Control.SIZE_SHRINK_BEGIN
content_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
content_label.fit_content = true
content_label.scroll_active = false
# ============================================================================
# 成员变量
# ============================================================================
# 是否为自己发送的消息
var _is_self: bool = false
# ============================================================================
# 公共 API
# ============================================================================
# 设置消息内容
#
# 参数:
# from_user: String - 发送者用户名
# content: String - 消息内容
# timestamp: float - Unix 时间戳
# is_self: bool - 是否为自己发送的消息(默认 false
#
# 使用示例:
# message.set_message("Alice", "Hello!", 1703500800.0, false)
func set_message(from_user: String, content: String, timestamp: float, is_self: bool = false) -> void:
_is_self = is_self
_cache_node_refs()
var safe_from_user := from_user
if safe_from_user.strip_edges().is_empty():
safe_from_user = "" if is_self else "玩家"
# 设置用户名(带空值检查)
if username_label:
username_label.text = safe_from_user
else:
push_error("ChatMessage: username_label is null!")
return
# 设置内容
if content_label:
content_label.clear() # 清除默认文本和所有内容
content_label.append_text(content) # 作为纯文本追加,避免 BBCode 解析导致内容不显示
else:
push_error("ChatMessage: content_label is null!")
return
# 设置时间戳
if timestamp_label:
timestamp_label.text = _format_timestamp(timestamp)
else:
push_error("ChatMessage: timestamp_label is null!")
return
# 应用样式
_apply_style()
# ============================================================================
# 内部方法 - 样式处理
# ============================================================================
# 应用样式(自己和他人的消息不同)
func _apply_style() -> void:
if not username_label or not timestamp_label or not user_info_container:
return
# 重要:设置垂直 size flags 让 Panel 适应内容高度
size_flags_vertical = Control.SIZE_SHRINK_BEGIN
if _is_self:
# 自己的消息:右侧对齐,蓝色背景
size_flags_horizontal = Control.SIZE_SHRINK_END
user_info_container.alignment = BoxContainer.ALIGNMENT_END
# 设置面板样式
add_theme_stylebox_override("panel", _get_self_style())
# 设置文字颜色 - ID使用金色 #FFD700
username_label.add_theme_color_override("font_color", Color(1.0, 0.8431373, 0.0))
timestamp_label.add_theme_color_override("font_color", Color(0.7, 0.7, 0.7))
if content_label:
content_label.add_theme_color_override("default_color", Color(0.95, 0.97, 1.0))
else:
# 他人的消息:左侧对齐,灰色背景
size_flags_horizontal = Control.SIZE_SHRINK_BEGIN
user_info_container.alignment = BoxContainer.ALIGNMENT_BEGIN
# 设置面板样式
add_theme_stylebox_override("panel", _get_other_style())
# 设置文字颜色 - ID使用蓝色 #69c0ff
username_label.add_theme_color_override("font_color", Color(0.4117647, 0.7529412, 1.0))
timestamp_label.add_theme_color_override("font_color", Color(0.5, 0.5, 0.5))
if content_label:
content_label.add_theme_color_override("default_color", Color(0.15, 0.15, 0.15))
# 获取自己消息的样式
func _get_self_style() -> StyleBoxFlat:
var style := StyleBoxFlat.new()
style.bg_color = Color(0.2, 0.6, 1.0, 0.8)
style.corner_radius_top_left = 10
style.corner_radius_top_right = 10
style.corner_radius_bottom_left = 10
style.corner_radius_bottom_right = 2
style.content_margin_left = 10
style.content_margin_right = 10
style.content_margin_top = 8
style.content_margin_bottom = 8
return style
# 获取他人消息的样式
func _get_other_style() -> StyleBoxFlat:
var style := StyleBoxFlat.new()
style.bg_color = Color(0.9, 0.9, 0.9, 0.8)
style.corner_radius_top_left = 10
style.corner_radius_top_right = 10
style.corner_radius_bottom_left = 2
style.corner_radius_bottom_right = 10
style.content_margin_left = 10
style.content_margin_right = 10
style.content_margin_top = 8
style.content_margin_bottom = 8
return style
# ============================================================================
# 内部方法 - 工具函数
# ============================================================================
# 格式化时间戳
#
# 参数:
# timestamp: float - Unix 时间戳
#
# 返回值:
# String - 格式化的时间字符串
func _format_timestamp(timestamp: float) -> String:
if timestamp == 0:
return ""
var datetime := Time.get_datetime_dict_from_unix_time(timestamp)
# 格式化为 HH:MM
return "%02d:%02d" % [datetime.hour, datetime.minute]
# 获取所有子节点名称(调试用)
func _get_all_children_names(node: Node, _indent: int = 0) -> String:
var result := ""
for child in node.get_children():
result += " ".repeat(_indent) + child.name + "\n"
result += _get_all_children_names(child, _indent + 1)
return result