refactor(ui): PR #134 review — MetaScreen/ESC chain tightening, D-192 reword

Addresses Tyre's 9 architecture items from the sprint-36 client review.

- decisions/architecture.md (Tyre #1): D-192 now says "deprecate; removal
  tracked in #868" instead of "remove". The branch does not remove the
  version field or guard — that belongs in the coordinated server+client
  PR. The decision text now matches the code on this branch.
- meta_stack.gd (#2): handle_escape() on a screen with
  closable_by_escape=false now consumes the event unconditionally. Was
  returning whatever on_escape() returned, which default-returned false
  and leaked ESC into main.gd's implant/settings chain — opening the
  settings dialog behind the loading screen.
- debug_console.gd (#4): drop the direct KEY_ESCAPE branch in
  _unhandled_input. ESC now falls through to main.gd → MetaStack, which
  finds the console on top of the stack and closes it via the normal
  path. Other keys are still consumed so movement/action can't leak.
- main.gd (#6, #10): extract the ESC priority chain into
  _handle_menu_key() so "MetaStack → implant → settings" is a named
  thing. Add a comment near connect_to_sim explaining that
  GameState.bookmark_catalog survives the Option A scene transition via
  the autoload.
- main_menu.gd (#7): header comment documenting the double LoadingScreen
  lifecycle — safe today because main_menu.tscn and main.tscn never
  co-exist, noted for future promotion to autoload if that changes.
- meta_screen.gd (#8): apply captures_input symmetrically in open()/
  close() — was set in open() only, so a screen changing the flag
  between open+close kept the opened value forever.
- meta_screen.gd (#9): on_escape() docstring clarifies the tri-state
  (consume-and-hold / consume-and-close / ignore) — and that
  closable_by_escape=false is the screen-wide way to say
  "consume-and-hold".
- bug_report_dialog.gd (#11): capture_cancelled now emits from
  on_close() (covers any close path — ESC, MetaStack pop, programmatic
  close) rather than only on_escape(). A new _completed flag
  distinguishes completion from cancel so the two signals stay
  mutually exclusive.
This commit is contained in:
2026-04-21 12:02:09 +02:00
parent effb83a0d6
commit 1b8e227413
7 changed files with 67 additions and 23 deletions
+24 -12
View File
@@ -49,6 +49,10 @@ func _ready() -> void:
# Connect to simulation (test mode sets CONNECTED immediately).
# Guard: Option A flow leaves SimBridge CONNECTED when main.tscn loads — don't drop it.
# Option A state handoff: main_menu polls and applies the snapshot first,
# seeding GameState (including bookmark_catalog) via the autoload before the
# scene swap. main.tscn then re-applies the next snapshot on top. Both paths
# write through GameState — which is autoloaded, so catalog state survives.
if SimBridge.state == SimBridge.ConnectionState.DISCONNECTED:
SimBridge.connect_to_sim()
@@ -267,19 +271,9 @@ func _process(delta: float) -> void:
elif err != OK:
push_error("main.gd: LOAD_GAME send_input failed: %s" % error_string(err))
continue
# #528: ESC/OPEN_MENU — priority chain: MetaStack modal → implant app → settings dialog
# #528: ESC/OPEN_MENU — delegate to ordered priority chain
if input.action == InputMapper.Action.OPEN_MENU:
if MetaStack.handle_escape():
continue
if HudGroups.is_implant_active():
HudGroups.close_app()
continue
if settings_dialog:
if settings_dialog.is_open():
settings_dialog.close()
else:
MetaStack.push(settings_dialog)
settings_dialog.open()
_handle_menu_key()
continue
if input.action == InputMapper.Action.INTERACT:
# D-057: prefer interaction list (multi-verb), fall back to prompt (v0.1)
@@ -314,6 +308,24 @@ func _process(delta: float) -> void:
_pending_record_inputs.clear()
# #528: ESC/OPEN_MENU priority chain — first handler to consume wins.
# Order matters: MetaStack modal > open implant app > settings dialog.
# Adding a fourth handler: append a new step here; don't re-inline in _process.
func _handle_menu_key() -> void:
if MetaStack.handle_escape():
return
if HudGroups.is_implant_active():
HudGroups.close_app()
return
if settings_dialog == null:
return
if settings_dialog.is_open():
settings_dialog.close()
else:
MetaStack.push(settings_dialog)
settings_dialog.open()
# #496: Finalize gauntlet stats on disconnect
func _on_connection_state_changed(
_old_state: SimBridge.ConnectionState, new_state: SimBridge.ConnectionState