Files
settled-reach/client/ui/implant/atlas_overlay_bar.gd
T
jpmschweitzer f8b81e1fe7 refactor(ui): atlas review warnings — headers, public API, KEY_N, overlay source of truth (#128)
4. AtlasPanel and AtlasViewer now compose their title + hint from an
   ImplantHeader child rather than hand-rolling them via draw_string, so the
   D-169 "theme swap changes the implant hardware appearance" invariant
   holds end-to-end. _refresh_screen_header() drives content per level and
   on system navigation.

5. AtlasViewer exposes city_canvas_pos(), get_hovered_city(),
   get_selected_city(), and get_overlay_defs() as public API — the marker
   overlay no longer reaches into underscore-prefixed state, which is
   especially important because viewer is an untyped var in the overlay.

6. KEY_N now consumes unconditionally while the viewer is visible, and
   main.gd's global economics-monitor toggle is gated on
   !HudGroups.is_app_active("implant/map/atlas"). Previously pressing N
   without a selected city fell through and closed the fullscreen atlas as
   a side effect.

7. OVERLAY_DEFS lives in AtlasViewer as the single source of truth.
   AtlasOverlayBar reads the list via viewer.get_overlay_defs(), and
   AtlasViewer derives _overlay_visibility / _overlay_locked from the same
   table at _ready() — no more hand-maintained parallel lists, so the bar
   and the guard in set_overlay_visible can't drift.

8. AtlasOverlayBar drops `class_name`: it now loads via
   load("res://ui/implant/atlas_overlay_bar.gd") from AtlasViewer, the same
   pattern AtlasPanel uses for AtlasViewer. _init(viewer_ref = null) keeps
   the required-arg footgun off the editor's introspection path.

9. `star-map-data` make target added to regenerate
   client/data/star_map_data.json from systems.db + wiki, and
   `check-star-map` wired into pre-pr-validate + pre-pr-client so any
   commit that touches the generator (or any downstream systems.db change
   like server #839) fails pre-pr until the JSON is regenerated. The
   terrain_reference data-availability dependency is no longer tribal
   knowledge.

Also addresses review #15 (push_warning on unknown overlay id in
set_overlay_visible) and #16 (disabled always-on buttons drop handler
churn) as part of the same refactor.
2026-04-15 08:59:21 +02:00

78 lines
2.7 KiB
GDScript

extends HBoxContainer
## Overlay toggle bar for #835 AtlasViewer — nine MVP overlays plus two locked
## layers (#836, D-191 §7, D-181 signal visibility ladder).
##
## This script has no `class_name` on purpose: the owner (AtlasViewer) needs to
## pass the viewer reference to _init() and a `class_name` + required-arg
## _init() combo is a Godot editor footgun (review #8). Instance it via
## load("res://ui/implant/atlas_overlay_bar.gd").new(self) like AtlasViewer
## itself is instanced from AtlasPanel.
##
## Layout: [ALWAYS-ON] [TOGGLEABLE] [LOCKED]. Each row in viewer.get_overlay_defs()
## produces exactly one button, so adding or retiring an overlay is a one-file
## change — the bar and marker renderer stay in lockstep (review #7).
const COLOR_PINNED: Color = Color("#c8d0e0")
const COLOR_ACTIVE: Color = Color("#f0d060")
const COLOR_INACTIVE: Color = Color("#556677")
const COLOR_LOCKED: Color = Color("#3a4a55")
var _viewer = null
var _buttons: Dictionary = {} # overlay_id -> Button
func _init(viewer_ref = null) -> void:
_viewer = viewer_ref
add_theme_constant_override("separation", 4)
func _ready() -> void:
mouse_filter = Control.MOUSE_FILTER_STOP
if _viewer == null:
return
for def: Dictionary in _viewer.get_overlay_defs():
var b: Button = Button.new()
b.text = def["label"]
b.tooltip_text = def["tooltip"]
b.toggle_mode = (def["group"] != "always")
b.focus_mode = Control.FOCUS_NONE
b.custom_minimum_size = Vector2(42.0, 24.0)
b.add_theme_font_size_override("font_size", 10)
match def["group"]:
"always":
# Disabled + pressed = visually pinned, no click handler churn
# (review #16). Theme font_disabled_color is overridden so the
# button still reads as "on" rather than greyed-out.
b.disabled = true
b.button_pressed = true
b.add_theme_color_override("font_disabled_color", COLOR_PINNED)
"toggle":
b.button_pressed = _viewer.is_overlay_visible(def["id"])
b.add_theme_color_override("font_color", COLOR_INACTIVE)
b.add_theme_color_override("font_pressed_color", COLOR_ACTIVE)
b.toggled.connect(_on_toggle_changed.bind(def["id"]))
"locked":
b.disabled = true
b.button_pressed = false
b.add_theme_color_override("font_disabled_color", COLOR_LOCKED)
add_child(b)
_buttons[def["id"]] = b
func _on_toggle_changed(pressed: bool, overlay_id: String) -> void:
if _viewer:
_viewer.set_overlay_visible(overlay_id, pressed)
func sync_from_viewer() -> void:
if _viewer == null:
return
for def: Dictionary in _viewer.get_overlay_defs():
if def["group"] != "toggle":
continue
var b: Button = _buttons.get(def["id"])
if b:
b.set_pressed_no_signal(_viewer.is_overlay_visible(def["id"]))