Files
settled-reach/client/ui/implant/implant_nav_stack.gd
T
jpmschweitzerandClaude Opus 4.6 ea7bcfabfb fix(ui): PR #131 review round 3 — nits sweep
Seven nit-level fixes from PR review:

- Remove dead signal insert_deactivated from ImplantApp; the hook method
  on_insert_deactivated() is the actual contract.
- Rewrite _on_atlas_economics_link comment in main.gd to reflect the
  actual flow (AtlasApp closes as a consequence of HudGroups single-
  active-app, not before emitting anything).
- Document the "pop never empties below default" invariant on
  ImplantNavStack.pop() with a pointer to reset_to_default.
- Add _mutating re-entrancy guard on ImplantNavStack mutation methods.
  push_error + early return if called during a screen_changed emission.
- main.gd registry loop now uses typed ImplantAppManifest property
  access (manifest.app_path, manifest.default_key) instead of
  dictionary-style .get() calls. Empty app_path triggers push_warning.
- Document the economics [/] hotkey exception in main.gd and reference
  the planned handle_global_key lifecycle hook. Arch doc Follow-up
  section gains a bullet for the new hook.
- Comment the independent-version-read rationale above client_ver and
  proto_ver in loading_screen.gd.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-19 14:47:05 +02:00

94 lines
2.3 KiB
GDScript

class_name ImplantNavStack
extends Node
## Intra-app navigation stack for ImplantApp subclasses (#844, D-191).
## Mutation is synchronous: screen_changed fires before push/pop/replace returns.
signal screen_changed(current_screen_id: String)
var _stack: Array[String] = []
var _payloads: Array[Dictionary] = []
var _default_screen_id: String = ""
var _mutating: bool = false # re-entrancy guard: set while screen_changed is emitting
func set_default(screen_id: String) -> void:
_default_screen_id = screen_id
func push(screen_id: String, payload: Dictionary = {}) -> void:
if _mutating:
push_error(
"ImplantNavStack: nested mutation detected — use call_deferred from screen_changed handler"
)
return
_stack.append(screen_id)
_payloads.append(payload)
_mutating = true
screen_changed.emit(screen_id)
_mutating = false
# pop() never empties the stack below the default screen — this app always
# has at least one screen visible. To truly reset, use reset_to_default().
func pop() -> void:
if _mutating:
push_error(
"ImplantNavStack: nested mutation detected — use call_deferred from screen_changed handler"
)
return
if _stack.is_empty():
push_warning("ImplantNavStack: pop() on empty stack")
return
_stack.pop_back()
_payloads.pop_back()
if not _stack.is_empty():
_mutating = true
screen_changed.emit(_stack.back())
_mutating = false
elif not _default_screen_id.is_empty():
push(_default_screen_id)
func replace(screen_id: String, payload: Dictionary = {}) -> void:
if _mutating:
push_error(
"ImplantNavStack: nested mutation detected — use call_deferred from screen_changed handler"
)
return
if not _stack.is_empty():
_stack.pop_back()
_payloads.pop_back()
_stack.append(screen_id)
_payloads.append(payload)
_mutating = true
screen_changed.emit(screen_id)
_mutating = false
func reset_to_default() -> void:
_stack.clear()
_payloads.clear()
if not _default_screen_id.is_empty():
push(_default_screen_id)
func is_empty() -> bool:
return _stack.is_empty()
func push_default() -> void:
if not _default_screen_id.is_empty():
push(_default_screen_id)
func current() -> String:
if _stack.is_empty():
return ""
return _stack.back()
func current_payload() -> Dictionary:
if _payloads.is_empty():
return {}
return _payloads.back()