extends Panel # ============================================================================ # 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: # 使用 get_node 获取节点引用 username_label = get_node_or_null("VBoxContainer/UserInfoContainer/UsernameLabel") timestamp_label = get_node_or_null("VBoxContainer/UserInfoContainer/TimestampLabel") content_label = get_node_or_null("VBoxContainer/ContentLabel") user_info_container = get_node_or_null("VBoxContainer/UserInfoContainer") # 调试输出 if username_label: print("✅ UsernameLabel found") else: print("❌ UsernameLabel NOT found!") # 打印所有子节点帮助调试 print("Available children: ", _get_all_children_names(self)) # 应用最大宽度限制 custom_minimum_size.x = min(max_width, get_parent().size.x) # ============================================================================ # 成员变量 # ============================================================================ # 是否为自己发送的消息 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 # 设置用户名(带空值检查) if username_label: username_label.text = from_user else: push_error("ChatMessage: username_label is null!") return # 设置内容 if content_label: content_label.clear() # 清除默认文本和所有内容 content_label.bbcode_text = content # 使用 bbcode_text 因为 bbcode_enabled = true 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 # 设置大小约束 - 宽度固定,高度适应内容 if get_parent(): custom_minimum_size.x = min(max_width, get_parent().size.x) # 重要:设置垂直 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)) 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)) # 获取自己消息的样式 func _get_self_style() -> StyleBoxFlat: var style := StyleBoxFlat.new() style.bg_color = Color(0.2, 0.6, 1.0, 0.3) 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.5) 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