extends Node # 统一处理 UI 的 Esc 退出。可关闭界面加入 whaletown_escape_dismissible 分组, # 并实现 is_escape_dismissible / get_escape_priority / request_escape_close。 const ESCAPE_DISMISSIBLE_GROUP: StringName = &"whaletown_escape_dismissible" func _ready() -> void: # 公告、排行榜等界面会暂停场景树,Esc 仍必须有效。 process_mode = Node.PROCESS_MODE_ALWAYS func _input(event: InputEvent) -> void: if not (event is InputEventKey): return var key_event: InputEventKey = event as InputEventKey if not key_event.pressed or key_event.echo or key_event.keycode != KEY_ESCAPE: return if _hide_open_popup_menu() or _close_topmost_dismissible() or _release_control_focus(): get_viewport().set_input_as_handled() func _hide_open_popup_menu() -> bool: var popup_nodes: Array[Node] = get_tree().root.find_children("*", "PopupMenu", true, false) for popup_node: Node in popup_nodes: var popup: PopupMenu = popup_node as PopupMenu if popup != null and popup.visible: popup.hide() return true return false func _close_topmost_dismissible() -> bool: var target: Node = null var target_priority: int = -2147483648 var dismissibles: Array[Node] = get_tree().get_nodes_in_group(ESCAPE_DISMISSIBLE_GROUP) for dismissible: Node in dismissibles: if not is_instance_valid(dismissible): continue if not dismissible.has_method("is_escape_dismissible") or not dismissible.has_method("request_escape_close"): continue var is_dismissible_variant: Variant = dismissible.call("is_escape_dismissible") if not bool(is_dismissible_variant): continue var priority: int = 0 if dismissible.has_method("get_escape_priority"): var priority_variant: Variant = dismissible.call("get_escape_priority") priority = int(priority_variant) if target == null or priority > target_priority: target = dismissible target_priority = priority if target == null: return false target.call("request_escape_close") return true func _release_control_focus() -> bool: var focus_owner: Control = get_viewport().gui_get_focus_owner() if focus_owner == null: return false focus_owner.release_focus() return true