## 界面注册面:呈现层界面元素清单的唯一事实源——哪些层、什么序、地址在哪。 ## 持有三域容器(screens/battle/modals)+模态编排件 ModalFlow 直驻(自 UiModals 升格——构造时点 ## 与今等价:注册面构造点先于装配)、模态登记唯一口 register_layer(随登记职责迁驻)、 ## LAYER_CONTRACT 合同表、node(key) 合同寻址、apply_layer_contract(root) 唯一落位口(全仓唯一 move_child 点)。 ## RefCounted 不挂树:零信号连接、零会话期引用、零核心写调用——组合根单点装配不变。 class_name UiRegistry extends RefCounted const ModalStack := preload("res://presentation/modal_stack.gd") # ==================== 视觉层合同(唯一事实源——树序=登记序=z 序;改序只改此表) ==================== # kind: shell=屏壳容器(成员随壳不占独立树位;level_screen 例外另占登记位)/modal=模态层(入登记册, # tags 随登记)/state=全屏态(占树位不入登记册)/node=非视觉节点(占树位不入登记册)。 # host=宿住屏壳(树序随宿主、登记序按本合同);shell=模态成员的屏壳(成员与屏壳同槽:屏壳占该树位、 # 成员占该登记位)。落位=apply_layer_contract(全仓唯一层序落位点,add_child 之后统一排序+登记)。 const LAYER_CONTRACT: Array[Dictionary] = [ {"key": "adventure_screen", "kind": "shell"}, {"key": "menu_screen", "kind": "shell"}, {"key": "row_pacer", "kind": "node"}, {"key": "legend_overlay", "kind": "modal", "tags": []}, {"key": "node_overlay", "kind": "modal", "tags": []}, {"key": "level_screen", "kind": "modal", "tags": [ModalStack.TAG_SCREEN_STATE], "host": "adventure_screen"}, {"key": "curio_overlay", "kind": "modal", "tags": []}, {"key": "end_overlay", "kind": "modal", "tags": []}, {"key": "shop_overlay", "kind": "modal", "tags": []}, {"key": "camp_scene", "kind": "modal", "tags": [], "shell": "camp_screen"}, # 营地场景根(交互式场景图——卡④) {"key": "forge_view", "kind": "modal", "tags": []}, # 营地四子界面(独立全屏页——场景根留底形态,卡④ T3) {"key": "treasure_view", "kind": "modal", "tags": []}, {"key": "tree_view", "kind": "modal", "tags": []}, {"key": "codex_view", "kind": "modal", "tags": []}, {"key": "camp_esc_overlay", "kind": "modal", "tags": []}, # Esc 浮窗(卡⑤——登记于营地层与设置层之间,契约 3 栈语义正解) {"key": "settings_overlay", "kind": "modal", "tags": []}, {"key": "status_panel", "kind": "modal", "tags": []}, {"key": "career_overlay", "kind": "modal", "tags": []}, {"key": "save_select_overlay", "kind": "modal", "tags": []}, # 选档界面(营地交互重做卡③——登记生涯同档) {"key": "inter_overlay", "kind": "state", "tags": []}, {"key": "confirm_dialog", "kind": "modal", "tags": []}, {"key": "debug_overlay", "kind": "modal", "tags": [ModalStack.TAG_NON_LOCKING]}, {"key": "debug_badge", "kind": "float"}, # 调试入口全局浮层——末位=最顶绘制序; # float 类不注册 ModalStack(open() 遮挡记账只隐藏登记层)→ 任何 UI 打开都不遮它 ] var screens: UiScreens var battle: UiBattle var modals: UiModals var flow: ModalFlow # 模态编排件直驻注册面(升格自 UiModals——查询/钩子 Callable 由 _build_modal_flow 注入,注入时点不变) func _init() -> void: screens = UiScreens.new() battle = UiBattle.new() modals = UiModals.new() flow = ModalFlow.new() # 模态登记唯一口(自 ui_modals.gd 逐字迁驻——登记职责随编排域归注册面单点) func register_layer(m: Control, tags: Array = []) -> void: # 撑满宿主(Control 默认锚 = 0×0——全屏变暗层与居中布局整体失效;route 选路层预览截图实证后 # 统一收口:六个模态同病,探针经信号直发绕过真实点击从未暴露。anchors_and_offsets 而非 anchors # ——后者会把控件重置回最小内容尺寸,ui_assembler 头注同款纪律) m.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) flow.register(m, tags) # 排除组随层登记(non_locking=调试浮窗/screen_state=关卡地图屏) # 合同层寻址(key→节点;纯查表不含层序语义——层序唯一事实源=LAYER_CONTRACT) func node(key: String) -> Node: match key: "adventure_screen": return screens.adventure_screen "menu_screen": return screens.menu_screen "row_pacer": return screens.row_pacer "legend_overlay": return modals.run.legend_overlay "node_overlay": return modals.run.node_overlay "level_screen": return modals.run.level_screen "curio_overlay": return modals.run.curio_overlay "end_overlay": return modals.run.end_overlay "shop_overlay": return modals.menu.shop_overlay "camp_screen": return screens.camp_screen "camp_scene": return modals.camp.scene "forge_view": return modals.camp.forge "treasure_view": return modals.camp.treasure "tree_view": return modals.camp.tree "codex_view": return modals.camp.codex "camp_esc_overlay": return modals.camp.esc_overlay "settings_overlay": return modals.menu.settings_overlay "status_panel": return modals.run.status_panel "career_overlay": return modals.menu.career_overlay "save_select_overlay": return modals.menu.save_select_overlay "inter_overlay": return screens.inter_overlay "confirm_dialog": return modals.menu.confirm_dialog "debug_overlay": return modals.menu.debug_overlay "debug_badge": return screens.debug_badge return null # 合同装配(_build_ui 尾调用):按 LAYER_CONTRACT 序统一落位——直接子层 move_child 到合同槽、 # 模态层按合同序登记;宿住层(host)树序随宿主只登记;成员层(shell)与屏壳同槽(屏壳占树位、成员占登记位)。 # 本函数是全仓唯一层序落位点(move_child 单口)——改层序只改 LAYER_CONTRACT。 func apply_layer_contract(root: Control) -> void: var slot := 0 for entry: Dictionary in LAYER_CONTRACT: var tags: Array[Variant] = entry.get("tags", []) if entry.has("shell"): var shell: Node = node(String(entry["shell"])) var member: Node = node(String(entry["key"])) if shell != null: root.move_child(shell, slot) slot += 1 if member != null: register_layer(member as Control, tags) else: push_error("[合同装配] 层缺失: %s" % String(entry["key"])) continue var layer: Node = node(String(entry["key"])) if layer == null: push_error("[合同装配] 层缺失: %s" % String(entry["key"])) continue if entry.has("host"): register_layer(layer as Control, tags) continue root.move_child(layer, slot) slot += 1 if String(entry["kind"]) == "modal": register_layer(layer as Control, tags)