feat(ui): implant component library + star map refactor (D-169)
New component library at client/ui/implant/: - ImplantTheme: shared Resource with colors, spacing, font sizes - ImplantPanel: PanelContainer root with themed background/border - ImplantHeader: title + subtitle - ImplantSeparator: themed horizontal rule with above/below spacing - ImplantDataRow: single-line text with optional color override - ImplantTextBlock: RichTextLabel for wrapping narrative text - default_implant.tres: default theme resource Star map info panel refactored from 140 lines of manual draw_string calls to 50 lines of component composition via _rebuild_info_panel(). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
[gd_resource type="Resource" script_class="ImplantTheme" load_steps=2 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://ui/implant/implant_theme.gd" id="1"]
|
||||
|
||||
[resource]
|
||||
script = ExtResource("1")
|
||||
text_primary = Color(0.784, 0.816, 0.878, 1)
|
||||
text_dim = Color(0.4, 0.467, 0.533, 1)
|
||||
accent_active = Color(0.941, 0.816, 0.376, 1)
|
||||
accent_positive = Color(0.267, 0.667, 0.4, 1)
|
||||
accent_negative = Color(0.667, 0.267, 0.267, 1)
|
||||
accent_warning = Color(0.667, 0.533, 0.267, 1)
|
||||
panel_bg = Color(0.05, 0.08, 0.14, 0.92)
|
||||
separator = Color(0.78, 0.82, 0.88, 0.12)
|
||||
hover_bg = Color(0.78, 0.82, 0.88, 0.08)
|
||||
line_height = 18
|
||||
sep_above = 6
|
||||
sep_below = 12
|
||||
padding = 12
|
||||
border_width = 1.0
|
||||
font_header = 15
|
||||
font_body = 11
|
||||
font_small = 10
|
||||
font_caption = 9
|
||||
@@ -0,0 +1,28 @@
|
||||
@tool
|
||||
class_name ImplantDataRow
|
||||
extends Label
|
||||
## Single-line data display — text with optional color override.
|
||||
|
||||
var _color_override: Color = Color.TRANSPARENT # transparent = use theme default
|
||||
|
||||
|
||||
func _init(text: String = "", color: Color = Color.TRANSPARENT) -> void:
|
||||
self.text = text
|
||||
_color_override = color
|
||||
mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
|
||||
|
||||
func set_content(text_value: String, color: Color = Color.TRANSPARENT) -> void:
|
||||
text = text_value
|
||||
_color_override = color
|
||||
if _color_override.a > 0.0:
|
||||
add_theme_color_override("font_color", _color_override)
|
||||
|
||||
|
||||
func apply_implant_theme(t: ImplantTheme) -> void:
|
||||
add_theme_font_size_override("font_size", t.font_body)
|
||||
if _color_override.a > 0.0:
|
||||
add_theme_color_override("font_color", _color_override)
|
||||
else:
|
||||
add_theme_color_override("font_color", t.text_dim)
|
||||
custom_minimum_size.y = t.line_height
|
||||
@@ -0,0 +1,37 @@
|
||||
@tool
|
||||
class_name ImplantHeader
|
||||
extends VBoxContainer
|
||||
## Panel header — title + optional subtitle.
|
||||
|
||||
var _title_label: Label
|
||||
var _subtitle_label: Label
|
||||
|
||||
|
||||
func _init(title: String = "", subtitle: String = "") -> void:
|
||||
_title_label = Label.new()
|
||||
_title_label.text = title
|
||||
_title_label.name = "Title"
|
||||
|
||||
_subtitle_label = Label.new()
|
||||
_subtitle_label.text = subtitle
|
||||
_subtitle_label.name = "Subtitle"
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
add_theme_constant_override("separation", 0)
|
||||
add_child(_title_label)
|
||||
add_child(_subtitle_label)
|
||||
|
||||
|
||||
func set_content(title: String, subtitle: String = "") -> void:
|
||||
_title_label.text = title
|
||||
_subtitle_label.text = subtitle
|
||||
_subtitle_label.visible = not subtitle.is_empty()
|
||||
|
||||
|
||||
func apply_implant_theme(t: ImplantTheme) -> void:
|
||||
_title_label.add_theme_font_size_override("font_size", t.font_header)
|
||||
_title_label.add_theme_color_override("font_color", t.text_primary)
|
||||
|
||||
_subtitle_label.add_theme_font_size_override("font_size", t.font_small)
|
||||
_subtitle_label.add_theme_color_override("font_color", t.text_dim)
|
||||
@@ -0,0 +1,74 @@
|
||||
@tool
|
||||
class_name ImplantPanel
|
||||
extends PanelContainer
|
||||
## Root container for any implant UI overlay (D-169).
|
||||
## Add ImplantHeader, ImplantSeparator, ImplantDataRow, ImplantTextBlock
|
||||
## as children — they auto-style from the shared theme.
|
||||
|
||||
@export var theme_resource: ImplantTheme:
|
||||
set(value):
|
||||
theme_resource = value
|
||||
_apply_theme()
|
||||
|
||||
var _vbox: VBoxContainer
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
_vbox = VBoxContainer.new()
|
||||
_vbox.name = "Content"
|
||||
add_child(_vbox)
|
||||
_apply_theme()
|
||||
|
||||
|
||||
func _apply_theme() -> void:
|
||||
if not theme_resource:
|
||||
return
|
||||
|
||||
# Panel background
|
||||
var style := StyleBoxFlat.new()
|
||||
style.bg_color = theme_resource.panel_bg
|
||||
style.border_color = theme_resource.separator
|
||||
style.border_width_top = int(theme_resource.border_width)
|
||||
style.border_width_bottom = int(theme_resource.border_width)
|
||||
style.border_width_left = int(theme_resource.border_width)
|
||||
style.border_width_right = int(theme_resource.border_width)
|
||||
style.content_margin_top = theme_resource.padding
|
||||
style.content_margin_bottom = theme_resource.padding
|
||||
style.content_margin_left = theme_resource.padding
|
||||
style.content_margin_right = theme_resource.padding
|
||||
add_theme_stylebox_override("panel", style)
|
||||
|
||||
# VBox spacing
|
||||
if _vbox:
|
||||
_vbox.add_theme_constant_override("separation", 0)
|
||||
|
||||
# Propagate theme to existing children
|
||||
for child in get_implant_children():
|
||||
if child.has_method("apply_implant_theme"):
|
||||
child.apply_implant_theme(theme_resource)
|
||||
|
||||
|
||||
## Add an implant component to the panel.
|
||||
func add_component(component: Control) -> void:
|
||||
if not _vbox:
|
||||
_vbox = VBoxContainer.new()
|
||||
_vbox.name = "Content"
|
||||
add_child(_vbox)
|
||||
_vbox.add_child(component)
|
||||
if theme_resource and component.has_method("apply_implant_theme"):
|
||||
component.apply_implant_theme(theme_resource)
|
||||
|
||||
|
||||
## Get all implant component children.
|
||||
func get_implant_children() -> Array:
|
||||
if not _vbox:
|
||||
return []
|
||||
return _vbox.get_children()
|
||||
|
||||
|
||||
## Clear all components.
|
||||
func clear() -> void:
|
||||
if not _vbox:
|
||||
return
|
||||
for child in _vbox.get_children():
|
||||
child.queue_free()
|
||||
@@ -0,0 +1,28 @@
|
||||
@tool
|
||||
class_name ImplantSeparator
|
||||
extends Control
|
||||
## Thin horizontal separator line with themed spacing.
|
||||
|
||||
var _theme: ImplantTheme
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
|
||||
|
||||
func apply_implant_theme(t: ImplantTheme) -> void:
|
||||
_theme = t
|
||||
custom_minimum_size.y = t.sep_above + t.sep_below
|
||||
queue_redraw()
|
||||
|
||||
|
||||
func _draw() -> void:
|
||||
if not _theme:
|
||||
return
|
||||
var y: float = _theme.sep_above
|
||||
draw_line(
|
||||
Vector2(0, y),
|
||||
Vector2(size.x, y),
|
||||
_theme.separator,
|
||||
1.0
|
||||
)
|
||||
@@ -0,0 +1,32 @@
|
||||
@tool
|
||||
class_name ImplantTextBlock
|
||||
extends RichTextLabel
|
||||
## Multi-line wrapping text block for narrative content (GTTR excerpts, etc.).
|
||||
|
||||
var _max_lines: int = -1
|
||||
var _text_alpha: float = 0.75
|
||||
|
||||
|
||||
func _init(content: String = "", max_lines: int = -1) -> void:
|
||||
_max_lines = max_lines
|
||||
text = content
|
||||
bbcode_enabled = false
|
||||
fit_content = true
|
||||
scroll_active = false
|
||||
mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||
if max_lines > 0:
|
||||
visible_characters_behavior = TextServer.VC_GLYPHS_LTR
|
||||
# Godot 4 RichTextLabel doesn't have max_lines directly —
|
||||
# we limit via visible_ratio or custom_minimum_size after layout.
|
||||
|
||||
|
||||
func set_content(content: String, max_lines: int = -1) -> void:
|
||||
text = content.replace("**", "") # strip markdown bold
|
||||
_max_lines = max_lines
|
||||
|
||||
|
||||
func apply_implant_theme(t: ImplantTheme) -> void:
|
||||
add_theme_font_size_override("normal_font_size", t.font_small)
|
||||
add_theme_color_override("default_color",
|
||||
Color(t.text_primary.r, t.text_primary.g, t.text_primary.b, _text_alpha))
|
||||
@@ -0,0 +1,46 @@
|
||||
class_name ImplantTheme
|
||||
extends Resource
|
||||
## Shared styling resource for all implant UI panels (D-169).
|
||||
## Swap this resource at runtime to change the implant's visual identity
|
||||
## (hardware variants, upgrades, faction overlays).
|
||||
|
||||
# ── Colors ───────────────────────────────────────────────────────────────────
|
||||
|
||||
## Primary readable text
|
||||
@export var text_primary: Color = Color("#c8d0e0")
|
||||
## Labels, metadata, captions
|
||||
@export var text_dim: Color = Color("#667788")
|
||||
## Selected, active, interactive highlight
|
||||
@export var accent_active: Color = Color("#f0d060")
|
||||
## Good state, opportunity, profit
|
||||
@export var accent_positive: Color = Color("#44aa66")
|
||||
## Danger, loss, hostile
|
||||
@export var accent_negative: Color = Color("#aa4444")
|
||||
## Caution, degraded, unknown
|
||||
@export var accent_warning: Color = Color("#aa8844")
|
||||
## Panel background
|
||||
@export var panel_bg: Color = Color(0.05, 0.08, 0.14, 0.92)
|
||||
## Panel border and separator lines
|
||||
@export var separator: Color = Color(0.78, 0.82, 0.88, 0.12)
|
||||
## Hover state background
|
||||
@export var hover_bg: Color = Color(0.78, 0.82, 0.88, 0.08)
|
||||
|
||||
# ── Spacing ──────────────────────────────────────────────────────────────────
|
||||
|
||||
## Line height for single-line labels (baseline to baseline)
|
||||
@export var line_height: int = 18
|
||||
## Space from last text baseline to separator line
|
||||
@export var sep_above: int = 6
|
||||
## Space from separator line to next text baseline
|
||||
@export var sep_below: int = 12
|
||||
## Panel internal padding
|
||||
@export var padding: int = 12
|
||||
## Border width
|
||||
@export var border_width: float = 1.0
|
||||
|
||||
# ── Font sizes ───────────────────────────────────────────────────────────────
|
||||
|
||||
@export var font_header: int = 15
|
||||
@export var font_body: int = 11
|
||||
@export var font_small: int = 10
|
||||
@export var font_caption: int = 9
|
||||
+44
-109
@@ -35,12 +35,9 @@ const HIT_RADIUS: float = 10.0 # click tolerance
|
||||
const EDGE_WIDTH: float = 0.8
|
||||
const EDGE_SELECTED_ALPHA: float = 0.55
|
||||
|
||||
# Info popup — expanded with wiki/GTTR content (#780)
|
||||
# Info popup — uses ImplantPanel component library (D-169)
|
||||
const POPUP_WIDTH: float = 300.0
|
||||
const POPUP_MARGIN: float = 16.0
|
||||
const POPUP_PADDING: float = 12.0
|
||||
const POPUP_LINE_H: float = 17.0
|
||||
const POPUP_GTTR_FONT_SIZE: int = 10
|
||||
const POPUP_GTTR_MAX_LINES: int = 7
|
||||
|
||||
# Colors — sector palette from wireframe
|
||||
@@ -51,7 +48,6 @@ const COLOR_GATEWAY: Color = Color("#f0d060")
|
||||
const COLOR_SELECTION: Color = Color("#f0d060")
|
||||
const COLOR_TEXT: Color = Color("#c8d0e0")
|
||||
const COLOR_TEXT_DIM: Color = Color("#667788")
|
||||
const COLOR_INFO_BG: Color = Color(0.05, 0.08, 0.14, 0.92)
|
||||
|
||||
const SECTOR_COLORS: Dictionary = {
|
||||
"core": Color("#c8d0e0"),
|
||||
@@ -104,10 +100,23 @@ var _pan_start_offset: Vector2 = Vector2.ZERO
|
||||
var _data_loaded: bool = false
|
||||
var _insert_active: bool = true
|
||||
var _dirty: bool = true # redraw needed — set by state changes, cleared after _draw
|
||||
var _info_panel: ImplantPanel # D-169: component-based info panel
|
||||
var _implant_theme: ImplantTheme
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
mouse_filter = Control.MOUSE_FILTER_STOP
|
||||
|
||||
# D-169: Load implant theme and create info panel
|
||||
_implant_theme = load("res://ui/implant/default_implant.tres") as ImplantTheme
|
||||
_info_panel = ImplantPanel.new()
|
||||
_info_panel.name = "InfoPanel"
|
||||
_info_panel.theme_resource = _implant_theme
|
||||
_info_panel.custom_minimum_size.x = POPUP_WIDTH
|
||||
_info_panel.visible = false
|
||||
_info_panel.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
add_child(_info_panel)
|
||||
|
||||
_load_data()
|
||||
if _data_loaded:
|
||||
_compute_layout()
|
||||
@@ -307,9 +316,12 @@ func _draw() -> void:
|
||||
if _selected_system != "":
|
||||
_draw_selection(center)
|
||||
|
||||
# Info panel for selected system
|
||||
if _selected_system != "":
|
||||
_draw_info_panel(sz)
|
||||
# Info panel positioned in top-right (D-169 component panel)
|
||||
if _info_panel:
|
||||
_info_panel.visible = _selected_system != ""
|
||||
if _info_panel.visible:
|
||||
_info_panel.position = Vector2(
|
||||
sz.x - POPUP_WIDTH - POPUP_MARGIN, POPUP_MARGIN)
|
||||
|
||||
# Title
|
||||
_draw_title()
|
||||
@@ -395,25 +407,26 @@ func _draw_selection(center: Vector2) -> void:
|
||||
HORIZONTAL_ALIGNMENT_LEFT, -1, 12, COLOR_SELECTION)
|
||||
|
||||
|
||||
func _draw_info_panel(sz: Vector2) -> void:
|
||||
## Rebuild the info panel with components for the selected system (D-169).
|
||||
func _rebuild_info_panel() -> void:
|
||||
if not _info_panel:
|
||||
return
|
||||
_info_panel.clear()
|
||||
|
||||
var node: Dictionary = _node_lookup.get(_selected_system, {})
|
||||
if node.is_empty():
|
||||
_info_panel.visible = false
|
||||
return
|
||||
|
||||
var font := get_theme_default_font()
|
||||
var panel_w: float = POPUP_WIDTH
|
||||
var pad: float = POPUP_PADDING
|
||||
var line_h: float = 18.0 # line height for single-line text (baseline to baseline)
|
||||
var sep_above: float = 6.0 # space from last text baseline to separator line
|
||||
var sep_below: float = 12.0 # space from separator line to next text baseline
|
||||
var inner_w: float = panel_w - pad * 2.0
|
||||
var sep_color := Color(COLOR_TEXT.r, COLOR_TEXT.g, COLOR_TEXT.b, 0.12)
|
||||
|
||||
# ── Prepare all text content before measuring ────────────────────────────
|
||||
# ── Header ───────────────────────────────────────────────────────────────
|
||||
var sys_name: String = node.get("proper_name", "")
|
||||
if sys_name.is_empty():
|
||||
sys_name = node.get("system_id", "Unknown")
|
||||
_info_panel.add_component(ImplantHeader.new(sys_name, node.get("system_id", "")))
|
||||
|
||||
_info_panel.add_component(ImplantSeparator.new())
|
||||
|
||||
# ── Stats ────────────────────────────────────────────────────────────────
|
||||
var star_type: String = node.get("star_type", "")
|
||||
var hop: int = int(node.get("hop_distance", 0))
|
||||
var stat_line_1: String
|
||||
@@ -421,9 +434,11 @@ 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]
|
||||
_info_panel.add_component(ImplantDataRow.new(stat_line_1))
|
||||
|
||||
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)
|
||||
_info_panel.add_component(ImplantDataRow.new(sector_str + " corridor", sector_color))
|
||||
|
||||
var bodies: String = node.get("bodies", "")
|
||||
var population: String = node.get("population", "")
|
||||
@@ -438,105 +453,24 @@ func _draw_info_panel(sz: Vector2) -> void:
|
||||
stat_line_3 = "%d aperture%s" % [
|
||||
int(node.get("aperture_count", 0)),
|
||||
"s" if int(node.get("aperture_count", 0)) != 1 else ""]
|
||||
_info_panel.add_component(ImplantDataRow.new(stat_line_3))
|
||||
|
||||
var gttr: String = node.get("gttr_excerpt", "").replace("**", "")
|
||||
# ── GTTR excerpt ─────────────────────────────────────────────────────────
|
||||
var gttr: String = node.get("gttr_excerpt", "")
|
||||
if not gttr.is_empty():
|
||||
_info_panel.add_component(ImplantSeparator.new())
|
||||
_info_panel.add_component(ImplantTextBlock.new(gttr, POPUP_GTTR_MAX_LINES))
|
||||
|
||||
# ── Adjacent systems ─────────────────────────────────────────────────────
|
||||
var adj: Array = node.get("adjacent_systems", [])
|
||||
var adj_line: String = ""
|
||||
if not adj.is_empty():
|
||||
_info_panel.add_component(ImplantSeparator.new())
|
||||
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)
|
||||
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 sep_total: float = sep_above + sep_below # full separator block height
|
||||
var panel_h: float = pad # top padding
|
||||
panel_h += line_h # system name (15px font)
|
||||
panel_h += line_h # system id (10px font)
|
||||
panel_h += sep_total # separator
|
||||
panel_h += line_h # star type + hop
|
||||
panel_h += line_h # corridor
|
||||
panel_h += line_h # bodies + pop
|
||||
if not gttr.is_empty():
|
||||
panel_h += sep_total + gttr_h
|
||||
panel_h += sep_total + 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 + line_h # first baseline
|
||||
|
||||
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 += line_h
|
||||
|
||||
# System ID
|
||||
draw_string(font, Vector2(x, y), node.get("system_id", ""),
|
||||
HORIZONTAL_ALIGNMENT_LEFT, inner_w, 10, COLOR_TEXT_DIM)
|
||||
|
||||
# Separator — space above from baseline, line, space below to next baseline
|
||||
y += sep_above
|
||||
draw_line(Vector2(x, y), Vector2(x_right, y), sep_color, 1.0)
|
||||
y += sep_below
|
||||
|
||||
# Star type + hop
|
||||
draw_string(font, Vector2(x, y), stat_line_1,
|
||||
HORIZONTAL_ALIGNMENT_LEFT, inner_w, 11, COLOR_TEXT_DIM)
|
||||
y += line_h
|
||||
|
||||
# Corridor
|
||||
draw_string(font, Vector2(x, y), sector_str + " corridor",
|
||||
HORIZONTAL_ALIGNMENT_LEFT, inner_w, 11, sector_color)
|
||||
y += line_h
|
||||
|
||||
# Bodies + population
|
||||
draw_string(font, Vector2(x, y), stat_line_3,
|
||||
HORIZONTAL_ALIGNMENT_LEFT, inner_w, 11, COLOR_TEXT_DIM)
|
||||
|
||||
# GTTR excerpt
|
||||
if not gttr.is_empty():
|
||||
y += sep_above
|
||||
draw_line(Vector2(x, y), Vector2(x_right, y), sep_color, 1.0)
|
||||
y += sep_below
|
||||
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 - line_h # multiline size includes first baseline
|
||||
|
||||
# Adjacent systems
|
||||
y += sep_above
|
||||
draw_line(Vector2(x, y), Vector2(x_right, y), sep_color, 1.0)
|
||||
y += sep_below
|
||||
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)
|
||||
_info_panel.add_component(ImplantDataRow.new(" · ".join(adj_names)))
|
||||
|
||||
|
||||
func _draw_title() -> void:
|
||||
@@ -613,6 +547,7 @@ func _find_nearest_system(pos: Vector2) -> String:
|
||||
func _handle_click(pos: Vector2) -> void:
|
||||
var nearest := _find_nearest_system(pos)
|
||||
_selected_system = nearest
|
||||
_rebuild_info_panel()
|
||||
_dirty = true
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user