Added resource table plugin
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
class_name ResourceTablesEditFormat
|
||||
extends RefCounted
|
||||
|
||||
var editor_view : Control
|
||||
|
||||
## Override to define reading behaviour.
|
||||
func get_value(entry, key : String):
|
||||
pass
|
||||
|
||||
## Override to define writing behaviour. This is NOT supposed to save - use `save_entries`.
|
||||
func set_value(entry, key : String, value, index : int):
|
||||
pass
|
||||
|
||||
## Override to define how the data gets saved.
|
||||
func save_entries(all_entries : Array, indices : Array):
|
||||
pass
|
||||
|
||||
## Override to allow editing rows from the Inspector.
|
||||
func create_resource(entry) -> Resource:
|
||||
return Resource.new()
|
||||
|
||||
## Override to define duplication behaviour. `name_input` should be a suffix if multiple entries, and full name if one.
|
||||
func duplicate_rows(rows : Array, name_input : String):
|
||||
pass
|
||||
|
||||
## Override to define removal behaviour.
|
||||
func delete_rows(rows : Array):
|
||||
pass
|
||||
|
||||
## Override with `return true` if `resource_path` is defined and the Rename butoon should show.
|
||||
func has_row_names():
|
||||
return false
|
||||
|
||||
## Override to define import behaviour. Must return the `rows` value for the editor view.
|
||||
func import_from_path(folderpath : String, insert_func : Callable, sort_by : String, sort_reverse : bool = false) -> Array:
|
||||
return []
|
||||
@@ -0,0 +1 @@
|
||||
uid://cthwrv3oq72bo
|
||||
@@ -0,0 +1,88 @@
|
||||
class_name ResourceTablesEditFormatCsv
|
||||
extends ResourceTablesEditFormatTres
|
||||
|
||||
var import_data : ResourceTablesImport
|
||||
var csv_rows := []
|
||||
var resource_original_positions := {}
|
||||
|
||||
|
||||
func get_value(entry, key : String):
|
||||
return entry.get(key)
|
||||
|
||||
|
||||
func set_value(entry, key : String, value, index : int):
|
||||
entry.set(key, value)
|
||||
csv_rows[resource_original_positions[entry]] = import_data.resource_to_strings(entry)
|
||||
|
||||
|
||||
func save_entries(all_entries : Array, indices : Array, repeat : bool = true):
|
||||
if timer == null or timer.time_left <= 0.0:
|
||||
var space_after_delimiter := import_data.delimeter.ends_with(" ")
|
||||
var file := FileAccess.open(import_data.edited_path, FileAccess.WRITE)
|
||||
for x in csv_rows:
|
||||
if space_after_delimiter:
|
||||
for i in x.size():
|
||||
if i == 0: continue
|
||||
x[i] = " " + x[i]
|
||||
|
||||
file.store_csv_line(x, import_data.delimeter[0])
|
||||
|
||||
if repeat:
|
||||
timer = editor_view.get_tree().create_timer(3.0)
|
||||
timer.timeout.connect(save_entries.bind(all_entries, indices, false))
|
||||
|
||||
|
||||
func create_resource(entry) -> Resource:
|
||||
return entry
|
||||
|
||||
|
||||
func duplicate_rows(rows : Array, name_input : String):
|
||||
for x in rows:
|
||||
var new_res = x.duplicate()
|
||||
var index : int = resource_original_positions[x]
|
||||
csv_rows.insert(index, import_data.resource_to_strings(new_res))
|
||||
_bump_row_indices(index + 1, 1)
|
||||
resource_original_positions[new_res] = index + 1
|
||||
|
||||
save_entries([], [])
|
||||
|
||||
|
||||
func delete_rows(rows):
|
||||
for x in rows:
|
||||
var index : int = resource_original_positions[x]
|
||||
csv_rows.remove_at(index)
|
||||
_bump_row_indices(index, -1)
|
||||
resource_original_positions.erase(x)
|
||||
|
||||
save_entries([], [])
|
||||
|
||||
|
||||
func has_row_names():
|
||||
return false
|
||||
|
||||
|
||||
func _bump_row_indices(from : int, increment : int = 1):
|
||||
for k in resource_original_positions:
|
||||
if resource_original_positions[k] >= from:
|
||||
resource_original_positions[k] += increment
|
||||
|
||||
|
||||
func import_from_path(path : String, insert_func : Callable, sort_by : String, sort_reverse : bool = false) -> Array:
|
||||
import_data = load(path)
|
||||
var file := FileAccess.open(import_data.edited_path, FileAccess.READ)
|
||||
csv_rows = ResourceTablesImportFormatCsv.import_as_arrays(import_data)
|
||||
|
||||
var rows := []
|
||||
var res : Resource
|
||||
resource_original_positions.clear()
|
||||
for i in csv_rows.size():
|
||||
if import_data.remove_first_row and i == 0:
|
||||
continue
|
||||
|
||||
res = import_data.strings_to_resource(csv_rows[i], "")
|
||||
res.resource_path = ""
|
||||
insert_func.call(res, rows, sort_by, sort_reverse)
|
||||
resource_original_positions[res] = i
|
||||
|
||||
editor_view.fill_property_data(rows[0])
|
||||
return rows
|
||||
@@ -0,0 +1 @@
|
||||
uid://darvq7n1f561a
|
||||
@@ -0,0 +1,148 @@
|
||||
class_name ResourceTablesEditFormatTres
|
||||
extends ResourceTablesEditFormat
|
||||
|
||||
var timer : SceneTreeTimer
|
||||
|
||||
|
||||
func get_value(entry, key : String):
|
||||
return entry[key]
|
||||
|
||||
|
||||
func set_value(entry, key : String, value, index : int):
|
||||
var prev_value = entry[key]
|
||||
if prev_value is StringName:
|
||||
entry[key] = StringName(value)
|
||||
return
|
||||
|
||||
if prev_value is String:
|
||||
entry[key] = String(value)
|
||||
return
|
||||
|
||||
if prev_value is float:
|
||||
entry[key] = float(value)
|
||||
return
|
||||
|
||||
if prev_value is int:
|
||||
entry[key] = int(value)
|
||||
return
|
||||
|
||||
entry[key] = value
|
||||
|
||||
func save_entries(all_entries : Array, indices : Array, repeat : bool = true):
|
||||
# No need to save. Resources are saved with Ctrl+S
|
||||
# (likely because plugin.edit_resource is called to show inspector)
|
||||
return
|
||||
|
||||
|
||||
func create_resource(entry) -> Resource:
|
||||
return entry
|
||||
|
||||
|
||||
func duplicate_rows(rows : Array, name_input : String):
|
||||
var new_path := ""
|
||||
if rows.size() == 1:
|
||||
var new_row = rows[0].duplicate(true)
|
||||
var res_extension := ".res" if rows[0].resource_path.ends_with(".res") else ".tres"
|
||||
new_path = rows[0].resource_path.get_base_dir() + "/" + name_input + res_extension
|
||||
while ResourceLoader.exists(new_path):
|
||||
new_path = new_path.trim_suffix(res_extension) + "_copy" + res_extension
|
||||
|
||||
new_row.resource_path = new_path
|
||||
ResourceSaver.save(new_row)
|
||||
return
|
||||
|
||||
var new_row
|
||||
for x in rows:
|
||||
new_row = x.duplicate(true)
|
||||
var res_extension := ".res" if x.resource_path.ends_with(".res") else ".tres"
|
||||
new_path = x.resource_path.get_basename() + name_input + res_extension
|
||||
while ResourceLoader.exists(new_path):
|
||||
new_path = new_path.trim_suffix(res_extension) + "_copy" + res_extension
|
||||
|
||||
new_row.resource_path = new_path
|
||||
ResourceSaver.save(new_row)
|
||||
|
||||
|
||||
func rename_row(row, new_name : String):
|
||||
var res_extension : String = ".res" if row.resource_path.ends_with(".res") else ".tres"
|
||||
var new_path : String = row.resource_path.get_base_dir() + "/" + new_name + res_extension
|
||||
while FileAccess.file_exists(new_path):
|
||||
new_path = new_path.trim_suffix(res_extension) + "_copy" + res_extension
|
||||
|
||||
var new_row = row
|
||||
DirAccess.open("res://").remove(row.resource_path)
|
||||
new_row.resource_path = new_path
|
||||
ResourceSaver.save(new_row)
|
||||
|
||||
|
||||
func delete_rows(rows):
|
||||
for x in rows:
|
||||
DirAccess.open("res://").remove(x.resource_path)
|
||||
|
||||
|
||||
func has_row_names():
|
||||
return true
|
||||
|
||||
|
||||
func import_from_path(folderpath : String, insert_func : Callable, sort_by : String, sort_reverse : bool = false) -> Array:
|
||||
var solo_property := ""
|
||||
var solo_property_split : Array[String] = []
|
||||
if folderpath.contains("::"):
|
||||
var found_at := folderpath.find("::")
|
||||
solo_property = folderpath.substr(found_at + "::".length()).trim_suffix("/")
|
||||
folderpath = folderpath.left(found_at)
|
||||
for x in solo_property.split("::"):
|
||||
solo_property_split.append(x)
|
||||
|
||||
var rows := []
|
||||
var dir := DirAccess.open(folderpath)
|
||||
if dir == null: return []
|
||||
|
||||
var file_stack : Array[String] = []
|
||||
var folder_stack : Array[String] = [folderpath]
|
||||
|
||||
while folder_stack.size() > 0:
|
||||
folderpath = folder_stack.pop_back()
|
||||
|
||||
for x in DirAccess.get_files_at(folderpath):
|
||||
file_stack.append(folderpath.path_join(x))
|
||||
|
||||
for x in DirAccess.get_directories_at(folderpath):
|
||||
folder_stack.append(folderpath.path_join(x))
|
||||
|
||||
var loaded_res_unique := {}
|
||||
for x in file_stack:
|
||||
if !x.ends_with(".tres") and !x.ends_with(".res"):
|
||||
continue
|
||||
|
||||
if solo_property.is_empty():
|
||||
loaded_res_unique[load(x)] = true
|
||||
|
||||
else:
|
||||
_append_soloed_property(load(x), loaded_res_unique, solo_property_split)
|
||||
|
||||
for x in loaded_res_unique.keys():
|
||||
if x == null: continue
|
||||
insert_func.call(x, rows, sort_by, sort_reverse)
|
||||
|
||||
editor_view.fill_property_data_many(loaded_res_unique.keys())
|
||||
return rows
|
||||
|
||||
|
||||
func _append_soloed_property(current_res : Resource, result : Dictionary, solo_property_split : Array[String], solo_property_split_idx : int = -solo_property_split.size()):
|
||||
var soloed_value = current_res[solo_property_split[solo_property_split_idx]]
|
||||
if solo_property_split_idx == -1:
|
||||
if soloed_value is Resource:
|
||||
result[soloed_value] = true
|
||||
|
||||
elif soloed_value is Array:
|
||||
for x in soloed_value:
|
||||
result[x] = true
|
||||
|
||||
else:
|
||||
if soloed_value is Resource:
|
||||
_append_soloed_property(soloed_value, result, solo_property_split, solo_property_split_idx + 1)
|
||||
|
||||
elif soloed_value is Array:
|
||||
for x in soloed_value:
|
||||
_append_soloed_property(x, result, solo_property_split, solo_property_split_idx + 1)
|
||||
@@ -0,0 +1 @@
|
||||
uid://wpj5py7d730a
|
||||
@@ -0,0 +1,34 @@
|
||||
class_name ResourceTablesExportFormatCsv
|
||||
extends RefCounted
|
||||
|
||||
|
||||
static func can_edit_path(path : String):
|
||||
return path.ends_with(".csv")
|
||||
|
||||
|
||||
static func export_to_file(entries_array : Array, column_names : Array, into_path : String, import_data : ResourceTablesImport):
|
||||
var file := FileAccess.open(into_path, FileAccess.WRITE)
|
||||
|
||||
var line := PackedStringArray()
|
||||
var uniques := {}
|
||||
var space_after_delimiter := import_data.delimeter.ends_with(" ")
|
||||
import_data.prop_names = column_names
|
||||
import_data.prop_types = import_data.get_resource_property_types(entries_array[0], column_names, uniques)
|
||||
import_data.uniques = uniques
|
||||
import_data.resource_path = ""
|
||||
line.resize(column_names.size())
|
||||
if import_data.remove_first_row:
|
||||
for j in column_names.size():
|
||||
line[j] = String(column_names[j])
|
||||
if space_after_delimiter and j != 0:
|
||||
line[j] = " " + line[j]
|
||||
|
||||
file.store_csv_line(line, import_data.delimeter[0])
|
||||
|
||||
for i in entries_array.size():
|
||||
for j in column_names.size():
|
||||
line[j] = import_data.property_to_string((entries_array[i].get(column_names[j])), j)
|
||||
if space_after_delimiter and j != 0:
|
||||
line[j] = " " + line[j]
|
||||
|
||||
file.store_csv_line(line, import_data.delimeter[0])
|
||||
@@ -0,0 +1 @@
|
||||
uid://bpf5v6r8mrgpy
|
||||
@@ -0,0 +1,57 @@
|
||||
class_name ResourceTablesImportFormatCsv
|
||||
extends RefCounted
|
||||
|
||||
|
||||
static func can_edit_path(path : String):
|
||||
return path.ends_with(".csv")
|
||||
|
||||
|
||||
static func get_properties(entries, import_data):
|
||||
return Array(entries[0])
|
||||
|
||||
|
||||
static func import_as_arrays(import_data) -> Array:
|
||||
var file := FileAccess.open(import_data.edited_path, FileAccess.READ)
|
||||
|
||||
import_data.delimeter = ";"
|
||||
var text_lines : Array[PackedStringArray] = [file.get_line().split(import_data.delimeter)]
|
||||
var space_after_delimeter := false
|
||||
var line := text_lines[0]
|
||||
if line.size() == 0:
|
||||
return []
|
||||
|
||||
if line.size() == 1:
|
||||
import_data.delimeter = ","
|
||||
line = line[0].split(import_data.delimeter)
|
||||
text_lines[0] = line
|
||||
if line.size() <= 1:
|
||||
return []
|
||||
|
||||
if line[1].begins_with(" "):
|
||||
for i in line.size():
|
||||
line[i] = line[i].trim_prefix(" ")
|
||||
|
||||
text_lines[0] = line
|
||||
space_after_delimeter = true
|
||||
import_data.delimeter += " "
|
||||
|
||||
while !file.eof_reached():
|
||||
line = file.get_csv_line(import_data.delimeter[0])
|
||||
if space_after_delimeter:
|
||||
for i in line.size():
|
||||
line[i] = line[i].trim_prefix(" ")
|
||||
|
||||
if line.size() == text_lines[0].size():
|
||||
text_lines.append(line)
|
||||
|
||||
elif line.size() != 1:
|
||||
line.resize(text_lines[0].size())
|
||||
text_lines.append(line)
|
||||
|
||||
var entries := []
|
||||
entries.resize(text_lines.size())
|
||||
|
||||
for i in entries.size():
|
||||
entries[i] = text_lines[i]
|
||||
|
||||
return entries
|
||||
@@ -0,0 +1 @@
|
||||
uid://cmsa4mdn3o5rr
|
||||
@@ -0,0 +1,245 @@
|
||||
@tool
|
||||
extends Control
|
||||
|
||||
@export var prop_list_item_scene : PackedScene
|
||||
@export var formats_export : Array[Script]
|
||||
@export var formats_import : Array[Script]
|
||||
|
||||
@onready var editor_view := $"../../.."
|
||||
@onready var filename_options := $"Import/Margins/Scroll/Box/Grid/UseAsFilename"
|
||||
@onready var classname_field := $"Import/Margins/Scroll/Box/Grid/Classname"
|
||||
@onready var script_path_field := $"Import/Margins/Scroll/Box/Grid/HBoxContainer/LineEdit"
|
||||
@onready var prop_list := $"Import/Margins/Scroll/Box"
|
||||
@onready var format_settings := $"Import/Margins/Scroll/Box/StyleSettingsI"
|
||||
@onready var file_dialog_use_script: FileDialog = $"Import/Margins/Scroll/Box/Grid/HBoxContainer/FileDialog"
|
||||
|
||||
var format_extension := ".csv"
|
||||
var entries := []
|
||||
|
||||
var import_data : ResourceTablesImport
|
||||
|
||||
|
||||
func _ready():
|
||||
hide()
|
||||
show()
|
||||
if get_parent().get("size"):
|
||||
get_parent().size = Vector2(600, 400)
|
||||
|
||||
file_dialog_use_script.file_selected.connect(_on_file_dialog_file_selected)
|
||||
|
||||
|
||||
func _on_file_selected(path : String):
|
||||
if !FileAccess.file_exists(path):
|
||||
if path.get_extension() != "":
|
||||
# Path is a file path: replace extension
|
||||
path = path.get_basename() + ".csv"
|
||||
|
||||
else:
|
||||
# Path is a directory: add filename, add extension
|
||||
path = path.path_join(editor_view.current_path.trim_suffix("/").get_file()) + ".csv"
|
||||
|
||||
FileAccess.open(path, FileAccess.WRITE)
|
||||
|
||||
import_data = null
|
||||
for x in DirAccess.get_files_at(path.get_base_dir()):
|
||||
if !x.ends_with(".tres") and !x.ends_with(".res"):
|
||||
continue
|
||||
|
||||
var found_res := load(path.get_base_dir().path_join(x))
|
||||
if !(found_res is ResourceTablesImport):
|
||||
continue
|
||||
|
||||
_import_settings_from_settings_file(found_res, path)
|
||||
break
|
||||
|
||||
if import_data == null:
|
||||
_create_new_settings_file(path)
|
||||
|
||||
_create_prop_editors()
|
||||
$"Import/Margins/Scroll/Box/StyleSettingsI"._send_signal()
|
||||
|
||||
if editor_view.rows.size() > 0:
|
||||
var using_script = editor_view.rows[0].get_script()
|
||||
if using_script != null:
|
||||
script_path_field.text = using_script.resource_path
|
||||
|
||||
await get_tree().process_frame
|
||||
get_parent().popup_centered()
|
||||
await get_tree().process_frame
|
||||
get_parent().min_size = get_combined_minimum_size()
|
||||
position = Vector2.ZERO
|
||||
size = get_parent().size
|
||||
|
||||
|
||||
func _on_files_selected(paths : PackedStringArray):
|
||||
_on_file_selected(paths[0])
|
||||
|
||||
|
||||
func _import_settings_from_settings_file(settings_file : ResourceTablesImport, textfile_path : String):
|
||||
import_data = settings_file
|
||||
|
||||
filename_options.clear()
|
||||
for i in import_data.prop_names.size():
|
||||
filename_options.add_item(import_data.prop_names[i], i)
|
||||
|
||||
if import_data.new_script != null:
|
||||
classname_field.text = import_data.new_script.get_global_name()
|
||||
script_path_field.text = settings_file.new_script.resource_path
|
||||
|
||||
format_settings.set_format_array(import_data.enum_format)
|
||||
for format_x in formats_import:
|
||||
var new_importer = format_x.new()
|
||||
if new_importer.can_edit_path(textfile_path):
|
||||
entries = format_x.new().import_as_arrays(import_data)
|
||||
break
|
||||
|
||||
|
||||
func _create_new_settings_file(textfile_path : String):
|
||||
import_data = ResourceTablesImport.new()
|
||||
import_data.initialize(textfile_path)
|
||||
|
||||
for format_x in formats_import:
|
||||
var new_importer = format_x.new()
|
||||
if new_importer.can_edit_path(textfile_path):
|
||||
entries = new_importer.import_as_arrays(import_data)
|
||||
import_data.prop_names = new_importer.get_properties(entries, import_data)
|
||||
break
|
||||
|
||||
classname_field.text = import_data.edited_path.get_file().get_basename()\
|
||||
.capitalize().replace(" ", "")
|
||||
import_data.script_classname = classname_field.text
|
||||
if script_path_field.text:
|
||||
var existing_resource : Resource = load(script_path_field.text).new()
|
||||
var uniques := {}
|
||||
import_data.prop_types = ResourceTablesImport.get_resource_property_types(existing_resource, import_data.prop_names, uniques)
|
||||
import_data.uniques = uniques
|
||||
|
||||
else:
|
||||
import_data.load_property_names_from_textfile(textfile_path, entries)
|
||||
|
||||
filename_options.clear()
|
||||
for i in import_data.prop_names.size():
|
||||
filename_options.add_item(import_data.prop_names[i], i)
|
||||
|
||||
|
||||
func _create_prop_editors():
|
||||
for x in prop_list.get_children():
|
||||
if !x is GridContainer: x.free()
|
||||
|
||||
await get_tree().process_frame
|
||||
for i in import_data.prop_names.size():
|
||||
var new_node := prop_list_item_scene.instantiate()
|
||||
prop_list.add_child(new_node)
|
||||
var prop_type = import_data.prop_types[i]
|
||||
new_node.display(import_data.prop_names[i], prop_type if !(prop_type is PackedStringArray) else ResourceTablesImport.PropType.ENUM)
|
||||
new_node.connect_all_signals(self, i)
|
||||
|
||||
|
||||
func _generate_class(save_script = true):
|
||||
save_script = true # Built-ins didn't work in 3.x, won't change because dont wanna test rn
|
||||
import_data.new_script = import_data.generate_script(entries, save_script)
|
||||
if save_script:
|
||||
import_data.new_script.resource_path = import_data.edited_path.get_basename() + ".gd"
|
||||
ResourceSaver.save(import_data.new_script)
|
||||
# Because when instanced, objects have a copy of the script
|
||||
import_data.new_script = load(import_data.edited_path.get_basename() + ".gd")
|
||||
|
||||
|
||||
func _on_import_to_tres_pressed():
|
||||
if script_path_field.text != "":
|
||||
import_data.load_external_script(load(script_path_field.text))
|
||||
|
||||
if import_data.new_script == null:
|
||||
_generate_class()
|
||||
|
||||
DirAccess.open("res://").make_dir_recursive(import_data.edited_path.get_basename())
|
||||
|
||||
import_data.prop_used_as_filename = import_data.prop_names[filename_options.selected]
|
||||
var new_res : Resource
|
||||
for i in entries.size():
|
||||
if import_data.remove_first_row and i == 0:
|
||||
continue
|
||||
|
||||
new_res = import_data.strings_to_resource(entries[i], editor_view.current_path)
|
||||
ResourceSaver.save(new_res)
|
||||
|
||||
await get_tree().process_frame
|
||||
await get_tree().process_frame
|
||||
editor_view.refresh()
|
||||
close()
|
||||
|
||||
|
||||
func _on_import_edit_pressed():
|
||||
if import_data.new_script == null:
|
||||
_generate_class(false)
|
||||
|
||||
import_data.prop_used_as_filename = ""
|
||||
import_data.save()
|
||||
await get_tree().process_frame
|
||||
editor_view.display_folder(import_data.resource_path)
|
||||
editor_view.node_columns.hidden_columns[editor_view.current_path] = {
|
||||
"resource_path" : true,
|
||||
"resource_local_to_scene" : true,
|
||||
}
|
||||
editor_view.save_data()
|
||||
await get_tree().process_frame
|
||||
editor_view.refresh()
|
||||
close()
|
||||
|
||||
|
||||
func _on_export_csv_pressed():
|
||||
var exported_cols : Array = editor_view.columns.duplicate()
|
||||
exported_cols.erase(&"resource_local_to_scene")
|
||||
var column_properties : Dictionary = editor_view.node_columns.column_properties[editor_view.current_path]
|
||||
for k in column_properties:
|
||||
if column_properties[k].get(&"visibility", 1.0) == 0.0:
|
||||
exported_cols.erase(k)
|
||||
|
||||
ResourceTablesExportFormatCsv.export_to_file(editor_view.rows, exported_cols, import_data.edited_path, import_data)
|
||||
await get_tree().process_frame
|
||||
editor_view.refresh()
|
||||
close()
|
||||
|
||||
# Input controls
|
||||
func _on_classname_field_text_changed(new_text : String):
|
||||
import_data.script_classname = new_text.replace(" ", "")
|
||||
|
||||
|
||||
func _on_remove_first_row_toggled(button_pressed : bool):
|
||||
import_data.remove_first_row = button_pressed
|
||||
# $"Export/Box2/Button".button_pressed = true
|
||||
$"Export/Box3/CheckBox".button_pressed = button_pressed
|
||||
|
||||
|
||||
func _on_list_item_type_selected(type : int, index : int):
|
||||
import_data.prop_types[index] = type
|
||||
|
||||
|
||||
func _on_list_item_name_changed(name : String, index : int):
|
||||
import_data.prop_names[index] = name.replace(" ", "")
|
||||
|
||||
|
||||
func _on_export_delimiter_pressed(del : String):
|
||||
import_data.delimeter = del + import_data.delimeter.substr(1)
|
||||
|
||||
|
||||
func _on_export_space_toggled(button_pressed : bool):
|
||||
import_data.delimeter = (
|
||||
import_data.delimeter[0]
|
||||
if !button_pressed else
|
||||
import_data.delimeter + " "
|
||||
)
|
||||
|
||||
|
||||
func _on_enum_format_changed(case, delimiter, bool_yes, bool_no):
|
||||
import_data.enum_format = [case, delimiter, bool_yes, bool_no]
|
||||
|
||||
|
||||
func close():
|
||||
get_parent().hide()
|
||||
|
||||
|
||||
# Handles reloading the import hint editor if a Resource script is chosen
|
||||
func _on_file_dialog_file_selected(path: String) -> void:
|
||||
script_path_field.text = path
|
||||
_on_file_selected(import_data.edited_path)
|
||||
@@ -0,0 +1 @@
|
||||
uid://b1uc8bum4bi01
|
||||
@@ -0,0 +1,240 @@
|
||||
[gd_scene load_steps=8 format=3 uid="uid://b413igx28kkvb"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://b1uc8bum4bi01" path="res://addons/resources_spreadsheet_view/import_export/import_export_dialog.gd" id="1"]
|
||||
[ext_resource type="Script" uid="uid://bpf5v6r8mrgpy" path="res://addons/resources_spreadsheet_view/import_export/formats_export/export_csv.gd" id="2_33c6s"]
|
||||
[ext_resource type="Script" uid="uid://cmsa4mdn3o5rr" path="res://addons/resources_spreadsheet_view/import_export/formats_import/import_csv.gd" id="2_fxayt"]
|
||||
[ext_resource type="PackedScene" uid="uid://b8llymigbprh6" path="res://addons/resources_spreadsheet_view/import_export/property_list_item.tscn" id="2_xfhmf"]
|
||||
[ext_resource type="PackedScene" uid="uid://ckhf3bqy2rqjr" path="res://addons/resources_spreadsheet_view/import_export/import_export_enum_format.tscn" id="4"]
|
||||
[ext_resource type="Script" uid="uid://dyxdmrhopt5i0" path="res://addons/resources_spreadsheet_view/editor_icon_button.gd" id="5_k5rhi"]
|
||||
|
||||
[sub_resource type="ButtonGroup" id="ButtonGroup_080hd"]
|
||||
|
||||
[node name="TabContainer" type="TabContainer"]
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
current_tab = 1
|
||||
script = ExtResource("1")
|
||||
prop_list_item_scene = ExtResource("2_xfhmf")
|
||||
formats_export = Array[Script]([ExtResource("2_33c6s")])
|
||||
formats_import = Array[Script]([ExtResource("2_fxayt")])
|
||||
|
||||
[node name="Import" type="VBoxContainer" parent="."]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
mouse_filter = 2
|
||||
metadata/_tab_index = 0
|
||||
|
||||
[node name="Margins" type="MarginContainer" parent="Import"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="Scroll" type="ScrollContainer" parent="Import/Margins"]
|
||||
layout_mode = 2
|
||||
horizontal_scroll_mode = 0
|
||||
|
||||
[node name="Box" type="VBoxContainer" parent="Import/Margins/Scroll"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="Grid" type="GridContainer" parent="Import/Margins/Scroll/Box"]
|
||||
layout_mode = 2
|
||||
columns = 2
|
||||
|
||||
[node name="Label" type="Label" parent="Import/Margins/Scroll/Box/Grid"]
|
||||
layout_mode = 2
|
||||
text = "Column Used as Filename:"
|
||||
|
||||
[node name="UseAsFilename" type="OptionButton" parent="Import/Margins/Scroll/Box/Grid"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="Label4" type="Label" parent="Import/Margins/Scroll/Box/Grid"]
|
||||
layout_mode = 2
|
||||
text = "Use Script:"
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="Import/Margins/Scroll/Box/Grid"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="LineEdit" type="LineEdit" parent="Import/Margins/Scroll/Box/Grid/HBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
placeholder_text = "(create new GDScript)"
|
||||
editable = false
|
||||
|
||||
[node name="Button2" type="Button" parent="Import/Margins/Scroll/Box/Grid/HBoxContainer"]
|
||||
layout_mode = 2
|
||||
script = ExtResource("5_k5rhi")
|
||||
icon_name = "Clear"
|
||||
|
||||
[node name="Button" type="Button" parent="Import/Margins/Scroll/Box/Grid/HBoxContainer"]
|
||||
layout_mode = 2
|
||||
script = ExtResource("5_k5rhi")
|
||||
icon_name = "Folder"
|
||||
|
||||
[node name="FileDialog" type="FileDialog" parent="Import/Margins/Scroll/Box/Grid/HBoxContainer"]
|
||||
title = "Choose Script..."
|
||||
initial_position = 4
|
||||
size = Vector2i(800, 500)
|
||||
ok_button_text = "Open"
|
||||
mode_overrides_title = false
|
||||
file_mode = 0
|
||||
filters = PackedStringArray("*.gd", "*.cs")
|
||||
|
||||
[node name="Control" type="Control" parent="Import/Margins/Scroll/Box/Grid"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label5" type="Label" parent="Import/Margins/Scroll/Box/Grid"]
|
||||
self_modulate = Color(1, 0.84375, 0, 1)
|
||||
layout_mode = 2
|
||||
text = "WARNING: Importing with a pre-existing Script will not import datatypes not selectible below."
|
||||
horizontal_alignment = 1
|
||||
autowrap_mode = 3
|
||||
|
||||
[node name="Label2" type="Label" parent="Import/Margins/Scroll/Box/Grid"]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
text = "Class Name"
|
||||
|
||||
[node name="Classname" type="LineEdit" parent="Import/Margins/Scroll/Box/Grid"]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
caret_blink = true
|
||||
caret_blink_interval = 0.5
|
||||
|
||||
[node name="Label3" type="Label" parent="Import/Margins/Scroll/Box/Grid"]
|
||||
layout_mode = 2
|
||||
text = "Flags:"
|
||||
|
||||
[node name="Flags" type="VBoxContainer" parent="Import/Margins/Scroll/Box/Grid"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="RemoveFirstRow" type="CheckBox" parent="Import/Margins/Scroll/Box/Grid/Flags"]
|
||||
layout_mode = 2
|
||||
text = "First row contains property names"
|
||||
|
||||
[node name="HSeparator" type="HSeparator" parent="Import/Margins/Scroll/Box"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="StyleSettingsI" parent="Import/Margins/Scroll/Box" instance=ExtResource("4")]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Box" type="HBoxContainer" parent="Import"]
|
||||
layout_mode = 2
|
||||
mouse_filter = 2
|
||||
alignment = 1
|
||||
|
||||
[node name="Ok2" type="Button" parent="Import/Box"]
|
||||
layout_mode = 2
|
||||
text = "Confirm and edit"
|
||||
|
||||
[node name="Ok" type="Button" parent="Import/Box"]
|
||||
layout_mode = 2
|
||||
text = "Convert to Resources and edit"
|
||||
|
||||
[node name="Cancel" type="Button" parent="Import/Box"]
|
||||
layout_mode = 2
|
||||
text = "Cancel"
|
||||
|
||||
[node name="Control" type="Control" parent="Import"]
|
||||
layout_mode = 2
|
||||
mouse_filter = 2
|
||||
|
||||
[node name="Export" type="VBoxContainer" parent="."]
|
||||
layout_mode = 2
|
||||
metadata/_tab_index = 1
|
||||
|
||||
[node name="Info" type="Label" parent="Export"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 0
|
||||
text = "The currently edited folder will be exported into the selected file.
|
||||
|
||||
Rows hidden by the filter will NOT be exported, and order follows the current sorting key. Rows on non-selected pages will not be removed.
|
||||
|
||||
Hidden columns will NOT be exported."
|
||||
autowrap_mode = 2
|
||||
|
||||
[node name="HSeparator" type="HSeparator" parent="Export"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Box" type="HBoxContainer" parent="Export"]
|
||||
layout_mode = 2
|
||||
alignment = 1
|
||||
|
||||
[node name="Label2" type="Label" parent="Export/Box"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Delimiter:"
|
||||
|
||||
[node name="Button" type="Button" parent="Export/Box"]
|
||||
layout_mode = 2
|
||||
toggle_mode = true
|
||||
button_pressed = true
|
||||
button_group = SubResource("ButtonGroup_080hd")
|
||||
text = "Comma (,)"
|
||||
|
||||
[node name="Button2" type="Button" parent="Export/Box"]
|
||||
layout_mode = 2
|
||||
toggle_mode = true
|
||||
button_group = SubResource("ButtonGroup_080hd")
|
||||
text = "Semicolon (;)"
|
||||
|
||||
[node name="Button3" type="Button" parent="Export/Box"]
|
||||
layout_mode = 2
|
||||
toggle_mode = true
|
||||
button_group = SubResource("ButtonGroup_080hd")
|
||||
text = "Tab"
|
||||
|
||||
[node name="CheckBox" type="CheckBox" parent="Export/Box"]
|
||||
layout_mode = 2
|
||||
text = "With space after"
|
||||
|
||||
[node name="Box3" type="HBoxContainer" parent="Export"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="CheckBox" type="CheckBox" parent="Export/Box3"]
|
||||
layout_mode = 2
|
||||
text = "First row contains property names (CSV)"
|
||||
|
||||
[node name="StyleSettingsE" parent="Export" instance=ExtResource("4")]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Control" type="Control" parent="Export"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="Box2" type="HBoxContainer" parent="Export"]
|
||||
layout_mode = 2
|
||||
alignment = 1
|
||||
|
||||
[node name="Button" type="Button" parent="Export/Box2"]
|
||||
layout_mode = 2
|
||||
text = "Export to CSV"
|
||||
|
||||
[node name="Cancel" type="Button" parent="Export/Box2"]
|
||||
layout_mode = 2
|
||||
text = "Cancel"
|
||||
|
||||
[node name="Control2" type="Control" parent="Export"]
|
||||
layout_mode = 2
|
||||
|
||||
[connection signal="pressed" from="Import/Margins/Scroll/Box/Grid/HBoxContainer/Button2" to="Import/Margins/Scroll/Box/Grid/HBoxContainer/LineEdit" method="set_text" binds= [""]]
|
||||
[connection signal="pressed" from="Import/Margins/Scroll/Box/Grid/HBoxContainer/Button" to="Import/Margins/Scroll/Box/Grid/HBoxContainer/FileDialog" method="popup_centered"]
|
||||
[connection signal="file_selected" from="Import/Margins/Scroll/Box/Grid/HBoxContainer/FileDialog" to="Import/Margins/Scroll/Box/Grid/HBoxContainer/LineEdit" method="set_text"]
|
||||
[connection signal="text_changed" from="Import/Margins/Scroll/Box/Grid/Classname" to="." method="_on_classname_field_text_changed"]
|
||||
[connection signal="toggled" from="Import/Margins/Scroll/Box/Grid/Flags/RemoveFirstRow" to="." method="_on_remove_first_row_toggled"]
|
||||
[connection signal="format_changed" from="Import/Margins/Scroll/Box/StyleSettingsI" to="." method="_on_enum_format_changed"]
|
||||
[connection signal="format_changed" from="Import/Margins/Scroll/Box/StyleSettingsI" to="Export/StyleSettingsE" method="_on_format_changed"]
|
||||
[connection signal="pressed" from="Import/Box/Ok2" to="." method="_on_import_edit_pressed"]
|
||||
[connection signal="pressed" from="Import/Box/Ok" to="." method="_on_import_to_tres_pressed"]
|
||||
[connection signal="pressed" from="Import/Box/Cancel" to="." method="close"]
|
||||
[connection signal="pressed" from="Export/Box/Button" to="." method="_on_export_delimiter_pressed" binds= [","]]
|
||||
[connection signal="pressed" from="Export/Box/Button2" to="." method="_on_export_delimiter_pressed" binds= [";"]]
|
||||
[connection signal="pressed" from="Export/Box/Button3" to="." method="_on_export_delimiter_pressed" binds= [" "]]
|
||||
[connection signal="toggled" from="Export/Box/CheckBox" to="." method="_on_export_space_toggled"]
|
||||
[connection signal="toggled" from="Export/Box3/CheckBox" to="." method="_on_remove_first_row_toggled"]
|
||||
[connection signal="format_changed" from="Export/StyleSettingsE" to="." method="_on_enum_format_changed"]
|
||||
[connection signal="pressed" from="Export/Box2/Button" to="." method="_on_export_csv_pressed"]
|
||||
[connection signal="pressed" from="Export/Box2/Cancel" to="." method="close"]
|
||||
@@ -0,0 +1,30 @@
|
||||
@tool
|
||||
extends GridContainer
|
||||
|
||||
signal format_changed(case : int, delimiter : String, bool_yes : String, bool_no: String)
|
||||
|
||||
|
||||
func set_format_array(format : Array):
|
||||
_on_format_changed(format[0], format[1], format[2], format[3])
|
||||
format_changed.emit(format[0], format[1], format[2], format[3])
|
||||
|
||||
|
||||
func set_format(case : int, delimiter : String, bool_yes : String, bool_no: String):
|
||||
_on_format_changed(case, delimiter, bool_yes, bool_no)
|
||||
format_changed.emit(case, delimiter, bool_yes, bool_no)
|
||||
|
||||
|
||||
func _send_signal(arg1 = null):
|
||||
format_changed.emit(
|
||||
$"HBoxContainer/Case".selected,
|
||||
[" ", "_", "-"][$"HBoxContainer/Separator".selected],
|
||||
$"HBoxContainer2/True".text,
|
||||
$"HBoxContainer2/False".text
|
||||
)
|
||||
|
||||
|
||||
func _on_format_changed(case : int, delimiter : String, bool_yes : String, bool_no: String):
|
||||
$"HBoxContainer/Case".selected = case
|
||||
$"HBoxContainer/Separator".selected = [" ", "_", "-"].find(delimiter)
|
||||
$"HBoxContainer2/True".text = bool_yes
|
||||
$"HBoxContainer2/False".text = bool_no
|
||||
@@ -0,0 +1 @@
|
||||
uid://cmbs7j2rx0xa3
|
||||
@@ -0,0 +1,68 @@
|
||||
[gd_scene load_steps=2 format=3 uid="uid://ckhf3bqy2rqjr"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://cmbs7j2rx0xa3" path="res://addons/resources_spreadsheet_view/import_export/import_export_enum_format.gd" id="1"]
|
||||
|
||||
[node name="EnumFormat" type="GridContainer"]
|
||||
columns = 2
|
||||
script = ExtResource("1")
|
||||
|
||||
[node name="Label3" type="Label" parent="."]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Enum word case/separator"
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="."]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="Case" type="OptionButton" parent="HBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
clip_text = true
|
||||
selected = 2
|
||||
item_count = 4
|
||||
popup/item_0/text = "all lower"
|
||||
popup/item_1/text = "caps Except First"
|
||||
popup/item_1/id = 1
|
||||
popup/item_2/text = "Caps Every Word"
|
||||
popup/item_2/id = 2
|
||||
popup/item_3/text = "ALL CAPS"
|
||||
popup/item_3/id = 3
|
||||
|
||||
[node name="Separator" type="OptionButton" parent="HBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_stretch_ratio = 0.5
|
||||
clip_text = true
|
||||
selected = 0
|
||||
item_count = 3
|
||||
popup/item_0/text = "Space \" \""
|
||||
popup/item_1/text = "Underscore \"_\""
|
||||
popup/item_1/id = 1
|
||||
popup/item_2/text = "Kebab \"-\""
|
||||
popup/item_2/id = 2
|
||||
|
||||
[node name="Label4" type="Label" parent="."]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Boolean True/False"
|
||||
|
||||
[node name="HBoxContainer2" type="HBoxContainer" parent="."]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="True" type="LineEdit" parent="HBoxContainer2"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Yes"
|
||||
|
||||
[node name="False" type="LineEdit" parent="HBoxContainer2"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "No"
|
||||
|
||||
[connection signal="mouse_entered" from="Label3" to="." method="_on_Label3_mouse_entered"]
|
||||
[connection signal="item_selected" from="HBoxContainer/Case" to="." method="_send_signal"]
|
||||
[connection signal="item_selected" from="HBoxContainer/Separator" to="." method="_send_signal"]
|
||||
[connection signal="text_changed" from="HBoxContainer2/True" to="." method="_send_signal"]
|
||||
[connection signal="text_changed" from="HBoxContainer2/False" to="." method="_send_signal"]
|
||||
@@ -0,0 +1,12 @@
|
||||
@tool
|
||||
extends HBoxContainer
|
||||
|
||||
|
||||
func display(name : String, type : int):
|
||||
$"LineEdit".text = name
|
||||
$"OptionButton".selected = type
|
||||
|
||||
|
||||
func connect_all_signals(to : Object, index : int, prefix : String = "_on_list_item_"):
|
||||
$"LineEdit".text_changed.connect(Callable(to, prefix + "name_changed").bind(index))
|
||||
$"OptionButton".item_selected.connect(Callable(to, prefix + "type_selected").bind(index))
|
||||
@@ -0,0 +1 @@
|
||||
uid://c0j7uonbiyaox
|
||||
@@ -0,0 +1,36 @@
|
||||
[gd_scene load_steps=2 format=3 uid="uid://b8llymigbprh6"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://c0j7uonbiyaox" path="res://addons/resources_spreadsheet_view/import_export/property_list_item.gd" id="1"]
|
||||
|
||||
[node name="Entry" type="HBoxContainer"]
|
||||
script = ExtResource("1")
|
||||
|
||||
[node name="LineEdit" type="LineEdit" parent="."]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_stretch_ratio = 0.5
|
||||
text = "1"
|
||||
|
||||
[node name="OptionButton" type="OptionButton" parent="."]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 5
|
||||
size_flags_stretch_ratio = 0.25
|
||||
fit_to_longest_item = false
|
||||
item_count = 8
|
||||
popup/item_0/text = "Bool"
|
||||
popup/item_0/id = 1
|
||||
popup/item_1/text = "Integer Number"
|
||||
popup/item_1/id = 2
|
||||
popup/item_2/text = "Floating Point Number"
|
||||
popup/item_2/id = 3
|
||||
popup/item_3/text = "String/Other"
|
||||
popup/item_3/id = 4
|
||||
popup/item_4/text = "Color"
|
||||
popup/item_4/id = 14
|
||||
popup/item_5/text = "Resource Path"
|
||||
popup/item_5/id = 17
|
||||
popup/item_6/text = "Enumeration"
|
||||
popup/item_6/id = 101
|
||||
popup/item_7/text = "Array"
|
||||
popup/item_7/id = 102
|
||||
@@ -0,0 +1,446 @@
|
||||
@tool
|
||||
class_name ResourceTablesImport
|
||||
extends Resource
|
||||
|
||||
enum PropType {
|
||||
BOOL,
|
||||
INT,
|
||||
FLOAT,
|
||||
STRING,
|
||||
COLOR,
|
||||
OBJECT,
|
||||
ENUM,
|
||||
COLLECTION,
|
||||
MAX,
|
||||
}
|
||||
|
||||
enum NameCasing {
|
||||
ALL_LOWER,
|
||||
CAPS_WORD_EXCEPT_FIRST,
|
||||
CAPS_WORD,
|
||||
ALL_CAPS,
|
||||
}
|
||||
|
||||
const SUFFIX := "_table_import.tres"
|
||||
const TYPE_MAP := {
|
||||
TYPE_STRING : PropType.STRING,
|
||||
TYPE_FLOAT : PropType.FLOAT,
|
||||
TYPE_BOOL : PropType.BOOL,
|
||||
TYPE_INT : PropType.INT,
|
||||
TYPE_OBJECT : PropType.OBJECT,
|
||||
TYPE_COLOR : PropType.COLOR,
|
||||
TYPE_ARRAY : PropType.COLLECTION,
|
||||
TYPE_DICTIONARY : PropType.COLLECTION,
|
||||
}
|
||||
|
||||
@export var prop_types : Array
|
||||
@export var prop_names : Array
|
||||
|
||||
@export var edited_path := "res://"
|
||||
@export var prop_used_as_filename := ""
|
||||
@export var script_classname := ""
|
||||
@export var remove_first_row := true
|
||||
|
||||
@export var new_script : Script
|
||||
@export var view_script : Script = ResourceTablesEditFormatCsv
|
||||
@export var delimeter := ";"
|
||||
@export var enum_format : Array = [NameCasing.CAPS_WORD, " ", "Yes", "No"]
|
||||
|
||||
@export var uniques : Dictionary
|
||||
|
||||
|
||||
func initialize(path):
|
||||
edited_path = path
|
||||
prop_types = []
|
||||
prop_names = []
|
||||
|
||||
|
||||
func save():
|
||||
resource_path = edited_path.get_basename() + SUFFIX
|
||||
ResourceSaver.save.call_deferred(self)
|
||||
|
||||
|
||||
func string_to_property(string : String, col_index : int):
|
||||
match prop_types[col_index]:
|
||||
PropType.STRING:
|
||||
return string
|
||||
|
||||
PropType.BOOL:
|
||||
string = string.to_lower()
|
||||
if string == enum_format[2].to_lower(): return true
|
||||
if string == enum_format[3].to_lower(): return false
|
||||
return !string in ["no", "disabled", "-", "false", "absent", "wrong", "off", "0", ""]
|
||||
|
||||
PropType.FLOAT:
|
||||
return string.to_float()
|
||||
|
||||
PropType.INT:
|
||||
return string.to_int()
|
||||
|
||||
PropType.COLOR:
|
||||
return Color(string)
|
||||
|
||||
PropType.OBJECT:
|
||||
return null if string == "" else load(string)
|
||||
|
||||
PropType.ENUM:
|
||||
if string == "":
|
||||
return int(uniques[col_index]["N_A"])
|
||||
|
||||
else:
|
||||
if !uniques.has(col_index):
|
||||
return -1
|
||||
|
||||
return int(uniques[col_index][string.capitalize().replace(" ", "_").to_upper()])
|
||||
# if string.is_valid_int():
|
||||
# return int(uniques[col_index][string.capitalize().replace(" ", "_").to_upper()])
|
||||
# else:
|
||||
# # If the enum is a string, we actually just want the key not the value
|
||||
# var enum_keys : Dictionary = uniques[col_index]
|
||||
# return int(enum_keys.find_key(string))
|
||||
|
||||
PropType.COLLECTION:
|
||||
var result = str_to_var(string)
|
||||
if result is Array:
|
||||
for i in result.size():
|
||||
if result[i] is String && result[i].begins_with("res://"):
|
||||
result[i] = load(result[i])
|
||||
|
||||
if result is Dictionary:
|
||||
for k in result:
|
||||
if result[k] is String && result[k].begins_with("res://"):
|
||||
result[k] = load(result[k])
|
||||
|
||||
if result == null:
|
||||
result = []
|
||||
|
||||
return result
|
||||
|
||||
|
||||
func property_to_string(value, col_index : int) -> String:
|
||||
if value == null: return ""
|
||||
match prop_types[col_index]:
|
||||
PropType.STRING:
|
||||
return str(value)
|
||||
|
||||
PropType.BOOL:
|
||||
return enum_format[2] if value else enum_format[3]
|
||||
|
||||
PropType.FLOAT, PropType.INT:
|
||||
return str(value)
|
||||
|
||||
PropType.COLOR:
|
||||
return value.to_html()
|
||||
|
||||
PropType.OBJECT:
|
||||
return value.resource_path
|
||||
|
||||
PropType.COLLECTION:
|
||||
if value is Array:
|
||||
var new_value := []
|
||||
new_value.resize(value.size())
|
||||
for i in value.size():
|
||||
new_value[i] = value[i]
|
||||
if value[i] is Resource:
|
||||
new_value[i] = value[i].resource_path
|
||||
|
||||
value = new_value
|
||||
|
||||
if value is Dictionary:
|
||||
var new_value := {}
|
||||
for k in value:
|
||||
new_value[k] = value[k]
|
||||
if value[k] is Resource:
|
||||
new_value[k] = value[k].resource_path
|
||||
|
||||
if k is Resource:
|
||||
new_value[k.resource_path] = new_value[k]
|
||||
|
||||
value = new_value
|
||||
|
||||
return str(value)
|
||||
|
||||
PropType.ENUM:
|
||||
var dict = uniques[col_index]
|
||||
for k in dict:
|
||||
if dict[k] == value:
|
||||
return change_name_to_format(k, enum_format[0], enum_format[1])
|
||||
|
||||
return str(value)
|
||||
|
||||
|
||||
func create_property_line_for_prop(col_index : int) -> String:
|
||||
var result : String = "@export var " + prop_names[col_index] + " :"
|
||||
match prop_types[col_index]:
|
||||
PropType.STRING:
|
||||
return result + "= \"\"\r\n"
|
||||
|
||||
PropType.BOOL:
|
||||
return result + "= false\r\n"
|
||||
|
||||
PropType.FLOAT:
|
||||
return result + "= 0.0\r\n"
|
||||
|
||||
PropType.INT:
|
||||
return result + "= 0\r\n"
|
||||
|
||||
PropType.COLOR:
|
||||
return result + "= Color.WHITE\r\n"
|
||||
|
||||
PropType.OBJECT:
|
||||
return result + " Resource\r\n"
|
||||
|
||||
PropType.COLLECTION:
|
||||
return result + "= []\r\n"
|
||||
|
||||
PropType.ENUM:
|
||||
return result + " %s\r\n" % _escape_forbidden_enum_names(prop_names[col_index].capitalize().replace(" ", ""))
|
||||
# return result.replace(
|
||||
# "@export var",
|
||||
# "@export_enum(" + _escape_forbidden_enum_names(
|
||||
# prop_names[col_index].capitalize()\
|
||||
# .replace(" ", "")
|
||||
# ) + ") var"
|
||||
# ) + "= 0\r\n"
|
||||
|
||||
return ""
|
||||
|
||||
|
||||
func _escape_forbidden_enum_names(string : String) -> String:
|
||||
if ClassDB.class_exists(string):
|
||||
return string + "_"
|
||||
|
||||
# Not in ClassDB, but are engine types and can be property names
|
||||
if string in [
|
||||
"Color", "String", "Plane", "Projection",
|
||||
"Basis", "Transform", "Variant",
|
||||
]:
|
||||
return string + "_"
|
||||
|
||||
return string
|
||||
|
||||
|
||||
func create_enum_for_prop(col_index) -> String:
|
||||
var result := (
|
||||
"enum "
|
||||
+ _escape_forbidden_enum_names(
|
||||
prop_names[col_index].capitalize().replace(" ", "")
|
||||
) + " {\r\n"
|
||||
)
|
||||
for k in uniques[col_index]:
|
||||
result += (
|
||||
"\t"
|
||||
+ k # Enum Entry
|
||||
+ " = "
|
||||
+ str(uniques[col_index][k]) # Value
|
||||
+ ",\r\n"
|
||||
)
|
||||
result += "\tMAX,\r\n}\r\n\r\n"
|
||||
|
||||
return result
|
||||
|
||||
|
||||
func generate_script(entries, has_classname = true) -> GDScript:
|
||||
var source := ""
|
||||
# if has_classname and script_classname != "":
|
||||
# source = "class_name " + script_classname + " \r\nextends Resource\r\n\r\n"
|
||||
#
|
||||
# else:
|
||||
source = "extends Resource\r\n\r\n"
|
||||
|
||||
# Enums
|
||||
uniques = get_uniques(entries)
|
||||
for i in prop_types.size():
|
||||
if prop_types[i] == PropType.ENUM:
|
||||
source += create_enum_for_prop(i)
|
||||
|
||||
# Properties
|
||||
for i in prop_names.size():
|
||||
if (prop_names[i] != "resource_path") and (prop_names[i] != "resource_name"):
|
||||
source += create_property_line_for_prop(i)
|
||||
|
||||
var created_script : GDScript = GDScript.new()
|
||||
created_script.source_code = source
|
||||
created_script.reload()
|
||||
return created_script
|
||||
|
||||
|
||||
func load_property_names_from_textfile(path : String, loaded_entries : Array):
|
||||
prop_types.resize(prop_names.size())
|
||||
prop_types.fill(4)
|
||||
var enums_exist := false
|
||||
for i in prop_names.size():
|
||||
prop_names[i] = loaded_entries[0][i]\
|
||||
.replace("\"", "")\
|
||||
.replace(" ", "_")\
|
||||
.replace("-", "_")\
|
||||
.replace(".", "_")\
|
||||
.replace(",", "_")\
|
||||
.replace("\t", "_")\
|
||||
.replace("/", "_")\
|
||||
.replace("\\", "_")\
|
||||
.to_lower()
|
||||
|
||||
var value = loaded_entries[1][i]
|
||||
var value_cast = str_to_var(value)
|
||||
# Don't guess Ints automatically - further rows might have floats
|
||||
if value_cast is Array or value_cast is Dictionary:
|
||||
prop_types[i] = ResourceTablesImport.PropType.COLLECTION
|
||||
elif value.is_valid_float():
|
||||
prop_types[i] = ResourceTablesImport.PropType.FLOAT
|
||||
elif value.begins_with("res://") && prop_names[i] != "resource_path":
|
||||
prop_types[i] = ResourceTablesImport.PropType.OBJECT
|
||||
elif value.length() == 6 or value.length() == 8 or (value.length() > 0 and value[0] == "#"):
|
||||
prop_types[i] = ResourceTablesImport.PropType.COLOR
|
||||
else:
|
||||
prop_types[i] = ResourceTablesImport.PropType.STRING
|
||||
enums_exist = true
|
||||
|
||||
|
||||
func load_external_script(script_res : Script):
|
||||
new_script = script_res
|
||||
var result := {}
|
||||
for x in script_res.get_script_property_list():
|
||||
|
||||
if x.hint != PROPERTY_HINT_ENUM or x.type != TYPE_INT:
|
||||
continue
|
||||
|
||||
var cur_value := ""
|
||||
var result_for_prop := {}
|
||||
result[prop_names.find(x.name)] = result_for_prop
|
||||
var hint_arr : Array = x.hint_string.split(",")
|
||||
for current_hint in hint_arr.size():
|
||||
var colon_found : int = hint_arr[current_hint].rfind(":")
|
||||
cur_value = hint_arr[current_hint]
|
||||
if cur_value == "":
|
||||
cur_value = "N_A"
|
||||
|
||||
if colon_found != -1:
|
||||
var value_split := cur_value.split(":")
|
||||
result_for_prop[value_split[1].to_upper()] = value_split[0]
|
||||
|
||||
else:
|
||||
result_for_prop[cur_value.to_upper()] = result_for_prop.size()
|
||||
|
||||
|
||||
func strings_to_resource(strings : Array, destination_path : String) -> Resource:
|
||||
if destination_path == "":
|
||||
destination_path = edited_path.get_base_dir().path_join("import/")
|
||||
DirAccess.make_dir_recursive_absolute(destination_path)
|
||||
|
||||
# If a full path is provided this catches that case
|
||||
var new_path : String = strings[prop_names.find(prop_used_as_filename)]
|
||||
|
||||
if !FileAccess.file_exists(new_path):
|
||||
new_path = destination_path.path_join(new_path).trim_suffix(".tres") + ".tres"
|
||||
|
||||
if !FileAccess.file_exists(new_path):
|
||||
new_path = (strings[prop_names.find(prop_used_as_filename)]
|
||||
.trim_prefix(destination_path)
|
||||
.trim_suffix(".tres") + ".tres"
|
||||
)
|
||||
if !new_path.begins_with("res://"):
|
||||
new_path = destination_path.path_join(new_path)
|
||||
|
||||
DirAccess.make_dir_recursive_absolute(new_path.get_base_dir())
|
||||
|
||||
var new_res : Resource
|
||||
if FileAccess.file_exists(new_path):
|
||||
new_res = load(new_path)
|
||||
|
||||
else:
|
||||
new_res = new_script.new()
|
||||
new_res.resource_path = new_path
|
||||
|
||||
for i in mini(prop_names.size(), strings.size()):
|
||||
var property_value = string_to_property(strings[i], i)
|
||||
# This is awful, but the workaround for typed casting
|
||||
# https://github.com/godotengine/godot/issues/72620
|
||||
if property_value is Array or property_value is Dictionary:
|
||||
var property_value_as_typed = new_res.get(prop_names[i])
|
||||
property_value_as_typed.assign(property_value)
|
||||
new_res.set(prop_names[i], property_value_as_typed)
|
||||
|
||||
else:
|
||||
new_res.set(prop_names[i], property_value)
|
||||
|
||||
if prop_used_as_filename != "":
|
||||
new_res.resource_path = new_path
|
||||
|
||||
return new_res
|
||||
|
||||
|
||||
func resource_to_strings(res : Resource):
|
||||
var strings := []
|
||||
strings.resize(prop_names.size())
|
||||
for i in prop_names.size():
|
||||
strings[i] = property_to_string(res.get(prop_names[i]), i)
|
||||
|
||||
return PackedStringArray(strings)
|
||||
|
||||
|
||||
func get_uniques(entries : Array) -> Dictionary:
|
||||
var result := {}
|
||||
for i in prop_types.size():
|
||||
if prop_types[i] is PropType and prop_types[i] == PropType.ENUM:
|
||||
var cur_value := ""
|
||||
result[i] = {}
|
||||
for j in entries.size():
|
||||
if j == 0 and remove_first_row: continue
|
||||
|
||||
cur_value = entries[j][i].capitalize().to_upper().replace(" ", "_")
|
||||
if cur_value == "":
|
||||
cur_value = "N_A"
|
||||
|
||||
if !result[i].has(cur_value):
|
||||
result[i][cur_value] = result[i].size()
|
||||
|
||||
return result
|
||||
|
||||
|
||||
static func change_name_to_format(name : String, case : int, delim : String):
|
||||
var string := name.capitalize().replace(" ", delim)
|
||||
if case == NameCasing.ALL_LOWER:
|
||||
return string.to_lower()
|
||||
|
||||
if case == NameCasing.CAPS_WORD_EXCEPT_FIRST:
|
||||
return string[0].to_lower() + string.substr(1)
|
||||
|
||||
if case == NameCasing.CAPS_WORD:
|
||||
return string
|
||||
|
||||
if case == NameCasing.ALL_CAPS:
|
||||
return string.to_upper()
|
||||
|
||||
|
||||
static func get_resource_property_types(res : Resource, properties : Array, uniques : Dictionary) -> Array:
|
||||
var result : Array[PropType] = []
|
||||
result.resize(properties.size())
|
||||
result.fill(PropType.STRING)
|
||||
var cur_type := 0
|
||||
for x in res.get_property_list():
|
||||
var poroperty_index := properties.find(x[&"name"])
|
||||
if poroperty_index == -1: continue
|
||||
if x[&"usage"] & PROPERTY_USAGE_EDITOR != 0:
|
||||
if x[&"hint"] == PROPERTY_HINT_ENUM:
|
||||
var enum_values : PackedStringArray = x[&"hint_string"].split(",")
|
||||
var enum_value_dict := {}
|
||||
var max_enum_value := 0
|
||||
for i in enum_values.size():
|
||||
var index_found : int = enum_values[i].find(":")
|
||||
if index_found == -1:
|
||||
enum_value_dict[enum_values[i].to_upper()] = max_enum_value
|
||||
max_enum_value += 1
|
||||
continue
|
||||
|
||||
var k = enum_values[i].left(index_found).to_upper()
|
||||
var v = enum_values[i].right(index_found + 1).to_int()
|
||||
enum_value_dict[k] = v
|
||||
|
||||
uniques[poroperty_index] = enum_value_dict
|
||||
result[poroperty_index] = PropType.ENUM
|
||||
|
||||
else:
|
||||
result[poroperty_index] = TYPE_MAP.get(x[&"type"], PropType.STRING)
|
||||
|
||||
return result
|
||||
@@ -0,0 +1 @@
|
||||
uid://dk6b4eht76v3x
|
||||
Reference in New Issue
Block a user