scopedvr-map-editor/src/ui/panels/scene_tree_panel.gd

155 lines
5.8 KiB
GDScript

extends "res://src/ui/panels/editor_panel.gd"
## Map-Inhalt-Baum (aus main.gd ausgezogen, Karte t_eaccbfed U1 — Logik
## unverändert). U2 baut hier den hierarchischen Szenen-Baum aus; diese Karte
## zieht nur um: Der Baum lebt jetzt im LINKEN TabContainer (Registry-Gruppe
## "links"), Zustand/Signale wie vorher.
var content_tree: Tree
var _tree_select_lock := false # verhindert Rückkopplung Auswahl ↔ Baum
static func spec() -> Dictionary:
return {"titel": "Map-Inhalt", "gruppe": "links", "order": 10}
func build() -> void:
add_child(_header_label("Map-Inhalt"))
content_tree = Tree.new()
content_tree.size_flags_vertical = Control.SIZE_EXPAND_FILL
content_tree.custom_minimum_size = UIScale.v2(0, 160)
content_tree.allow_reselect = true
content_tree.select_mode = Tree.SELECT_MULTI # Strg+Klick im Baum = Mehrfachauswahl (t_c46d31c2)
content_tree.hide_root = true
content_tree.item_selected.connect(_on_tree_selected)
add_child(content_tree)
## Der Baum ruft zurück, wenn der Nutzer dort ein Objekt auswählt.
## SELECT_MULTI (Karte t_c46d31c2): einfacher Klick = Ersatz-Auswahl,
## Strg+Klick addiert/entfernt nativ — ALLE markierten Objekt-Zeilen spiegeln
## sich in die Viewport-Auswahl (Baum und Szene sind immer synchron).
func _on_tree_selected() -> void:
if _tree_select_lock:
return
var item := content_tree.get_selected()
if item == null:
return
if item.has_meta("gebaeude_index"):
_on_tree_selected_gebaeude(int(item.get_meta("gebaeude_index")))
return
var indices: Array = []
var walk := content_tree.get_next_selected(null)
while walk != null:
if walk.has_meta("obj_index"):
indices.append(int(walk.get_meta("obj_index")))
walk = content_tree.get_next_selected(walk)
if indices.is_empty():
return
var objects: Array = main.doc.get("objects", [])
var valid: Array = []
for idx in indices:
if idx >= 0 and idx < objects.size():
valid.append(idx)
if valid.is_empty():
return
if valid.size() == 1:
main.interaction.select_object(valid[0], true)
main.call("_set_status", "%s ausgewählt — ziehen zum Verschieben, R/Mausrad zum Drehen, Entf zum Löschen. Strg+Klick markiert weitere dazu." % main.object_label(str(objects[valid[0]].get("id", ""))))
else:
main.interaction.select_many(valid)
main.call("_set_status", "%d Objekte im Baum markiert — Entf löscht ALLE, Strg+D dupliziert ALLE (jeweils EIN Undo-Schritt)." % valid.size())
## Baum: Bauteil-Klick → Bauen-Modus + Teil anwählen (Karte t_97ea59c5).
func _on_tree_selected_gebaeude(gi: int) -> void:
if str(main.get("tool_mode")) != "bauen":
main.call("_set_tool_mode", "bauen")
main.build_tool.call("select_index", gi)
func mark_tree_selection(idx: int) -> void:
mark_tree_selection_multi([idx])
func mark_tree_selection_multi(indices: Array) -> void:
_tree_select_lock = true
var wanted := {}
for i in indices:
wanted[int(i)] = true
_mark_tree_selection_walk(content_tree.get_root(), wanted)
var primary: Variant = main.interaction.get("selected_index") if main.interaction != null else -1
if primary is int and primary >= 0:
_ensure_tree_visible(content_tree.get_root(), primary)
_tree_select_lock = false
func _mark_tree_selection_walk(item: TreeItem, wanted: Dictionary) -> void:
if item == null:
return
if item.has_meta("obj_index"):
var idx := int(item.get_meta("obj_index"))
if wanted.has(idx):
item.select(0)
elif item.is_selected(0):
item.deselect(0)
for child in item.get_children():
_mark_tree_selection_walk(child, wanted)
func _ensure_tree_visible(item: TreeItem, idx: int) -> void:
if item == null:
return
if item.has_meta("obj_index") and int(item.get_meta("obj_index")) == idx:
# Fix (beim Inspektor-Einbau gefunden, Vorbestand t_f6f29d86): Tree
# und TreeItem haben KEIN ensure_visible — die Godot-4-API heißt
# Tree.scroll_to_item. Der alte Ruf warf bei JEDER Auswahl einen
# SCRIPT ERROR und scrollte den Baum nie zur Auswahl.
content_tree.scroll_to_item(item)
return
for child in item.get_children():
_ensure_tree_visible(child, idx)
func refresh_tree() -> void:
content_tree.clear()
var root := content_tree.create_item()
var objects: Array = main.doc.get("objects", [])
var obj_item := content_tree.create_item(root)
obj_item.set_text(0, "Objekte (%d)" % objects.size())
for i in objects.size():
var obj = objects[i]
var item := content_tree.create_item(obj_item)
if obj is Dictionary:
item.set_text(0, "%s #%d" % [main.object_label(str(obj.get("id", "?"))), i + 1])
item.set_meta("obj_index", i)
else:
item.set_text(0, "ungültig #%d" % (i + 1))
var zones: Dictionary = main.doc.get("spawn_zones", {})
var spawn_item := content_tree.create_item(root)
spawn_item.set_text(0, "Spawn-Zonen")
for team_key in ["0", "1"]:
if not zones.has(team_key):
continue
var zone: Dictionary = zones[team_key]
var team_item := content_tree.create_item(spawn_item)
var team_name := "Team Blau" if team_key == "0" else "Team Rot"
team_item.set_text(0, "%s (%d Punkte)" % [team_name, (zone.get("points", []) as Array).size()])
for p in (zone.get("points", []) as Array):
if p is Dictionary:
var point_item := content_tree.create_item(team_item)
point_item.set_text(0, "%s (%.0f, %.0f)" % [
str(p.get("id", "?")), float(p.get("pos", [0, 0, 0])[0]), float(p.get("pos", [0, 0, 0])[2])])
# Gebäude (Karte t_97ea59c5): Bauteile im Baum — Klick wählt das Teil an.
var geb_list: Array = BuildOps.pieces(main.doc)
if not geb_list.is_empty():
var geb_item := content_tree.create_item(root)
geb_item.set_text(0, "Gebäude (%d Bauteile)" % geb_list.size())
for i in geb_list.size():
var teil: Dictionary = geb_list[i]
var g_child := content_tree.create_item(geb_item)
g_child.set_text(0, "%s #%d" % [BuildOps.kind_label(str(teil.get("id", "?"))), i + 1])
g_child.set_meta("gebaeude_index", i)