New implant app (app_path implant/browser, key B, fullscreen), sibling
to the Atlas per Jeroen's IA ruling: kind picker -> generic filterable
index (live search-mode typing) -> generic detail, parameterized per
kind, composed entirely from D-169 components. available_in_companion
left unset (default true) — the app appears in the companion shell
automatically via the generic-host seam, zero companion-side wiring.
browser_adapter.gd is the sole home of literal wire field names: maps
Oscar's BrowseResponse contract ({id, primary, secondary} index rows;
BrowseDetail enum-as-single-key-map) to view models for all six kinds,
folding join partners (system economy/factions/culture, corporation
presence, commodity production chains with nested Leontief inputs).
browse_protocol.gd split out of protocol.gd (max-file-lines);
sim_bridge gains browse_response_received + request_browse_index/detail.
Live-data catch: a present-but-NULL key (unnamed asteroid belt
proper_name) bypasses Dictionary.get fallbacks and rendered '<null>' —
_display_or() null-vs-absent helper applied across all six detail
mappers, 4 regression tests distinct from the absent-key cases.
43 gdUnit adapter cases; full suite 3074 green. Live-verified against
a real server + real systems.db: all six kinds Ready with real row
counts (301/3240/466/165/36/28), detail drill-down, NotFound on bogus
ids. Spawn-mode DB resolution issue found during verification is
pre-existing (cwd-relative data/systems.db) — server-side fix follows
separately.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
88 lines
2.4 KiB
GDScript
88 lines
2.4 KiB
GDScript
class_name BrowserKindMenuScreen
|
|
extends Control
|
|
## Root screen for the data browser app (T-1133, D-254 §4).
|
|
## Six-entity kind picker — the browser's "reach" screen equivalent. Emits
|
|
## kind_selected when the player commits to one of the six v1 entity kinds.
|
|
|
|
signal kind_selected(kind: String)
|
|
|
|
const COLOR_BG: Color = Color("#0d1117")
|
|
const PANEL_WIDTH: float = 340.0
|
|
const PANEL_MARGIN: float = 16.0
|
|
|
|
var _selected_idx: int = 0
|
|
var _panel = null # ImplantPanel
|
|
var _rows: Array = [] # Array[ImplantDataRow], one per BrowserAdapter.KIND_ORDER entry
|
|
|
|
|
|
func _ready() -> void:
|
|
mouse_filter = Control.MOUSE_FILTER_STOP
|
|
set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
|
|
|
|
|
func _draw() -> void:
|
|
draw_rect(Rect2(Vector2.ZERO, get_rect().size), COLOR_BG)
|
|
|
|
|
|
func setup(implant_theme) -> void:
|
|
_build_panel(implant_theme)
|
|
|
|
|
|
func enter(_payload: Dictionary) -> void:
|
|
_rebuild_panel()
|
|
|
|
|
|
func leave() -> void:
|
|
pass
|
|
|
|
|
|
func navigate(delta: int) -> void:
|
|
var count: int = BrowserAdapter.KIND_ORDER.size()
|
|
_selected_idx = wrapi(_selected_idx + delta, 0, count)
|
|
_rebuild_panel()
|
|
|
|
|
|
func trigger_enter() -> void:
|
|
kind_selected.emit(BrowserAdapter.KIND_ORDER[_selected_idx])
|
|
|
|
|
|
func current_kind() -> String:
|
|
return BrowserAdapter.KIND_ORDER[_selected_idx]
|
|
|
|
|
|
# =============================================================================
|
|
# Panel
|
|
# =============================================================================
|
|
|
|
|
|
func _build_panel(implant_theme) -> void:
|
|
_panel = ImplantPanel.new()
|
|
_panel.name = "KindMenuPanel"
|
|
_panel.theme_resource = implant_theme
|
|
_panel.custom_minimum_size.x = PANEL_WIDTH
|
|
_panel.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
|
_panel.position = Vector2(PANEL_MARGIN, PANEL_MARGIN)
|
|
add_child(_panel)
|
|
_rebuild_panel()
|
|
|
|
|
|
func _rebuild_panel() -> void:
|
|
if not _panel:
|
|
return
|
|
_panel.clear()
|
|
_rows.clear()
|
|
|
|
_panel.add_component(ImplantHeader.new("DATA BROWSER", "REGISTRY INDEX"))
|
|
_panel.add_component(ImplantSeparator.new())
|
|
|
|
for i: int in range(BrowserAdapter.KIND_ORDER.size()):
|
|
var kind: String = BrowserAdapter.KIND_ORDER[i]
|
|
var label: String = BrowserAdapter.kind_label(kind)
|
|
var prefix: String = "▸ " if i == _selected_idx else " "
|
|
var row := ImplantDataRow.new(prefix + label)
|
|
_panel.add_component(row)
|
|
_rows.append(row)
|
|
|
|
_panel.add_component(ImplantSeparator.new())
|
|
_panel.add_component(ImplantTextBlock.new("↑ ↓ select enter open esc close"))
|