217 lines
6.5 KiB
GDScript
217 lines
6.5 KiB
GDScript
extends Node
|
|
## Runtime debug panel. Toggle with F1.
|
|
## Reads DP.get_all() to build controls — adding a param to DP is all that's needed to show it here.
|
|
|
|
var _panel: Control
|
|
var _status_label: Label
|
|
var _prev_mouse_mode: Input.MouseMode = Input.MOUSE_MODE_CAPTURED
|
|
|
|
|
|
func _ready() -> void:
|
|
_build_ui()
|
|
|
|
|
|
func _build_ui() -> void:
|
|
var canvas := CanvasLayer.new()
|
|
canvas.layer = 128
|
|
add_child(canvas)
|
|
|
|
_panel = PanelContainer.new()
|
|
_panel.anchor_left = 1.0
|
|
_panel.anchor_right = 1.0
|
|
_panel.anchor_top = 0.0
|
|
_panel.anchor_bottom = 1.0
|
|
_panel.offset_left = -390.0
|
|
_panel.offset_right = 0.0
|
|
_panel.offset_top = 0.0
|
|
_panel.offset_bottom = 0.0
|
|
_panel.mouse_filter = Control.MOUSE_FILTER_STOP
|
|
_panel.visible = false
|
|
|
|
var bg := StyleBoxFlat.new()
|
|
bg.bg_color = Color(0.08, 0.08, 0.10, 0.95)
|
|
_panel.add_theme_stylebox_override("panel", bg)
|
|
canvas.add_child(_panel)
|
|
|
|
var root_vbox := VBoxContainer.new()
|
|
root_vbox.size_flags_horizontal = Control.SIZE_FILL
|
|
_panel.add_child(root_vbox)
|
|
|
|
# ── Toolbar ───────────────────────────────────────────────────────────────
|
|
var toolbar_margin := MarginContainer.new()
|
|
toolbar_margin.add_theme_constant_override("margin_left", 8)
|
|
toolbar_margin.add_theme_constant_override("margin_right", 8)
|
|
toolbar_margin.add_theme_constant_override("margin_top", 6)
|
|
toolbar_margin.add_theme_constant_override("margin_bottom", 4)
|
|
root_vbox.add_child(toolbar_margin)
|
|
|
|
var toolbar := HBoxContainer.new()
|
|
toolbar.add_theme_constant_override("separation", 6)
|
|
toolbar_margin.add_child(toolbar)
|
|
|
|
var title := Label.new()
|
|
title.text = "DEBUG [F1]"
|
|
title.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
|
toolbar.add_child(title)
|
|
|
|
_status_label = Label.new()
|
|
_status_label.modulate = Color(0.4, 1.0, 0.5)
|
|
toolbar.add_child(_status_label)
|
|
|
|
var save_btn := Button.new()
|
|
save_btn.text = "Save"
|
|
save_btn.pressed.connect(_on_save)
|
|
toolbar.add_child(save_btn)
|
|
|
|
var rand_all_btn := Button.new()
|
|
rand_all_btn.text = "Randomize All"
|
|
rand_all_btn.pressed.connect(_on_randomize_all)
|
|
toolbar.add_child(rand_all_btn)
|
|
|
|
var reset_btn := Button.new()
|
|
reset_btn.text = "Reset All"
|
|
reset_btn.pressed.connect(DP.reset_all)
|
|
toolbar.add_child(reset_btn)
|
|
|
|
root_vbox.add_child(HSeparator.new())
|
|
|
|
# ── Scrollable content ────────────────────────────────────────────────────
|
|
var scroll := ScrollContainer.new()
|
|
scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
|
scroll.size_flags_horizontal = Control.SIZE_FILL
|
|
root_vbox.add_child(scroll)
|
|
|
|
var content_margin := MarginContainer.new()
|
|
content_margin.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
|
content_margin.add_theme_constant_override("margin_left", 8)
|
|
content_margin.add_theme_constant_override("margin_right", 8)
|
|
content_margin.add_theme_constant_override("margin_top", 4)
|
|
content_margin.add_theme_constant_override("margin_bottom", 8)
|
|
scroll.add_child(content_margin)
|
|
|
|
var content := VBoxContainer.new()
|
|
content.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
|
content_margin.add_child(content)
|
|
|
|
_build_params(content)
|
|
|
|
|
|
func _build_params(content: VBoxContainer) -> void:
|
|
# Group keys by section preserving registration order
|
|
var sections: Dictionary = {}
|
|
for key: String in DP.get_all():
|
|
var sec: String = DP.get_all()[key]["section"]
|
|
if not sections.has(sec):
|
|
sections[sec] = []
|
|
sections[sec].append(key)
|
|
|
|
for section: String in sections:
|
|
content.add_child(HSeparator.new())
|
|
|
|
var header := Label.new()
|
|
header.text = section.to_upper()
|
|
header.modulate = Color(0.75, 0.85, 1.0)
|
|
content.add_child(header)
|
|
|
|
for key: String in sections[section]:
|
|
var meta: Dictionary = DP.get_all()[key]
|
|
if meta["type"] == TYPE_FLOAT:
|
|
_add_float_row(content, key, meta)
|
|
|
|
|
|
func _add_float_row(parent: VBoxContainer, key: String, meta: Dictionary) -> void:
|
|
var row := HBoxContainer.new()
|
|
row.size_flags_horizontal = Control.SIZE_FILL
|
|
parent.add_child(row)
|
|
|
|
var lbl := Label.new()
|
|
lbl.text = key.replace("_", " ")
|
|
lbl.custom_minimum_size.x = 140
|
|
lbl.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
|
row.add_child(lbl)
|
|
|
|
var slider := HSlider.new()
|
|
slider.min_value = meta["min"]
|
|
slider.max_value = meta["max"]
|
|
slider.step = meta["step"]
|
|
slider.value = meta["value"]
|
|
slider.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
|
slider.custom_minimum_size.x = 60
|
|
row.add_child(slider)
|
|
|
|
var spin := SpinBox.new()
|
|
spin.min_value = meta["min"]
|
|
spin.max_value = meta["max"]
|
|
spin.step = meta["step"]
|
|
spin.value = meta["value"]
|
|
spin.custom_minimum_size.x = 85
|
|
row.add_child(spin)
|
|
|
|
var rand_btn := Button.new()
|
|
rand_btn.text = "🎲"
|
|
rand_btn.tooltip_text = "Randomize"
|
|
rand_btn.pressed.connect(func() -> void:
|
|
var v := randf_range(meta["min"], meta["max"])
|
|
v = snappedf(v, meta["step"])
|
|
slider.value = v
|
|
)
|
|
row.add_child(rand_btn)
|
|
|
|
# Slider → SpinBox + DP (use no_signal to avoid echo)
|
|
slider.value_changed.connect(func(v: float) -> void:
|
|
spin.set_value_no_signal(v)
|
|
DP.set_value(key, v)
|
|
)
|
|
# SpinBox → Slider + DP
|
|
spin.value_changed.connect(func(v: float) -> void:
|
|
slider.set_value_no_signal(v)
|
|
DP.set_value(key, v)
|
|
)
|
|
# External changes (reset, load) → sync both controls
|
|
DP.any_changed.connect(func(changed_key: String, _val: Variant) -> void:
|
|
if changed_key != key and changed_key != "__all__":
|
|
return
|
|
var v := DP.f(key)
|
|
slider.set_value_no_signal(v)
|
|
spin.set_value_no_signal(v)
|
|
)
|
|
|
|
|
|
|
|
func _on_save() -> void:
|
|
DP.save()
|
|
_status_label.text = "Saved!"
|
|
get_tree().create_timer(1.5).timeout.connect(func() -> void:
|
|
_status_label.text = ""
|
|
)
|
|
|
|
|
|
|
|
|
|
func _input(event: InputEvent) -> void:
|
|
if _panel.visible and Input.is_key_pressed(KEY_CTRL) and event is InputEventMouseMotion:
|
|
get_viewport().set_input_as_handled()
|
|
|
|
|
|
func _unhandled_input(event: InputEvent) -> void:
|
|
if not (event is InputEventKey and event.pressed and not event.echo):
|
|
return
|
|
if (event as InputEventKey).keycode != KEY_F1:
|
|
return
|
|
_panel.visible = not _panel.visible
|
|
if _panel.visible:
|
|
_prev_mouse_mode = Input.mouse_mode
|
|
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
|
|
else:
|
|
Input.mouse_mode = _prev_mouse_mode
|
|
get_viewport().set_input_as_handled()
|
|
|
|
|
|
|
|
func _on_randomize_all() -> void:
|
|
for key: String in DP.get_all():
|
|
var meta: Dictionary = DP.get_all()[key]
|
|
if meta["type"] == TYPE_FLOAT:
|
|
var v := snappedf(randf_range(meta["min"], meta["max"]), meta["step"])
|
|
DP.set_value(key, v)
|