fix(ui): star map info panel — dynamic height measurement

Panel now measures all variable content (GTTR text, adjacent systems)
before calculating height. Fixes clipping on hub systems with many
neighbors and long GTTR excerpts. Unified separator spacing (8px).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-05 10:27:45 +02:00
co-authored by Claude Opus 4.6
parent a22bc6ce46
commit d2ea687ec3
+94 -73
View File
@@ -403,57 +403,15 @@ func _draw_info_panel(sz: Vector2) -> void:
var font := get_theme_default_font()
var panel_w: float = POPUP_WIDTH
var pad: float = POPUP_PADDING
var sep_h: float = 8.0 # separator line + spacing
var inner_w: float = panel_w - pad * 2.0
var sep_color := Color(COLOR_TEXT.r, COLOR_TEXT.g, COLOR_TEXT.b, 0.12)
# Measure GTTR text height first so we can size the panel
var gttr: String = node.get("gttr_excerpt", "")
# Strip markdown bold markers for display
gttr = gttr.replace("**", "")
var gttr_block_h: float = 0.0
if not gttr.is_empty():
var gttr_size: Vector2 = font.get_multiline_string_size(
gttr, HORIZONTAL_ALIGNMENT_LEFT, inner_w, POPUP_GTTR_FONT_SIZE,
POPUP_GTTR_MAX_LINES)
gttr_block_h = gttr_size.y + 6.0
# Fixed rows: name, id, star+hop, corridor, bodies+pop, adjacent, separator lines
var fixed_h: float = (
POPUP_LINE_H * 2.0 # name + id
+ 4.0 # separator
+ POPUP_LINE_H * 3.0 # star+hop, corridor, bodies+pop
+ 4.0 # separator
+ gttr_block_h
+ 4.0 # separator (before adjacent)
+ POPUP_LINE_H # adjacent systems label
+ pad * 2.0
)
var panel_h: float = fixed_h
var panel_pos := Vector2(sz.x - panel_w - POPUP_MARGIN, POPUP_MARGIN)
var x: float = panel_pos.x + pad
var y: float = panel_pos.y + pad + POPUP_LINE_H # baseline offset
# Background + border
draw_rect(Rect2(panel_pos, Vector2(panel_w, panel_h)), COLOR_INFO_BG)
draw_rect(Rect2(panel_pos, Vector2(panel_w, panel_h)),
Color(COLOR_TEXT.r, COLOR_TEXT.g, COLOR_TEXT.b, 0.15), false, 1.0)
# ── System name ──────────────────────────────────────────────────────────
# ── Prepare all text content before measuring ────────────────────────────
var sys_name: String = node.get("proper_name", "")
if sys_name.is_empty():
sys_name = node.get("system_id", "Unknown")
draw_string(font, Vector2(x, y), sys_name, HORIZONTAL_ALIGNMENT_LEFT, inner_w, 15, COLOR_TEXT)
y += POPUP_LINE_H
draw_string(font, Vector2(x, y), node.get("system_id", ""), HORIZONTAL_ALIGNMENT_LEFT, inner_w, 10, COLOR_TEXT_DIM)
y += POPUP_LINE_H
# Separator
draw_line(Vector2(x, y + 1.0), Vector2(panel_pos.x + panel_w - pad, y + 1.0),
Color(COLOR_TEXT.r, COLOR_TEXT.g, COLOR_TEXT.b, 0.12), 1.0)
y += 6.0
# ── Stats block ──────────────────────────────────────────────────────────
var star_type: String = node.get("star_type", "")
var hop: int = int(node.get("hop_distance", 0))
var stat_line_1: String
@@ -461,13 +419,9 @@ func _draw_info_panel(sz: Vector2) -> void:
stat_line_1 = "Hop %d from Gateway" % hop
else:
stat_line_1 = "%s star · hop %d" % [star_type, hop]
draw_string(font, Vector2(x, y), stat_line_1, HORIZONTAL_ALIGNMENT_LEFT, inner_w, 11, COLOR_TEXT_DIM)
y += POPUP_LINE_H
var sector_str: String = node.get("geographic_sector", "unknown").replace("_", " ").to_upper()
var sector_color: Color = SECTOR_COLORS.get(node.get("geographic_sector", ""), COLOR_TEXT_DIM)
draw_string(font, Vector2(x, y), sector_str + " corridor", HORIZONTAL_ALIGNMENT_LEFT, inner_w, 11, sector_color)
y += POPUP_LINE_H
var bodies: String = node.get("bodies", "")
var population: String = node.get("population", "")
@@ -479,39 +433,106 @@ func _draw_info_panel(sz: Vector2) -> void:
elif not population.is_empty():
stat_line_3 = "Population: " + population
else:
stat_line_3 = "%d aperture%s · %s" % [
stat_line_3 = "%d aperture%s" % [
int(node.get("aperture_count", 0)),
"s" if int(node.get("aperture_count", 0)) != 1 else "",
node.get("gate_topology", "")]
draw_string(font, Vector2(x, y), stat_line_3, HORIZONTAL_ALIGNMENT_LEFT, inner_w, 11, COLOR_TEXT_DIM)
y += POPUP_LINE_H
"s" if int(node.get("aperture_count", 0)) != 1 else ""]
# ── GTTR excerpt ─────────────────────────────────────────────────────────
if not gttr.is_empty():
draw_line(Vector2(x, y + 1.0), Vector2(panel_pos.x + panel_w - pad, y + 1.0),
Color(COLOR_TEXT.r, COLOR_TEXT.g, COLOR_TEXT.b, 0.12), 1.0)
y += 6.0
draw_multiline_string(font, Vector2(x, y), gttr, HORIZONTAL_ALIGNMENT_LEFT,
inner_w, POPUP_GTTR_FONT_SIZE, POPUP_GTTR_MAX_LINES,
Color(COLOR_TEXT.r, COLOR_TEXT.g, COLOR_TEXT.b, 0.75))
y += gttr_block_h
var gttr: String = node.get("gttr_excerpt", "").replace("**", "")
# ── Adjacent systems ──────────────────────────────────────────────────────
draw_line(Vector2(x, y + 1.0), Vector2(panel_pos.x + panel_w - pad, y + 1.0),
Color(COLOR_TEXT.r, COLOR_TEXT.g, COLOR_TEXT.b, 0.12), 1.0)
y += 6.0
var adj: Array = node.get("adjacent_systems", [])
if adj.is_empty():
draw_string(font, Vector2(x, y), "No gate connections", HORIZONTAL_ALIGNMENT_LEFT, inner_w, 10, COLOR_TEXT_DIM)
else:
var adj_line: String = ""
if not adj.is_empty():
var adj_names: Array = []
for neighbor_id: String in adj:
var neighbor: Dictionary = _node_lookup.get(neighbor_id, {})
var n_name: String = neighbor.get("proper_name", "")
adj_names.append(n_name if not n_name.is_empty() else neighbor_id)
var adj_line: String = " · ".join(adj_names)
draw_multiline_string(font, Vector2(x, y), adj_line, HORIZONTAL_ALIGNMENT_LEFT,
inner_w, 10, 2, COLOR_TEXT_DIM)
adj_line = " · ".join(adj_names)
# ── Measure variable-height blocks ───────────────────────────────────────
var gttr_h: float = 0.0
if not gttr.is_empty():
gttr_h = font.get_multiline_string_size(
gttr, HORIZONTAL_ALIGNMENT_LEFT, inner_w,
POPUP_GTTR_FONT_SIZE, POPUP_GTTR_MAX_LINES).y
var adj_h: float = POPUP_LINE_H # minimum one line
if not adj_line.is_empty():
adj_h = font.get_multiline_string_size(
adj_line, HORIZONTAL_ALIGNMENT_LEFT, inner_w, 10, 3).y
# ── Calculate total panel height ─────────────────────────────────────────
var panel_h: float = pad # top padding
panel_h += POPUP_LINE_H # system name (15px font)
panel_h += POPUP_LINE_H # system id (10px font)
panel_h += sep_h # separator
panel_h += POPUP_LINE_H # star type + hop
panel_h += POPUP_LINE_H # corridor
panel_h += POPUP_LINE_H # bodies + pop
if not gttr.is_empty():
panel_h += sep_h + gttr_h
panel_h += sep_h + adj_h # adjacent systems
panel_h += pad # bottom padding
# ── Draw background ──────────────────────────────────────────────────────
var panel_pos := Vector2(sz.x - panel_w - POPUP_MARGIN, POPUP_MARGIN)
var x: float = panel_pos.x + pad
var x_right: float = panel_pos.x + panel_w - pad
var y: float = panel_pos.y + pad + POPUP_LINE_H # baseline offset
draw_rect(Rect2(panel_pos, Vector2(panel_w, panel_h)), COLOR_INFO_BG)
draw_rect(Rect2(panel_pos, Vector2(panel_w, panel_h)), sep_color, false, 1.0)
# ── Draw content ─────────────────────────────────────────────────────────
# System name
draw_string(font, Vector2(x, y), sys_name,
HORIZONTAL_ALIGNMENT_LEFT, inner_w, 15, COLOR_TEXT)
y += POPUP_LINE_H
# System ID
draw_string(font, Vector2(x, y), node.get("system_id", ""),
HORIZONTAL_ALIGNMENT_LEFT, inner_w, 10, COLOR_TEXT_DIM)
y += POPUP_LINE_H
# Separator
draw_line(Vector2(x, y + 2.0), Vector2(x_right, y + 2.0), sep_color, 1.0)
y += sep_h
# Star type + hop
draw_string(font, Vector2(x, y), stat_line_1,
HORIZONTAL_ALIGNMENT_LEFT, inner_w, 11, COLOR_TEXT_DIM)
y += POPUP_LINE_H
# Corridor
draw_string(font, Vector2(x, y), sector_str + " corridor",
HORIZONTAL_ALIGNMENT_LEFT, inner_w, 11, sector_color)
y += POPUP_LINE_H
# Bodies + population
draw_string(font, Vector2(x, y), stat_line_3,
HORIZONTAL_ALIGNMENT_LEFT, inner_w, 11, COLOR_TEXT_DIM)
y += POPUP_LINE_H
# GTTR excerpt
if not gttr.is_empty():
draw_line(Vector2(x, y + 2.0), Vector2(x_right, y + 2.0), sep_color, 1.0)
y += sep_h
draw_multiline_string(font, Vector2(x, y), gttr,
HORIZONTAL_ALIGNMENT_LEFT, inner_w, POPUP_GTTR_FONT_SIZE,
POPUP_GTTR_MAX_LINES,
Color(COLOR_TEXT.r, COLOR_TEXT.g, COLOR_TEXT.b, 0.75))
y += gttr_h
# Adjacent systems
draw_line(Vector2(x, y + 2.0), Vector2(x_right, y + 2.0), sep_color, 1.0)
y += sep_h
if adj_line.is_empty():
draw_string(font, Vector2(x, y), "No gate connections",
HORIZONTAL_ALIGNMENT_LEFT, inner_w, 10, COLOR_TEXT_DIM)
else:
draw_multiline_string(font, Vector2(x, y), adj_line,
HORIZONTAL_ALIGNMENT_LEFT, inner_w, 10, 3, COLOR_TEXT_DIM)
func _draw_title() -> void: