extends Control class_name DialogueBox ## 对话框 UI ## 显示对话消息和输入界面 # UI 元素 var message_display: RichTextLabel var message_input: LineEdit var send_button: Button var close_button: Button var target_label: Label var container: VBoxContainer var scroll_container: ScrollContainer # 状态 var current_target_name: String = "" # 信号 signal message_sent(message: String) signal dialogue_closed() func _ready(): """初始化对话框""" _create_ui() hide() func _process(_delta): """每帧检查焦点状态""" # 简化焦点管理,避免干扰按钮点击 pass func _input(event): """处理对话框的输入事件""" # 只有在对话框可见时才处理输入 if not visible: return # ESC键关闭对话框 if event is InputEventKey and event.pressed: if event.keycode == KEY_ESCAPE: _on_close_pressed() get_viewport().set_input_as_handled() # 阻止事件继续传播 return # 不要处理鼠标事件,让按钮自己处理 if event is InputEventMouseButton: return ## 创建 UI 元素 func _create_ui(): """创建对话框的所有 UI 元素""" # 设置为居中显示 anchor_left = 0.5 anchor_top = 0.5 anchor_right = 0.5 anchor_bottom = 0.5 offset_left = -250 offset_top = -200 offset_right = 250 offset_bottom = 200 # 创建背景 var panel = Panel.new() panel.anchor_right = 1.0 panel.anchor_bottom = 1.0 add_child(panel) # 创建主容器 container = VBoxContainer.new() container.anchor_right = 1.0 container.anchor_bottom = 1.0 container.offset_left = 10 container.offset_top = 10 container.offset_right = -10 container.offset_bottom = -10 add_child(container) # 标题栏 var title_bar = HBoxContainer.new() container.add_child(title_bar) # 对话目标标签 target_label = Label.new() target_label.text = "对话" target_label.add_theme_font_size_override("font_size", 18) target_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL title_bar.add_child(target_label) # 关闭按钮 close_button = Button.new() close_button.text = "X" close_button.custom_minimum_size = Vector2(30, 30) close_button.mouse_filter = Control.MOUSE_FILTER_STOP # 确保按钮能接收鼠标事件 close_button.focus_mode = Control.FOCUS_ALL # 确保按钮可以获得焦点 close_button.pressed.connect(_on_close_pressed) title_bar.add_child(close_button) # 间距 container.add_child(_create_spacer(10)) # 消息显示区域(带滚动) scroll_container = ScrollContainer.new() scroll_container.custom_minimum_size = Vector2(0, 250) scroll_container.size_flags_vertical = Control.SIZE_EXPAND_FILL container.add_child(scroll_container) message_display = RichTextLabel.new() message_display.bbcode_enabled = true message_display.scroll_following = true message_display.size_flags_horizontal = Control.SIZE_EXPAND_FILL message_display.size_flags_vertical = Control.SIZE_EXPAND_FILL scroll_container.add_child(message_display) # 间距 container.add_child(_create_spacer(10)) # 输入区域 var input_container = HBoxContainer.new() container.add_child(input_container) # 消息输入框 message_input = LineEdit.new() message_input.placeholder_text = "输入消息..." message_input.size_flags_horizontal = Control.SIZE_EXPAND_FILL message_input.max_length = 500 message_input.text_submitted.connect(_on_message_submitted) # 连接焦点丢失信号,自动重新获取焦点 message_input.focus_exited.connect(_on_input_focus_exited) input_container.add_child(message_input) # 发送按钮 send_button = Button.new() send_button.text = "发送" send_button.custom_minimum_size = Vector2(80, 0) send_button.mouse_filter = Control.MOUSE_FILTER_STOP # 确保按钮能接收鼠标事件 send_button.focus_mode = Control.FOCUS_ALL # 确保按钮可以获得焦点 send_button.pressed.connect(_on_send_pressed) input_container.add_child(send_button) print("Send button created and connected") ## 创建间距 func _create_spacer(height: float) -> Control: """创建垂直间距""" var spacer = Control.new() spacer.custom_minimum_size = Vector2(0, height) return spacer ## 开始对话 func start_dialogue(target_name: String): """ 开始对话并显示对话框 @param target_name: 对话目标的名称 """ current_target_name = target_name target_label.text = "与 " + target_name + " 对话" message_display.clear() message_input.clear() show() # 延迟获取焦点,确保对话框完全显示后再获取 call_deferred("_ensure_input_focus") ## 添加消息到显示区域 func add_message(sender: String, message: String): """ 添加消息到对话显示区域 @param sender: 发送者名称 @param message: 消息内容 """ var timestamp = Time.get_time_string_from_system() var color = "[color=cyan]" if sender == "你" else "[color=yellow]" var formatted_message = "%s[%s] %s:[/color] %s\n" % [color, timestamp, sender, message] # 使用call_deferred确保UI更新不会被阻塞 message_display.call_deferred("append_text", formatted_message) # 延迟滚动到底部,确保文本已添加 call_deferred("_scroll_to_bottom") ## 关闭对话框 func close_dialogue(): """关闭对话框""" hide() message_input.clear() current_target_name = "" ## 发送按钮点击 func _on_send_pressed(): """发送按钮被点击""" print("=== SEND BUTTON PRESSED ===") if not message_input: print("ERROR: message_input is null") return var message = message_input.text.strip_edges() print("Message text: '", message, "'") if message.is_empty(): print("Empty message, focusing input") _ensure_input_focus() return print("Processing message: ", message) # 立即显示玩家消息 add_message("你", message) # 发射信号给对话系统处理 print("Emitting message_sent signal") message_sent.emit(message) # 清空输入框并重新获取焦点 message_input.clear() call_deferred("_ensure_input_focus") print("=== SEND BUTTON PROCESSING COMPLETE ===") ## 关闭按钮点击 func _on_close_pressed(): """关闭按钮被点击""" print("=== CLOSE BUTTON PRESSED ===") dialogue_closed.emit() close_dialogue() print("=== CLOSE BUTTON PROCESSING COMPLETE ===") ## 消息输入框回车 func _on_message_submitted(_text: String): """消息输入框按下回车""" print("Enter key pressed") # 调试日志 _on_send_pressed() ## 滚动到底部 func _scroll_to_bottom(): """滚动消息显示区域到底部""" if scroll_container and scroll_container.get_v_scroll_bar(): var v_scroll = scroll_container.get_v_scroll_bar() v_scroll.value = v_scroll.max_value ## 确保输入框焦点 func _ensure_input_focus(): """确保输入框获得并保持焦点""" if message_input and visible: # 使用call_deferred确保在UI更新完成后获取焦点 message_input.call_deferred("grab_focus") ## 输入框焦点丢失处理 func _on_input_focus_exited(): """当输入框失去焦点时的处理""" # 简化焦点管理,避免干扰按钮操作 # 只在特定情况下重新获取焦点 if visible and message_input: # 延迟检查,给按钮操作时间完成 await get_tree().create_timer(0.1).timeout if visible and message_input: var focused_control = get_viewport().gui_get_focus_owner() # 只有当没有任何控件获得焦点时才重新获取 if not focused_control: message_input.grab_focus()