## 游戏设计器通用 UI 辅助(静态方法 + 共享常量,各标签页复用) @tool extends RefCounted ## 元素词表(spell_tab 复选框 / resonance_tab 元素下拉共用);label 双语显示,tag 存储,index 对齐 const ELEM_LABELS := ["火 Fire", "冰 Ice", "雷 Lightning", "水 Water", "毒 Poison"] const ELEM_TAGS := ["tag:fire", "tag:ice", "tag:lightning", "tag:water", "tag:poison"] static func spin(min_v: float, max_v: float, step: float, val: float) -> SpinBox: var s := SpinBox.new() s.min_value = min_v; s.max_value = max_v; s.step = step s.value = val s.custom_minimum_size = Vector2(80, 0) s.size_flags_horizontal = Control.SIZE_EXPAND_FILL return s static func line(text: String, placeholder: String = "") -> LineEdit: var le := LineEdit.new() le.text = text le.placeholder_text = placeholder le.custom_minimum_size = Vector2(120, 0) le.size_flags_horizontal = Control.SIZE_EXPAND_FILL return le static func opt(items: Array, selected: int) -> OptionButton: var o := OptionButton.new() for it in items: o.add_item(str(it)) o.selected = clampi(selected, 0, items.size() - 1) o.size_flags_horizontal = Control.SIZE_EXPAND_FILL return o static func header(text: String) -> Label: var l := Label.new() l.text = text l.add_theme_font_size_override("font_size", 15) l.modulate = Color(1.0, 0.85, 0.4) return l static func cell_label(text: String) -> Label: var l := Label.new() l.text = text l.custom_minimum_size = Vector2(70, 0) return l static func btn(text: String, cb: Callable) -> Button: var b := Button.new() b.text = text b.pressed.connect(cb) return b static func status_label() -> Label: var l := Label.new() l.add_theme_font_size_override("font_size", 11) l.modulate = Color(0.6, 0.9, 0.6) return l static func set_status(lbl: Label, msg: String, is_err: bool = false) -> void: if lbl: lbl.text = msg lbl.modulate = Color(1.0, 0.5, 0.5) if is_err else Color(0.6, 0.9, 0.6) static func load_json(path: String): if not FileAccess.file_exists(path): return null return JSON.parse_string(FileAccess.get_file_as_string(path)) static func save_json(path: String, data) -> bool: DirAccess.make_dir_recursive_absolute("res://data") var f := FileAccess.open(path, FileAccess.WRITE) if f: f.store_string(JSON.stringify(data, " ")) f.close() return true return false