Initial commit to new repo
6
class_overview.gd
Normal file
@ -0,0 +1,6 @@
|
||||
extends PanelContainer
|
||||
|
||||
func _ready():
|
||||
pass
|
||||
|
||||
|
143
class_overview.tscn
Normal file
@ -0,0 +1,143 @@
|
||||
[gd_scene load_steps=4 format=1]
|
||||
|
||||
[ext_resource path="res://addons/DataEditor/class_overview.gd" type="Script" id=1]
|
||||
[ext_resource path="res://addons/DataEditor/style/light_gray_panel.tres" type="StyleBox" id=2]
|
||||
[ext_resource path="res://addons/DataEditor/fonts/droid_serif_bold.tres" type="DynamicFont" id=3]
|
||||
|
||||
[node name="ClassOverview" type="PanelContainer"]
|
||||
|
||||
anchor/right = 1
|
||||
anchor/bottom = 1
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 3
|
||||
size_flags/vertical = 3
|
||||
margin/left = 0.0
|
||||
margin/top = 0.0
|
||||
margin/right = 0.0
|
||||
margin/bottom = 0.0
|
||||
script/script = ExtResource( 1 )
|
||||
|
||||
[node name="Panel" type="Panel" parent="."]
|
||||
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 2
|
||||
margin/left = 7.0
|
||||
margin/top = 7.0
|
||||
margin/right = 1273.0
|
||||
margin/bottom = 593.0
|
||||
custom_styles/panel = ExtResource( 2 )
|
||||
|
||||
[node name="Body" type="VBoxContainer" parent="Panel"]
|
||||
|
||||
anchor/right = 1
|
||||
anchor/bottom = 1
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = false
|
||||
size_flags/horizontal = 3
|
||||
size_flags/vertical = 3
|
||||
margin/left = 10.0
|
||||
margin/top = 10.0
|
||||
margin/right = 10.0
|
||||
margin/bottom = 10.0
|
||||
alignment = 0
|
||||
|
||||
[node name="ClassPropertiesLabel" type="Label" parent="Panel/Body"]
|
||||
|
||||
focus/ignore_mouse = true
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 0
|
||||
margin/left = 0.0
|
||||
margin/top = 0.0
|
||||
margin/right = 1246.0
|
||||
margin/bottom = 2.0
|
||||
custom_fonts/font = ExtResource( 3 )
|
||||
text = "Static Class Properties"
|
||||
uppercase = true
|
||||
percent_visible = 1.0
|
||||
lines_skipped = 0
|
||||
max_lines_visible = -1
|
||||
|
||||
[node name="HSeparator" type="HSeparator" parent="Panel/Body"]
|
||||
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 2
|
||||
margin/left = 0.0
|
||||
margin/top = 6.0
|
||||
margin/right = 1246.0
|
||||
margin/bottom = 9.0
|
||||
|
||||
[node name="Control" type="Control" parent="Panel/Body"]
|
||||
|
||||
rect/min_size = Vector2( 0, 15 )
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 2
|
||||
margin/left = 0.0
|
||||
margin/top = 13.0
|
||||
margin/right = 1246.0
|
||||
margin/bottom = 28.0
|
||||
|
||||
[node name="Scroll" type="ScrollContainer" parent="Panel/Body"]
|
||||
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 3
|
||||
size_flags/vertical = 3
|
||||
margin/left = 0.0
|
||||
margin/top = 32.0
|
||||
margin/right = 1246.0
|
||||
margin/bottom = 566.0
|
||||
scroll/horizontal = true
|
||||
scroll/vertical = true
|
||||
|
||||
[node name="Statics" type="VBoxContainer" parent="Panel/Body/Scroll"]
|
||||
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = false
|
||||
size_flags/horizontal = 3
|
||||
size_flags/vertical = 2
|
||||
margin/left = 0.0
|
||||
margin/top = 0.0
|
||||
margin/right = 1246.0
|
||||
margin/bottom = 38.0
|
||||
alignment = 0
|
||||
|
||||
[node name="NoStaticsPropertiesLabel" type="Label" parent="Panel/Body/Scroll/Statics"]
|
||||
|
||||
focus/ignore_mouse = true
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 3
|
||||
size_flags/vertical = 0
|
||||
margin/left = 0.0
|
||||
margin/top = 0.0
|
||||
margin/right = 1246.0
|
||||
margin/bottom = 14.0
|
||||
text = "There are no static properties for this class. Click below to add one."
|
||||
percent_visible = 1.0
|
||||
lines_skipped = 0
|
||||
max_lines_visible = -1
|
||||
|
||||
[node name="AddStaticPropertyButton" type="Button" parent="Panel/Body/Scroll/Statics"]
|
||||
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 2
|
||||
margin/left = 0.0
|
||||
margin/top = 18.0
|
||||
margin/right = 1246.0
|
||||
margin/bottom = 38.0
|
||||
toggle_mode = false
|
||||
enabled_focus_mode = 2
|
||||
shortcut = null
|
||||
text = "Add static property"
|
||||
flat = false
|
||||
|
||||
|
50
class_properties.gd
Normal file
@ -0,0 +1,50 @@
|
||||
tool
|
||||
extends Panel
|
||||
|
||||
var property_item_class = preload("property_item.tscn")
|
||||
|
||||
onready var class_properties_box = get_node("Body/Scroll/ClassProperties")
|
||||
onready var no_class_properties_label = get_node("Body/Scroll/ClassProperties/NoClassPropertiesLabel")
|
||||
|
||||
var item = null
|
||||
|
||||
signal on_item_changed(item)
|
||||
|
||||
|
||||
func build_properties(item):
|
||||
self.item = item
|
||||
for node in class_properties_box.get_children():
|
||||
if node.has_meta("property"):
|
||||
class_properties_box.remove_child(node)
|
||||
|
||||
|
||||
var number_of_properties = 0
|
||||
for property in item.get_property_list():
|
||||
if property["usage"] == (PROPERTY_USAGE_SCRIPT_VARIABLE + PROPERTY_USAGE_STORAGE + PROPERTY_USAGE_EDITOR + PROPERTY_USAGE_NETWORK):
|
||||
no_class_properties_label.hide()
|
||||
number_of_properties += 1
|
||||
var property_item = property_item_class.instance()
|
||||
var property_name = property["name"]
|
||||
var value = item.get(property_name)
|
||||
property_item.initialize(property_name, property["type"], value, property["hint"], property["hint_string"])
|
||||
property_item.connect("property_item_load_button_down", self, "_property_item_requests_file_dialog", [])
|
||||
var changed_values = []
|
||||
property_item.connect("on_property_value_changed", self, "item_changed", changed_values)
|
||||
property_item.set_meta("property", true)
|
||||
class_properties_box.add_child(property_item)
|
||||
pass
|
||||
if number_of_properties == 0:
|
||||
no_class_properties_label.show()
|
||||
|
||||
|
||||
func item_changed(property, value):
|
||||
if item:
|
||||
item.set(property, value)
|
||||
emit_signal("on_item_changed", item)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
116
class_properties.tscn
Normal file
@ -0,0 +1,116 @@
|
||||
[gd_scene load_steps=4 format=1]
|
||||
|
||||
[ext_resource path="res://addons/DataEditor/style/light_gray_panel.tres" type="StyleBox" id=1]
|
||||
[ext_resource path="res://addons/DataEditor/class_properties.gd" type="Script" id=2]
|
||||
[ext_resource path="res://addons/DataEditor/fonts/droid_serif_bold.tres" type="DynamicFont" id=3]
|
||||
|
||||
[node name="ClassProperties" type="Panel"]
|
||||
|
||||
anchor/right = 1
|
||||
anchor/bottom = 1
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 3
|
||||
size_flags/vertical = 3
|
||||
margin/left = 0.0
|
||||
margin/top = 0.0
|
||||
margin/right = 0.0
|
||||
margin/bottom = 0.0
|
||||
custom_styles/panel = ExtResource( 1 )
|
||||
script/script = ExtResource( 2 )
|
||||
|
||||
[node name="Body" type="VBoxContainer" parent="."]
|
||||
|
||||
anchor/right = 1
|
||||
anchor/bottom = 1
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = false
|
||||
size_flags/horizontal = 3
|
||||
size_flags/vertical = 3
|
||||
margin/left = 10.0
|
||||
margin/top = 10.0
|
||||
margin/right = 10.0
|
||||
margin/bottom = 10.0
|
||||
alignment = 0
|
||||
|
||||
[node name="TitleLabel" type="Label" parent="Body"]
|
||||
|
||||
focus/ignore_mouse = true
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 0
|
||||
margin/left = 0.0
|
||||
margin/top = 0.0
|
||||
margin/right = 1260.0
|
||||
margin/bottom = 2.0
|
||||
custom_fonts/font = ExtResource( 3 )
|
||||
text = "Class Properties"
|
||||
uppercase = true
|
||||
percent_visible = 1.0
|
||||
lines_skipped = 0
|
||||
max_lines_visible = -1
|
||||
|
||||
[node name="HSeparator" type="HSeparator" parent="Body"]
|
||||
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 2
|
||||
margin/left = 0.0
|
||||
margin/top = 6.0
|
||||
margin/right = 1260.0
|
||||
margin/bottom = 9.0
|
||||
|
||||
[node name="Spacer" type="Control" parent="Body"]
|
||||
|
||||
rect/min_size = Vector2( 0, 15 )
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 2
|
||||
margin/left = 0.0
|
||||
margin/top = 13.0
|
||||
margin/right = 1260.0
|
||||
margin/bottom = 28.0
|
||||
|
||||
[node name="Scroll" type="ScrollContainer" parent="Body"]
|
||||
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 3
|
||||
size_flags/vertical = 3
|
||||
margin/left = 0.0
|
||||
margin/top = 32.0
|
||||
margin/right = 1260.0
|
||||
margin/bottom = 580.0
|
||||
scroll/horizontal = true
|
||||
scroll/vertical = true
|
||||
|
||||
[node name="ClassProperties" type="VBoxContainer" parent="Body/Scroll"]
|
||||
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = false
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 2
|
||||
margin/left = 0.0
|
||||
margin/top = 0.0
|
||||
margin/right = 454.0
|
||||
margin/bottom = 14.0
|
||||
alignment = 0
|
||||
|
||||
[node name="NoClassPropertiesLabel" type="Label" parent="Body/Scroll/ClassProperties"]
|
||||
|
||||
focus/ignore_mouse = true
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 3
|
||||
size_flags/vertical = 0
|
||||
margin/left = 0.0
|
||||
margin/top = 0.0
|
||||
margin/right = 454.0
|
||||
margin/bottom = 14.0
|
||||
text = "There are currently no class properties. Edit the Class to add new ones."
|
||||
percent_visible = 1.0
|
||||
lines_skipped = 0
|
||||
max_lines_visible = -1
|
||||
|
||||
|
77
custom_properties.gd
Normal file
@ -0,0 +1,77 @@
|
||||
tool
|
||||
extends Panel
|
||||
|
||||
var item = null
|
||||
var property_item_class = preload("property_item.tscn")
|
||||
var remove_icon = preload("icons/icon_remove.png")
|
||||
|
||||
|
||||
onready var custom_properties_box = get_node("Body/Scroll/CustomProperties")
|
||||
onready var no_custom_properties_label = get_node("Body/Scroll/CustomProperties/NoCustomPropertiesLabel")
|
||||
|
||||
onready var item_manager = null # Item Manager, used to load, modify and save items
|
||||
|
||||
signal on_item_changed(item)
|
||||
signal new_custom_property_created
|
||||
signal custom_property_add_requested
|
||||
signal custom_property_delete_requested(custom_property_id)
|
||||
|
||||
#TODO: Somehow the properties are initialized twice
|
||||
|
||||
func _ready():
|
||||
pass
|
||||
self.item_manager = Globals.get("item_manager")
|
||||
|
||||
func build_properties(item):
|
||||
self.item = item
|
||||
var properties = item._custom_properties
|
||||
for node in custom_properties_box.get_children():
|
||||
if node.has_meta("property"):
|
||||
custom_properties_box.remove_child(node)
|
||||
|
||||
var number_of_properties = 0
|
||||
var property_names = properties.keys()
|
||||
property_names.sort()
|
||||
for property_name in property_names:
|
||||
no_custom_properties_label.hide()
|
||||
number_of_properties += 1
|
||||
|
||||
var container = MarginContainer.new()
|
||||
var property_item = property_item_class.instance()
|
||||
var type = properties[property_name][0]
|
||||
|
||||
# If there already is a value, read it, otherwise set it to null
|
||||
var value = null
|
||||
if properties[property_name].size() == 2:
|
||||
value = properties[property_name][1]
|
||||
|
||||
property_item.initialize(property_name, type, value, 0, "", true)
|
||||
property_item.connect("custom_property_delete_requested", self, "emit_signal", ["custom_property_delete_requested", property_name, ])
|
||||
property_item.connect("property_item_load_button_down", self, "_property_item_requests_file_dialog", [])
|
||||
var changed_values = []
|
||||
property_item.connect("on_property_value_changed", self, "item_changed", changed_values)
|
||||
|
||||
container.set_meta("property", true)
|
||||
container.add_child(property_item)
|
||||
custom_properties_box.add_child(container)
|
||||
|
||||
pass
|
||||
if number_of_properties == 0:
|
||||
no_custom_properties_label.show()
|
||||
|
||||
|
||||
# Fires signal when the item's custom properties is to be updated, delegates to data_editor_gui.
|
||||
func item_changed(property, value):
|
||||
if item:
|
||||
item._custom_properties[property][1] = value
|
||||
emit_signal("on_item_changed", item)
|
||||
|
||||
|
||||
# Delegates the deletion
|
||||
func delete_custom_property(property_name):
|
||||
emit_signal("custom_property_delete_requested", property_name)
|
||||
|
||||
|
||||
# Fires signal when the item's custom properties is to be updated, delegates to data_editor_gui.func _on_NewCustomPropertyButton_button_down():
|
||||
func _on_NewCustomPropertyButton_button_down():
|
||||
emit_signal("custom_property_add_requested")
|
134
custom_properties.tscn
Normal file
@ -0,0 +1,134 @@
|
||||
[gd_scene load_steps=4 format=1]
|
||||
|
||||
[ext_resource path="res://addons/DataEditor/style/light_gray_panel.tres" type="StyleBox" id=1]
|
||||
[ext_resource path="res://addons/DataEditor/custom_properties.gd" type="Script" id=2]
|
||||
[ext_resource path="res://addons/DataEditor/fonts/droid_serif_bold.tres" type="DynamicFont" id=3]
|
||||
|
||||
[node name="Panel" type="Panel"]
|
||||
|
||||
anchor/right = 1
|
||||
anchor/bottom = 1
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 2
|
||||
margin/left = 0.0
|
||||
margin/top = 0.0
|
||||
margin/right = 0.0
|
||||
margin/bottom = 0.0
|
||||
custom_styles/panel = ExtResource( 1 )
|
||||
script/script = ExtResource( 2 )
|
||||
|
||||
[node name="Body" type="VBoxContainer" parent="."]
|
||||
|
||||
anchor/right = 1
|
||||
anchor/bottom = 1
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = false
|
||||
size_flags/horizontal = 3
|
||||
size_flags/vertical = 3
|
||||
margin/left = 10.0
|
||||
margin/top = 10.0
|
||||
margin/right = 10.0
|
||||
margin/bottom = 10.0
|
||||
alignment = 0
|
||||
|
||||
[node name="TitleLabel" type="Label" parent="Body"]
|
||||
|
||||
focus/ignore_mouse = true
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 0
|
||||
margin/left = 0.0
|
||||
margin/top = 0.0
|
||||
margin/right = 1260.0
|
||||
margin/bottom = 2.0
|
||||
custom_fonts/font = ExtResource( 3 )
|
||||
text = "Custom Properties"
|
||||
uppercase = true
|
||||
percent_visible = 1.0
|
||||
lines_skipped = 0
|
||||
max_lines_visible = -1
|
||||
|
||||
[node name="HSeparator" type="HSeparator" parent="Body"]
|
||||
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 2
|
||||
margin/left = 0.0
|
||||
margin/top = 6.0
|
||||
margin/right = 1260.0
|
||||
margin/bottom = 9.0
|
||||
|
||||
[node name="Spacer" type="Control" parent="Body"]
|
||||
|
||||
rect/min_size = Vector2( 0, 15 )
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 2
|
||||
margin/left = 0.0
|
||||
margin/top = 13.0
|
||||
margin/right = 1260.0
|
||||
margin/bottom = 28.0
|
||||
|
||||
[node name="Scroll" type="ScrollContainer" parent="Body"]
|
||||
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 3
|
||||
size_flags/vertical = 3
|
||||
margin/left = 0.0
|
||||
margin/top = 32.0
|
||||
margin/right = 1260.0
|
||||
margin/bottom = 556.0
|
||||
scroll/horizontal = true
|
||||
scroll/vertical = true
|
||||
|
||||
[node name="CustomProperties" type="VBoxContainer" parent="Body/Scroll"]
|
||||
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = false
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 3
|
||||
margin/left = 0.0
|
||||
margin/top = 0.0
|
||||
margin/right = 441.0
|
||||
margin/bottom = 524.0
|
||||
alignment = 0
|
||||
|
||||
[node name="NoCustomPropertiesLabel" type="Label" parent="Body/Scroll/CustomProperties"]
|
||||
|
||||
focus/ignore_mouse = true
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 0
|
||||
margin/left = 0.0
|
||||
margin/top = 0.0
|
||||
margin/right = 441.0
|
||||
margin/bottom = 14.0
|
||||
text = "There are no custom properties for this item. Click below to add one."
|
||||
percent_visible = 1.0
|
||||
lines_skipped = 0
|
||||
max_lines_visible = -1
|
||||
|
||||
[node name="NewCustomPropertyButton" type="Button" parent="Body"]
|
||||
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 2
|
||||
margin/left = 0.0
|
||||
margin/top = 560.0
|
||||
margin/right = 1260.0
|
||||
margin/bottom = 580.0
|
||||
toggle_mode = false
|
||||
enabled_focus_mode = 2
|
||||
shortcut = null
|
||||
text = "Add new custom property"
|
||||
flat = false
|
||||
|
||||
[connection signal="button_down" from="Body/NewCustomPropertyButton" to="." method="_on_NewCustomPropertyButton_button_down"]
|
||||
|
||||
|
110
data.gd
Normal file
@ -0,0 +1,110 @@
|
||||
extends Node
|
||||
|
||||
var item_manager = null
|
||||
var items = {}
|
||||
var values = {}
|
||||
|
||||
signal any_value_changed(item, property, value)
|
||||
|
||||
|
||||
var signals_any_value_changed = []
|
||||
var signals_item_class_any_value_changed = []
|
||||
var signals_item_any_values_changed = []
|
||||
var signals_item_value_changed = []
|
||||
|
||||
func _init():
|
||||
self.item_manager = preload("item_manager.gd").new()
|
||||
self.items = item_manager.items
|
||||
|
||||
# TODO: Allow eager loading
|
||||
|
||||
|
||||
|
||||
func get_item(item_class, id):
|
||||
return item_manager.get_item(item_class, id)
|
||||
|
||||
func get_items(item_class):
|
||||
return item_manager.get_items(item_class)
|
||||
|
||||
# if items[item_class].has(id):
|
||||
# return items[item_class][id]
|
||||
# else:
|
||||
# _load_item(item_class, id)
|
||||
# return items[item_class][id]
|
||||
|
||||
func _load_item(item_class, id):
|
||||
items[item_class][id] = item_manager.load_item(item_class, id)
|
||||
# values[item_class][id] = {}
|
||||
|
||||
func load_values_of_all_items():
|
||||
pass
|
||||
|
||||
func load_item_value(item, property):
|
||||
return get_progress(item._class, item._id, property)
|
||||
|
||||
|
||||
func get_progress(item_class, id, property):
|
||||
if items[item_class].has(id) and items[item_class][id].has(property):
|
||||
return items[item_class][id][property]
|
||||
|
||||
|
||||
func set_progress(item_class, id, property, value):
|
||||
var item = item_manager.get_item(item_class, id)
|
||||
var has_value = item.get(property)
|
||||
if item and has_value:
|
||||
item.set(property, value)
|
||||
emit_signal("any_value_changed", item, property, value)
|
||||
|
||||
var signal_name = ""
|
||||
signal_name = item_class
|
||||
# Class signal
|
||||
if has_user_signal(signal_name):
|
||||
emit_signal(signal_name, item, property, value)
|
||||
|
||||
# Item signal
|
||||
signal_name = item_class + "|" + id
|
||||
if has_user_signal(signal_name):
|
||||
emit_signal(signal_name, item, property, value)
|
||||
|
||||
# Property signal
|
||||
signal_name = item_class + "|" + id + "|" + property
|
||||
if has_user_signal(signal_name):
|
||||
emit_signal(signal_name, item, property, value)
|
||||
|
||||
return true
|
||||
else:
|
||||
return false
|
||||
|
||||
func observe_all_changes(observer, method, binds=[], flags = 0):
|
||||
self.connect("any_value_changed", observer, method, binds, flags)
|
||||
|
||||
func observe_class(observer, item_class, method, binds=[], flags = 0):
|
||||
self.add_user_signal(item_class) # TODO: Args
|
||||
self.connect(item_class, observer, method, binds, flags)
|
||||
|
||||
func observe_item(observer, item, method, binds=[], flags = 0):
|
||||
var signal_name = item._class + "|" + item._id
|
||||
if not has_user_signal(signal_name):
|
||||
self.add_user_signal(signal_name) # TODO: Args
|
||||
self.connect(signal_name, observer, method, binds, flags)
|
||||
|
||||
func observe_item_property(observer, item, property, method, binds=[], flags = 0):
|
||||
var signal_name = item._class + "|" + item._id + "|" + property
|
||||
if not has_user_signal(signal_name):
|
||||
self.add_user_signal(signal_name) # TODO: Args
|
||||
self.connect(signal_name, observer, method, binds, flags)
|
||||
|
||||
func stop_observing_all_changes(observer):
|
||||
pass
|
||||
# observer.disconnect(
|
||||
|
||||
#TODO: func block_signals()
|
||||
|
||||
|
||||
func set_item_progress(item, property, value):
|
||||
set_progress(item._class, item._id, property, value)
|
||||
|
||||
func get_progress_by_item(item):
|
||||
|
||||
|
||||
|
97
data_editor.gd
Normal file
@ -0,0 +1,97 @@
|
||||
tool
|
||||
extends EditorPlugin
|
||||
|
||||
var data_editor_class = preload("data_editor_gui.tscn")
|
||||
var data_class = preload("data.gd")
|
||||
var gui = null
|
||||
|
||||
var all_items = {}
|
||||
|
||||
signal data_item_class_opened(item_class)
|
||||
|
||||
func _enter_tree():
|
||||
OS.set_low_processor_usage_mode(true)
|
||||
check_for_data_singleton()
|
||||
check_plugin_settings()
|
||||
gui = data_editor_class.instance()
|
||||
get_editor_viewport().add_child(gui)
|
||||
gui.set_area_as_parent_rect()
|
||||
gui.hide()
|
||||
|
||||
|
||||
# Remove control and data singleton
|
||||
func _exit_tree():
|
||||
OS.set_low_processor_usage_mode(false)
|
||||
get_editor_viewport().remove_child(gui)
|
||||
gui.free()
|
||||
var config = ConfigFile.new()
|
||||
var status = config.load("res://engine.cfg")
|
||||
if status == OK:
|
||||
if config.has_section_key("autoload", "data"):
|
||||
config.set_value("autoload", "data", null)
|
||||
config.save("res://engine.cfg")
|
||||
# Check if the Classes and Data folders exist
|
||||
Globals.clear("item_manager")
|
||||
|
||||
func _ready():
|
||||
gui.connect("class_edit_requested", self, "edit_class", [])
|
||||
Globals.set("debug_is_editor", true)
|
||||
|
||||
# Opens the selected class in the Script Editor
|
||||
func edit_class(item_class):
|
||||
edit_resource(item_class)
|
||||
|
||||
|
||||
func check_for_data_singleton():
|
||||
var config = ConfigFile.new()
|
||||
var status = config.load("res://engine.cfg")
|
||||
|
||||
|
||||
if status == OK:
|
||||
if not config.has_section_key("autoload", "data"):
|
||||
config.set_value("autoload", "data", "*res://addons/DataEditor/data.gd")
|
||||
config.save("res://engine.cfg")
|
||||
#var directory = Directory.new()
|
||||
#directory.copy("res://engine.cfg", "res://engine.cfg_BACKUP")
|
||||
|
||||
# Load the plugin settings and adds default if they do not exist.
|
||||
# TODO: Obtain defaults from dialog
|
||||
func check_plugin_settings():
|
||||
var config = ConfigFile.new()
|
||||
var status = config.load("res://addons/DataEditor/plugin.cfg")
|
||||
if status == OK:
|
||||
if not config.has_section_key("custom", "class_directory"):
|
||||
config.set_value("custom", "class_directory", "res://classes")
|
||||
# TODO: Create folders
|
||||
if not config.has_section_key("custom", "extension"):
|
||||
config.set_value("custom", "extension", "json")
|
||||
if not config.has_section_key("custom", "output_directory"):
|
||||
config.set_value("custom", "output_directory", "res://data")
|
||||
# TODO: Create folders
|
||||
if not config.has_section_key("custom", "password"):
|
||||
config.set_value("custom", "password", "")
|
||||
if not config.has_section_key("custom", "sanitize_ids"):
|
||||
config.set_value("custom", "sanitize_ids", true)
|
||||
if not config.has_section_key("custom", "serializer"):
|
||||
config.set_value("custom", "serializer", "json")
|
||||
config.save("res://addons/DataEditor/plugin.cfg")
|
||||
|
||||
|
||||
# Virtual: Name of the tool button on top
|
||||
func get_name():
|
||||
return "Data"
|
||||
|
||||
# Virtual: Makes sure that the control owns the main screen
|
||||
func has_main_screen():
|
||||
return true
|
||||
|
||||
# Virtual:
|
||||
func make_visible(visible):
|
||||
if(visible):
|
||||
gui.reload()
|
||||
gui.show()
|
||||
|
||||
else:
|
||||
gui.hide()
|
||||
|
||||
|
431
data_editor_gui.gd
Normal file
@ -0,0 +1,431 @@
|
||||
tool
|
||||
extends Control
|
||||
|
||||
var selected_item = null
|
||||
var selected_id = null
|
||||
var selected_class = null
|
||||
|
||||
|
||||
onready var item_tree = get_node("VBox/Body/ItemTree")
|
||||
onready var id_label = get_node("VBox/Body/Content/VBox/Container/ItemIdLabel")
|
||||
|
||||
onready var instance_details = get_node("VBox/Body/Content/VBox/InstanceDetails/")
|
||||
onready var class_properties = get_node("VBox/Body/Content/VBox/InstanceDetails/HBox/ClassProperties")
|
||||
onready var custom_properties = get_node("VBox/Body/Content/VBox/InstanceDetails/HBox/CustomProperties")
|
||||
onready var class_overview = get_node("VBox/Body/Content/VBox/ClassOverview")
|
||||
onready var no_classes = get_node("VBox/Body/Content/VBox/NoClasses")
|
||||
|
||||
#onready var last_modified_date = get_node("VBox/Body/Content/VBox/Container/GridContainer/LastModifiedDate")
|
||||
#onready var created_date = get_node("VBox/Body/Content/VBox/Container/GridContainer/CreatedDate")
|
||||
|
||||
|
||||
onready var new_custom_property_dialog = get_node("NewCustomPropertyDialog")
|
||||
onready var new_custom_property_name = get_node("NewCustomPropertyDialog/LineEdit")
|
||||
onready var new_custom_property_type_options = get_node("NewCustomPropertyDialog/TypeOptions")
|
||||
|
||||
onready var add_button = get_node("VBox/Head/Add")
|
||||
onready var delete_button = get_node("VBox/Head/Delete")
|
||||
onready var duplicate_button = get_node("VBox/Head/Duplicate")
|
||||
onready var change_display_name_button = get_node("VBox/Body/Content/VBox/Container/HBox/DisplayName")
|
||||
onready var rename_button = get_node("VBox/Head/Rename")
|
||||
onready var save_button = get_node("VBox/Head/Save")
|
||||
onready var save_all_button = get_node("VBox/Head/SaveAll")
|
||||
|
||||
onready var copy_id_button = get_node("VBox/Body/Content/VBox/Container/HBox/CopyId")
|
||||
onready var edit_class_button = get_node("VBox/Body/Content/VBox/Container/HBox/EditClass")
|
||||
|
||||
# Dialogs
|
||||
onready var input_dialog = get_node("InputDialog")
|
||||
|
||||
onready var new_item_class_dialog = get_node("NewClassDialog")
|
||||
onready var new_item_class_name = get_node("NewClassDialog/ClassName")
|
||||
onready var new_item_class_icon = get_node("NewClassDialog/ClassIconPath")
|
||||
onready var new_item_class_icon_dialog = get_node("NewClassDialog/ClassIconFileDialog")
|
||||
|
||||
|
||||
#onready var delete_item_dialog = get_node("DeleteItemDialog")
|
||||
|
||||
#onready var rename_item_dialog = get_node("RenameItemDialog")
|
||||
#onready var rename_item_id = get_node("RenameItemDialog/Id")
|
||||
|
||||
#onready var duplicate_item_dialog = get_node("DuplicateItemDialog")
|
||||
#onready var duplicate_item_id = get_node("DuplicateItemDialog/Id")
|
||||
|
||||
#onready var delete_class_dialog = get_node("DeleteClassDialog")
|
||||
|
||||
#onready var display_name_dialog = get_node("DisplayNameDialog")
|
||||
#onready var display_name_dialog_name = get_node("DisplayNameDialog/Name")
|
||||
|
||||
onready var warn_dialog = get_node("WarnDialog")
|
||||
onready var options_screen = get_node("OptionsDialog")
|
||||
|
||||
var item_tree_class = preload("item_tree.tscn")
|
||||
#var active_element = null
|
||||
var item_manager = null
|
||||
|
||||
signal class_edit_requested(script)
|
||||
signal input_dialog_confirmed(text1, text2)
|
||||
|
||||
# First initialize the item manager which is used for loading, saving and configs
|
||||
func _init():
|
||||
item_manager = preload("item_manager.gd").new()
|
||||
#item_manager.set_name("ItemManager")
|
||||
#Globals.set("item_manager", item_manager)
|
||||
# self.add_child(item_manager)
|
||||
# item_manager.raise()
|
||||
|
||||
|
||||
|
||||
|
||||
func _ready():
|
||||
#add_button.set_shortcut(create_shortcut(KEY_MASK_CMD|KEY_N))
|
||||
#delete_button.set_shortcut(create_shortcut(KEY_DELETE))
|
||||
#save_button.set_shortcut(create_shortcut(KEY_MASK_SHIFT|KEY_MASK_CMD|KEY_S))
|
||||
#save_all_button.set_shortcut(create_shortcut(KEY_MASK_SHIFT|KEY_MASK_CMD|KEY_MASK_ALT|KEY_S))
|
||||
|
||||
Globals.set("debug_is_editor", false)
|
||||
# Tree signals
|
||||
item_tree.connect("on_new_item_pressed", self, "handle_actions", ["add"])
|
||||
item_tree.connect("on_rename_pressed", self, "handle_actions", ["rename"])
|
||||
item_tree.connect("on_delete_pressed", self, "handle_actions", ["delete"])
|
||||
item_tree.connect("on_duplicate_pressed", self, "handle_actions", ["duplicate"])
|
||||
item_tree.connect("on_item_selected", self, "change_item_context", [])
|
||||
item_tree.connect("on_open", self, "open_item", [])
|
||||
|
||||
custom_properties.connect("custom_property_add_requested", self, "handle_actions", ["add_custom_property"])
|
||||
custom_properties.connect("new_custom_property_created", self, "handle_actions", ["add_custom_property"])
|
||||
custom_properties.connect("custom_property_delete_requested", self, "delete_custom_property", [])
|
||||
class_properties.connect("on_item_changed", self, "toggle_item_dirty_state", [])
|
||||
|
||||
options_screen.connect("extension_changed", item_manager, "rename_extension_of_all_items", [])
|
||||
options_screen.connect("encryption_changed", item_manager, "delete_and_resave", [])
|
||||
|
||||
item_manager.connect("class_insertion_failed", self, "show_warning", [])
|
||||
item_manager.connect("item_insertion_failed", self, "show_warning", [])
|
||||
item_manager.connect("custom_property_insertion_failed", self, "show_warning", [])
|
||||
item_manager.connect("item_duplication_failed", self, "show_warning", [])
|
||||
|
||||
# Add types to the custom property type dropdown
|
||||
var type_names = item_manager.type_names.keys()
|
||||
type_names.sort()
|
||||
new_custom_property_type_options.clear()
|
||||
var index = 0
|
||||
for type in type_names:
|
||||
new_custom_property_type_options.add_item(type)
|
||||
new_custom_property_type_options.set_item_metadata(index, item_manager.type_names[type])
|
||||
index += 1
|
||||
|
||||
# No classes available
|
||||
var has_no_classes = item_manager.classes.size() == 0
|
||||
if has_no_classes:
|
||||
change_display_name_button.set_disabled(has_no_classes)
|
||||
duplicate_button.set_disabled(true)
|
||||
save_button.set_disabled(true)
|
||||
save_all_button.set_disabled(true)
|
||||
save_all_button.set_disabled(true)
|
||||
rename_button.set_disabled(true)
|
||||
add_button.set_disabled(true)
|
||||
delete_button.set_disabled(true)
|
||||
copy_id_button.set_disabled(true)
|
||||
edit_class_button.set_disabled(true)
|
||||
no_classes.show()
|
||||
id_label.set_text("No Classes")
|
||||
instance_details.hide()
|
||||
class_overview.hide()
|
||||
else:
|
||||
# Select the first item in the tree when loading the GUI
|
||||
change_item_context(selected_item, selected_class)
|
||||
|
||||
# TODO: Other OS...
|
||||
func open_item():
|
||||
var item_path = item_manager.get_full_path(selected_item)
|
||||
print(item_path)
|
||||
OS.execute("explorer", [item_path], false)
|
||||
|
||||
func change_item_context(selected_item, selected_class):
|
||||
|
||||
if selected_class:
|
||||
self.selected_class = selected_class
|
||||
|
||||
|
||||
|
||||
# TODO: Move to method, clean up
|
||||
var has_no_classes = item_manager.classes.size() == 0
|
||||
if has_no_classes:
|
||||
change_display_name_button.set_disabled(has_no_classes)
|
||||
duplicate_button.set_disabled(true)
|
||||
save_button.set_disabled(true)
|
||||
save_all_button.set_disabled(true)
|
||||
save_all_button.set_disabled(true)
|
||||
rename_button.set_disabled(true)
|
||||
add_button.set_disabled(true)
|
||||
delete_button.set_disabled(true)
|
||||
copy_id_button.set_disabled(true)
|
||||
edit_class_button.set_disabled(true)
|
||||
no_classes.show()
|
||||
instance_details.hide()
|
||||
class_overview.hide()
|
||||
id_label.set_text("No Classes")
|
||||
|
||||
|
||||
# An item was selected
|
||||
if selected_item:
|
||||
|
||||
# Context was lost, e.g. because of changes to the classes. Get a new copy from the item_manager
|
||||
if selected_item and not selected_item.get("_id"):
|
||||
self.item_manager.load_manager()
|
||||
self.item_tree.load_tree(true)
|
||||
selected_item = item_tree.select_first_element()
|
||||
|
||||
|
||||
change_display_name_button.set_disabled(false)
|
||||
duplicate_button.set_disabled(false)
|
||||
save_button.set_disabled(false)
|
||||
save_all_button.set_disabled(false)
|
||||
rename_button.set_disabled(false)
|
||||
add_button.set_disabled(false)
|
||||
delete_button.set_disabled(false)
|
||||
copy_id_button.set_disabled(false)
|
||||
edit_class_button.set_disabled(false)
|
||||
|
||||
self.selected_item = selected_item
|
||||
self.selected_id = selected_item._id
|
||||
class_overview.hide()
|
||||
no_classes.hide()
|
||||
instance_details.show()
|
||||
if selected_item._display_name == selected_id:
|
||||
id_label.set_text(selected_id)
|
||||
else:
|
||||
id_label.set_text(selected_item._display_name + " (" + selected_id + ")")
|
||||
|
||||
class_properties.build_properties(selected_item)
|
||||
custom_properties.build_properties(selected_item)
|
||||
# A class was selected
|
||||
elif selected_class:
|
||||
change_display_name_button.set_disabled(true)
|
||||
duplicate_button.set_disabled(true)
|
||||
save_button.set_disabled(true)
|
||||
save_all_button.set_disabled(false)
|
||||
rename_button.set_disabled(false)
|
||||
add_button.set_disabled(false)
|
||||
delete_button.set_disabled(false)
|
||||
copy_id_button.set_disabled(false)
|
||||
edit_class_button.set_disabled(false)
|
||||
|
||||
self.selected_item = null
|
||||
self.selected_id = null
|
||||
id_label.set_text(selected_class.capitalize() + " " + (selected_class))
|
||||
class_overview.show()
|
||||
instance_details.hide()
|
||||
no_classes.hide()
|
||||
|
||||
|
||||
|
||||
func _on_ItemTree_on_new_item_created(new_item):
|
||||
selected_item = new_item
|
||||
|
||||
|
||||
func create_shortcut(keys):
|
||||
var short_cut = ShortCut.new()
|
||||
var input_event = InputEvent()
|
||||
input_event.type = InputEvent.KEY
|
||||
input_event.ID = keys
|
||||
short_cut.set_shortcut(input_event)
|
||||
|
||||
func warn_about_reload():
|
||||
if item_manager.has_unsaved_items():
|
||||
input_dialog.popup(self, "reload_confirmed", tr("Confirm reload"), tr("Some changes have not been saved. \nThey will be discarded if you proceed. Are you sure you want to perform this action?"))
|
||||
|
||||
func reload():
|
||||
item_manager.load_manager()
|
||||
item_tree.load_tree(true)
|
||||
if item_manager.get_item(selected_class, selected_id):
|
||||
item_tree.select_item(item_manager.get_item(selected_class, selected_id))
|
||||
|
||||
|
||||
#var last_selected_class = selected_class
|
||||
#var last_selected_id = selected_id
|
||||
#selected_item = item_manager.get_item(last_selected_class, last_selected_id)
|
||||
#if selected_item:
|
||||
#selected_id = selected_item._id
|
||||
#change_item_context(selected_item, selected_class)
|
||||
|
||||
|
||||
func toggle_item_dirty_state(item):
|
||||
item._dirty = true
|
||||
item_tree.set_tree_item_label_text(item)
|
||||
|
||||
#func show_delete_custom_property_dialog(property_name):
|
||||
# input_dialog.popup(self, "delete_custom_property", tr("Delete Custom Property"), tr("Are you sure you want to delete this property?"))
|
||||
|
||||
# Validation takes place in the item manager
|
||||
func _on_NewCustomPropertyDialog_confirmed():
|
||||
var custom_property_id = new_custom_property_name.get_text().strip_edges()
|
||||
var custom_property_type = new_custom_property_type_options.get_selected_metadata()
|
||||
var success = item_manager.add_custom_property(selected_item, custom_property_id, custom_property_type)
|
||||
if success:
|
||||
item_tree.set_tree_item_label_text(selected_item)
|
||||
toggle_item_dirty_state(selected_item)
|
||||
custom_properties.build_properties(selected_item)
|
||||
|
||||
|
||||
|
||||
# TODO: Show confirmation dialog
|
||||
func delete_custom_property(property_name):
|
||||
#input_dialog.popup(self, "_on_delete__confirmed", tr("New Item"), tr("Please enter an ID and optionally a display name the new item"), tr("ID"), "", tr("Display Name (optional)"), "")
|
||||
item_manager.delete_custom_property(selected_item, property_name)
|
||||
toggle_item_dirty_state(selected_item)
|
||||
custom_properties.build_properties(selected_item)
|
||||
|
||||
|
||||
# TODO: New Class Dialog is still a mess
|
||||
func _on_AddClassButton_button_down():
|
||||
new_item_class_name.set_text("")
|
||||
new_item_class_icon.set_text("")
|
||||
new_item_class_icon_dialog.set_current_path("")
|
||||
new_item_class_dialog.popup_centered()
|
||||
new_item_class_name.grab_focus()
|
||||
|
||||
|
||||
func _on_NewClassDialog_confirmed():
|
||||
var name = new_item_class_name.get_text().to_lower()
|
||||
var icon_path = new_item_class_icon.get_text()
|
||||
item_manager.create_class(name, icon_path)
|
||||
item_tree.load_tree()
|
||||
item_tree.select_class(name)
|
||||
reload()
|
||||
edit_class()
|
||||
|
||||
|
||||
# New Class Dialog
|
||||
func _on_NewClassIconSearchButton_button_down():
|
||||
new_item_class_icon_dialog.popup_centered()
|
||||
|
||||
func _on_NewClassIconFileDialog_file_selected(path):
|
||||
new_item_class_icon.set_text(path)
|
||||
|
||||
|
||||
func handle_actions(action, argument = ""):
|
||||
if action == "add":
|
||||
input_dialog.popup(self, "_on_add_item_confirmed", tr("New Item"), tr("Please enter an ID for and optionally a display name the new item"), tr("ID"), "", tr("Display Name (optional)"), "")
|
||||
elif action == "rename":
|
||||
if selected_item:
|
||||
input_dialog.popup(self, "_on_rename_item_confirmed", tr("Rename Item"), tr("Please enter a new ID for this item."), "ID", selected_id)
|
||||
else:
|
||||
input_dialog.popup(self, "_on_rename_class_confirmed", tr("Rename Class"), tr("Please enter a new name for this class. All pending changes will be discarded!"), "ID", selected_class)
|
||||
elif action == "duplicate":
|
||||
var new_display_name = ""
|
||||
if selected_item._dirty:
|
||||
input_dialog.popup(self, "item_duplication_failed", tr("Item duplication failed"), tr("Before duplicating this item, please first save it."))
|
||||
return
|
||||
if selected_id != selected_item._display_name:
|
||||
new_display_name = selected_item._display_name
|
||||
input_dialog.popup(self, "_on_duplicate_confirmed", tr("Duplicate Item"), tr("Please enter a new ID for this item"), "ID", selected_id, tr("Display Name (optional)"), new_display_name)
|
||||
reload()
|
||||
elif action == "save":
|
||||
item_manager.save_item(selected_item)
|
||||
item_tree.load_tree()
|
||||
#reload()
|
||||
elif action == "save_all":
|
||||
item_manager.save_all_items()
|
||||
item_tree.load_tree()
|
||||
#reload()
|
||||
elif action == "reload":
|
||||
item_manager.load_manager()
|
||||
reload()
|
||||
elif action == "new_class":
|
||||
_on_AddClassButton_button_down() # TODO: Incorporate into dialog handling
|
||||
elif action == "delete":
|
||||
if selected_item:
|
||||
input_dialog.popup(self, "_on_delete_item_confirmed", tr("Delete Item"), tr("Are you sure you want to delete this item?"))
|
||||
else:
|
||||
input_dialog.popup(self, "_on_delete_class_confirmed", tr("Delete Class"), tr("Are you sure you want to delete class along with all items?"))
|
||||
elif action == "options":
|
||||
options_screen.popup_centered()
|
||||
elif action == "edit_class":
|
||||
edit_class()
|
||||
elif action == "copy_id":
|
||||
copy_id()
|
||||
elif action == "change_display_name":
|
||||
input_dialog.popup(self, "change_display_name", tr("Change Display Name"), tr("Please enter a display name for this item."), "Display Name", selected_item._display_name)
|
||||
elif action == "add_custom_property":
|
||||
new_custom_property_name.set_text("")
|
||||
new_custom_property_dialog.popup_centered()
|
||||
new_custom_property_name.grab_focus()
|
||||
|
||||
#########################################################################
|
||||
# Handlers #
|
||||
#########################################################################
|
||||
|
||||
func _on_add_item_confirmed(id, display_name):
|
||||
var new_item = item_manager.create_and_add_new_item(selected_class, id, display_name)
|
||||
if new_item:
|
||||
item_tree.add_leaf(new_item, true)
|
||||
|
||||
|
||||
func _on_rename_item_confirmed(id):
|
||||
item_manager.rename_item(selected_item, id)
|
||||
reload()
|
||||
|
||||
|
||||
func _on_rename_class_confirmed(name):
|
||||
item_manager.rename_class(selected_class, name)
|
||||
reload()
|
||||
|
||||
|
||||
func _on_duplicate_confirmed(id, display_name):
|
||||
var duplicated_item = item_manager.duplicate_item(selected_item, id, display_name)
|
||||
item_tree.add_leaf(duplicated_item, true)
|
||||
|
||||
|
||||
func _on_delete_item_confirmed():
|
||||
item_manager.delete_item(selected_item)
|
||||
reload()
|
||||
|
||||
|
||||
func _on_delete_class_confirmed():
|
||||
item_manager.delete_class(selected_class)
|
||||
if item_manager.classes.size() > 0:
|
||||
item_tree.select_class(item_manager.class_names[0])
|
||||
else:
|
||||
change_item_context(null, null)
|
||||
reload()
|
||||
|
||||
#########################################################################
|
||||
# Buttons on the right #
|
||||
#########################################################################
|
||||
func change_display_name(new_name):
|
||||
selected_item._display_name = new_name
|
||||
toggle_item_dirty_state(selected_item)
|
||||
change_item_context(selected_item, selected_class)
|
||||
|
||||
func copy_id():
|
||||
if selected_item:
|
||||
OS.set_clipboard(selected_id)
|
||||
else:
|
||||
OS.set_clipboard(selected_class)
|
||||
|
||||
func edit_class():
|
||||
var script = null
|
||||
if selected_item:
|
||||
script = selected_item.get_script()
|
||||
else:
|
||||
script = item_manager.classes[selected_class].new("dummy").get_script() # If no item is selected, create a dummy item
|
||||
emit_signal("class_edit_requested", script)
|
||||
|
||||
#####################################################
|
||||
# OTHERS
|
||||
#####################################################
|
||||
|
||||
func show_warning(title, text):
|
||||
warn_dialog.set_title(title)
|
||||
warn_dialog.set_text(text)
|
||||
warn_dialog.popup_centered()
|
||||
|
||||
|
||||
func log_text(text):
|
||||
var file = File.new()
|
||||
file.open("res://test.log", File.READ_WRITE)
|
||||
var old_text = file.get_as_text()
|
||||
var date = str(OS.get_datetime()["hour"]) + ":" + str(OS.get_datetime()["minute"]) + ":" + str(OS.get_datetime()["second"]) + "\t"
|
||||
file.store_line(old_text + date + text)
|
840
data_editor_gui.tscn
Normal file
@ -0,0 +1,840 @@
|
||||
[gd_scene load_steps=22 format=1]
|
||||
|
||||
[ext_resource path="res://addons/DataEditor/data_editor_gui.gd" type="Script" id=1]
|
||||
[ext_resource path="res://addons/DataEditor/icons/icon_add.png" type="Texture" id=2]
|
||||
[ext_resource path="res://addons/DataEditor/icons/icon_save.png" type="Texture" id=3]
|
||||
[ext_resource path="res://addons/DataEditor/icons/icon_rename.png" type="Texture" id=4]
|
||||
[ext_resource path="res://addons/DataEditor/icons/icon_duplicate.png" type="Texture" id=5]
|
||||
[ext_resource path="res://addons/DataEditor/icons/icon_remove.png" type="Texture" id=6]
|
||||
[ext_resource path="res://addons/DataEditor/icons/icon_reload_small.png" type="Texture" id=7]
|
||||
[ext_resource path="res://addons/DataEditor/icons/icon_script.png" type="Texture" id=8]
|
||||
[ext_resource path="res://addons/DataEditor/icons/icon_options.png" type="Texture" id=9]
|
||||
[ext_resource path="res://addons/DataEditor/item_tree.tscn" type="PackedScene" id=10]
|
||||
[ext_resource path="res://addons/DataEditor/icons/icon_display-name.png" type="Texture" id=11]
|
||||
[ext_resource path="res://addons/DataEditor/icons/icon_copy.png" type="Texture" id=12]
|
||||
[ext_resource path="res://addons/DataEditor/icons/icon_edit.png" type="Texture" id=13]
|
||||
[ext_resource path="res://addons/DataEditor/fonts/droid_sans_title.tres" type="DynamicFont" id=14]
|
||||
[ext_resource path="res://addons/DataEditor/class_properties.tscn" type="PackedScene" id=15]
|
||||
[ext_resource path="res://addons/DataEditor/custom_properties.tscn" type="PackedScene" id=16]
|
||||
[ext_resource path="res://addons/DataEditor/class_overview.tscn" type="PackedScene" id=17]
|
||||
[ext_resource path="res://addons/DataEditor/no_classes.tscn" type="PackedScene" id=18]
|
||||
[ext_resource path="res://addons/DataEditor/warn.gd" type="Script" id=19]
|
||||
[ext_resource path="res://addons/DataEditor/options.tscn" type="PackedScene" id=20]
|
||||
[ext_resource path="res://addons/DataEditor/input_dialog.tscn" type="PackedScene" id=21]
|
||||
|
||||
[node name="DataEditor" type="Control"]
|
||||
|
||||
anchor/right = 1
|
||||
anchor/bottom = 1
|
||||
rect/min_size = Vector2( 800, 0 )
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 3
|
||||
size_flags/vertical = 3
|
||||
margin/left = 0.0
|
||||
margin/top = 0.0
|
||||
margin/right = 0.0
|
||||
margin/bottom = 0.0
|
||||
script/script = ExtResource( 1 )
|
||||
|
||||
[node name="VBox" type="VBoxContainer" parent="."]
|
||||
|
||||
anchor/right = 1
|
||||
anchor/bottom = 1
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 3
|
||||
size_flags/vertical = 3
|
||||
margin/left = 0.0
|
||||
margin/top = 0.0
|
||||
margin/right = 0.0
|
||||
margin/bottom = 0.0
|
||||
alignment = 0
|
||||
|
||||
[node name="Head" type="HBoxContainer" parent="VBox"]
|
||||
|
||||
rect/min_size = Vector2( 0, 22 )
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = false
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 0
|
||||
margin/left = 0.0
|
||||
margin/top = 0.0
|
||||
margin/right = 1280.0
|
||||
margin/bottom = 28.0
|
||||
alignment = 0
|
||||
|
||||
[node name="Add" type="ToolButton" parent="VBox/Head"]
|
||||
|
||||
rect/min_size = Vector2( 28, 28 )
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 2
|
||||
margin/left = 0.0
|
||||
margin/top = 0.0
|
||||
margin/right = 56.0
|
||||
margin/bottom = 28.0
|
||||
disabled = true
|
||||
toggle_mode = false
|
||||
enabled_focus_mode = 2
|
||||
shortcut = null
|
||||
text = "Add"
|
||||
icon = ExtResource( 2 )
|
||||
flat = true
|
||||
align = 0
|
||||
|
||||
[node name="VSeparator5" type="VSeparator" parent="VBox/Head"]
|
||||
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 2
|
||||
margin/left = 60.0
|
||||
margin/top = 0.0
|
||||
margin/right = 63.0
|
||||
margin/bottom = 28.0
|
||||
|
||||
[node name="Save" type="ToolButton" parent="VBox/Head"]
|
||||
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 2
|
||||
margin/left = 67.0
|
||||
margin/top = 0.0
|
||||
margin/right = 127.0
|
||||
margin/bottom = 28.0
|
||||
disabled = true
|
||||
toggle_mode = false
|
||||
enabled_focus_mode = 2
|
||||
shortcut = null
|
||||
text = "Save"
|
||||
icon = ExtResource( 3 )
|
||||
flat = false
|
||||
|
||||
[node name="SaveAll" type="ToolButton" parent="VBox/Head"]
|
||||
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 2
|
||||
margin/left = 131.0
|
||||
margin/top = 0.0
|
||||
margin/right = 212.0
|
||||
margin/bottom = 28.0
|
||||
disabled = true
|
||||
toggle_mode = false
|
||||
enabled_focus_mode = 2
|
||||
shortcut = null
|
||||
text = "Save All"
|
||||
icon = ExtResource( 3 )
|
||||
flat = false
|
||||
|
||||
[node name="VSeparator" type="VSeparator" parent="VBox/Head"]
|
||||
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 2
|
||||
margin/left = 216.0
|
||||
margin/top = 0.0
|
||||
margin/right = 219.0
|
||||
margin/bottom = 28.0
|
||||
|
||||
[node name="Rename" type="ToolButton" parent="VBox/Head"]
|
||||
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 2
|
||||
margin/left = 223.0
|
||||
margin/top = 0.0
|
||||
margin/right = 306.0
|
||||
margin/bottom = 28.0
|
||||
disabled = true
|
||||
toggle_mode = false
|
||||
enabled_focus_mode = 2
|
||||
shortcut = null
|
||||
text = "Rename"
|
||||
icon = ExtResource( 4 )
|
||||
flat = false
|
||||
|
||||
[node name="Duplicate" type="ToolButton" parent="VBox/Head"]
|
||||
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 2
|
||||
margin/left = 310.0
|
||||
margin/top = 0.0
|
||||
margin/right = 402.0
|
||||
margin/bottom = 28.0
|
||||
disabled = true
|
||||
toggle_mode = false
|
||||
enabled_focus_mode = 2
|
||||
shortcut = null
|
||||
text = "Duplicate"
|
||||
icon = ExtResource( 5 )
|
||||
flat = false
|
||||
|
||||
[node name="Delete" type="ToolButton" parent="VBox/Head"]
|
||||
|
||||
rect/min_size = Vector2( 28, 28 )
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 2
|
||||
margin/left = 406.0
|
||||
margin/top = 0.0
|
||||
margin/right = 480.0
|
||||
margin/bottom = 28.0
|
||||
disabled = true
|
||||
toggle_mode = false
|
||||
enabled_focus_mode = 2
|
||||
shortcut = null
|
||||
text = "Delete"
|
||||
icon = ExtResource( 6 )
|
||||
flat = true
|
||||
|
||||
[node name="VSeparator3" type="VSeparator" parent="VBox/Head"]
|
||||
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 2
|
||||
margin/left = 484.0
|
||||
margin/top = 0.0
|
||||
margin/right = 487.0
|
||||
margin/bottom = 28.0
|
||||
|
||||
[node name="Refresh2" type="ToolButton" parent="VBox/Head"]
|
||||
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 2
|
||||
margin/left = 491.0
|
||||
margin/top = 0.0
|
||||
margin/right = 563.0
|
||||
margin/bottom = 28.0
|
||||
toggle_mode = false
|
||||
enabled_focus_mode = 2
|
||||
shortcut = null
|
||||
text = "Reload"
|
||||
icon = ExtResource( 7 )
|
||||
flat = false
|
||||
|
||||
[node name="VSeparator2" type="VSeparator" parent="VBox/Head"]
|
||||
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 2
|
||||
margin/left = 567.0
|
||||
margin/top = 0.0
|
||||
margin/right = 570.0
|
||||
margin/bottom = 28.0
|
||||
|
||||
[node name="NewClass" type="ToolButton" parent="VBox/Head"]
|
||||
|
||||
rect/min_size = Vector2( 28, 28 )
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 2
|
||||
margin/left = 574.0
|
||||
margin/top = 0.0
|
||||
margin/right = 670.0
|
||||
margin/bottom = 28.0
|
||||
toggle_mode = false
|
||||
enabled_focus_mode = 2
|
||||
shortcut = null
|
||||
text = "New Class"
|
||||
icon = ExtResource( 8 )
|
||||
flat = true
|
||||
align = 0
|
||||
|
||||
[node name="VSeparator4" type="VSeparator" parent="VBox/Head"]
|
||||
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 2
|
||||
margin/left = 674.0
|
||||
margin/top = 0.0
|
||||
margin/right = 677.0
|
||||
margin/bottom = 28.0
|
||||
|
||||
[node name="Options" type="ToolButton" parent="VBox/Head"]
|
||||
|
||||
rect/min_size = Vector2( 28, 28 )
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 2
|
||||
margin/left = 681.0
|
||||
margin/top = 0.0
|
||||
margin/right = 763.0
|
||||
margin/bottom = 28.0
|
||||
toggle_mode = false
|
||||
enabled_focus_mode = 2
|
||||
shortcut = null
|
||||
text = "Options"
|
||||
icon = ExtResource( 9 )
|
||||
flat = true
|
||||
align = 0
|
||||
|
||||
[node name="Search" type="LineEdit" parent="VBox/Head"]
|
||||
|
||||
visibility/visible = false
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 2
|
||||
margin/left = 377.0
|
||||
margin/top = 0.0
|
||||
margin/right = 435.0
|
||||
margin/bottom = 28.0
|
||||
placeholder/text = "Search"
|
||||
placeholder/alpha = 0.6
|
||||
focus_mode = 2
|
||||
caret/caret_blink = false
|
||||
caret/caret_blink_speed = 0.65
|
||||
|
||||
[node name="HSeparator" type="HSeparator" parent="VBox"]
|
||||
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 2
|
||||
margin/left = 0.0
|
||||
margin/top = 32.0
|
||||
margin/right = 1280.0
|
||||
margin/bottom = 35.0
|
||||
|
||||
[node name="Body" type="HSplitContainer" parent="VBox"]
|
||||
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = false
|
||||
size_flags/horizontal = 3
|
||||
size_flags/vertical = 3
|
||||
margin/left = 0.0
|
||||
margin/top = 39.0
|
||||
margin/right = 1280.0
|
||||
margin/bottom = 600.0
|
||||
split/offset = 0
|
||||
split/collapsed = false
|
||||
split/dragger_visibility = 0
|
||||
|
||||
[node name="ItemTree" parent="VBox/Body" instance=ExtResource( 10 )]
|
||||
|
||||
anchor/right = 0
|
||||
anchor/bottom = 0
|
||||
rect/min_size = Vector2( 165, 0 )
|
||||
size_flags/horizontal = 2
|
||||
size_flags/stretch_ratio = 0.0
|
||||
margin/right = 165.0
|
||||
margin/bottom = 561.0
|
||||
|
||||
[node name="Content" type="Panel" parent="VBox/Body"]
|
||||
|
||||
visibility/self_opacity = 0.0
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 3
|
||||
size_flags/vertical = 3
|
||||
margin/left = 177.0
|
||||
margin/top = 0.0
|
||||
margin/right = 1280.0
|
||||
margin/bottom = 561.0
|
||||
|
||||
[node name="VBox" type="VBoxContainer" parent="VBox/Body/Content"]
|
||||
|
||||
anchor/right = 1
|
||||
anchor/bottom = 1
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = false
|
||||
size_flags/horizontal = 3
|
||||
size_flags/vertical = 3
|
||||
size_flags/stretch_ratio = 0.0
|
||||
margin/left = 0.0
|
||||
margin/top = 0.0
|
||||
margin/right = 0.0
|
||||
margin/bottom = 0.0
|
||||
alignment = 0
|
||||
|
||||
[node name="Container" type="MarginContainer" parent="VBox/Body/Content/VBox"]
|
||||
|
||||
rect/min_size = Vector2( 0, 25 )
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 2
|
||||
margin/left = 0.0
|
||||
margin/top = 0.0
|
||||
margin/right = 1103.0
|
||||
margin/bottom = 25.0
|
||||
|
||||
[node name="HBox" type="HBoxContainer" parent="VBox/Body/Content/VBox/Container"]
|
||||
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = false
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 2
|
||||
margin/left = 8.0
|
||||
margin/top = 0.0
|
||||
margin/right = 1103.0
|
||||
margin/bottom = 25.0
|
||||
custom_constants/separation = 15
|
||||
alignment = 2
|
||||
|
||||
[node name="DisplayName" type="ToolButton" parent="VBox/Body/Content/VBox/Container/HBox"]
|
||||
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 0
|
||||
size_flags/vertical = 2
|
||||
margin/left = 722.0
|
||||
margin/top = 0.0
|
||||
margin/right = 892.0
|
||||
margin/bottom = 25.0
|
||||
disabled = true
|
||||
toggle_mode = false
|
||||
enabled_focus_mode = 2
|
||||
shortcut = null
|
||||
text = "Change Display Name"
|
||||
icon = ExtResource( 11 )
|
||||
flat = true
|
||||
align = 0
|
||||
|
||||
[node name="CopyId" type="ToolButton" parent="VBox/Body/Content/VBox/Container/HBox"]
|
||||
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 0
|
||||
size_flags/vertical = 2
|
||||
margin/left = 907.0
|
||||
margin/top = 0.0
|
||||
margin/right = 988.0
|
||||
margin/bottom = 25.0
|
||||
disabled = true
|
||||
toggle_mode = false
|
||||
enabled_focus_mode = 2
|
||||
shortcut = null
|
||||
text = "Copy ID"
|
||||
icon = ExtResource( 12 )
|
||||
flat = true
|
||||
align = 0
|
||||
|
||||
[node name="EditClass" type="ToolButton" parent="VBox/Body/Content/VBox/Container/HBox"]
|
||||
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 0
|
||||
size_flags/vertical = 2
|
||||
margin/left = 1003.0
|
||||
margin/top = 0.0
|
||||
margin/right = 1095.0
|
||||
margin/bottom = 25.0
|
||||
disabled = true
|
||||
toggle_mode = false
|
||||
enabled_focus_mode = 2
|
||||
shortcut = null
|
||||
text = "Edit Class"
|
||||
icon = ExtResource( 13 )
|
||||
flat = true
|
||||
align = 0
|
||||
|
||||
[node name="ItemIdLabel" type="Label" parent="VBox/Body/Content/VBox/Container"]
|
||||
|
||||
focus/ignore_mouse = true
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 0
|
||||
margin/left = 8.0
|
||||
margin/top = 0.0
|
||||
margin/right = 1103.0
|
||||
margin/bottom = 25.0
|
||||
custom_fonts/font = ExtResource( 14 )
|
||||
custom_colors/font_color_shadow = Color( 0.300781, 0.300781, 0.300781, 1 )
|
||||
custom_constants/shadow_offset_x = 1
|
||||
custom_constants/shadow_offset_y = 1
|
||||
text = "No Classes"
|
||||
percent_visible = 1.0
|
||||
lines_skipped = 0
|
||||
max_lines_visible = -1
|
||||
|
||||
[node name="GridContainer" type="GridContainer" parent="VBox/Body/Content/VBox/Container"]
|
||||
|
||||
visibility/visible = false
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = false
|
||||
size_flags/horizontal = 0
|
||||
size_flags/vertical = 2
|
||||
margin/left = 555.0
|
||||
margin/top = 0.0
|
||||
margin/right = 555.0
|
||||
margin/bottom = 25.0
|
||||
custom_constants/hseparation = 10
|
||||
columns = 2
|
||||
|
||||
[node name="Created" type="Label" parent="VBox/Body/Content/VBox/Container/GridContainer"]
|
||||
|
||||
visibility/opacity = 0.25
|
||||
focus/ignore_mouse = true
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 0
|
||||
margin/left = 0.0
|
||||
margin/top = 0.0
|
||||
margin/right = 91.0
|
||||
margin/bottom = 14.0
|
||||
text = "Created:"
|
||||
percent_visible = 1.0
|
||||
lines_skipped = 0
|
||||
max_lines_visible = -1
|
||||
|
||||
[node name="CreatedDate" type="Label" parent="VBox/Body/Content/VBox/Container/GridContainer"]
|
||||
|
||||
visibility/opacity = 0.25
|
||||
focus/ignore_mouse = true
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 0
|
||||
margin/left = 101.0
|
||||
margin/top = 0.0
|
||||
margin/right = 217.0
|
||||
margin/bottom = 14.0
|
||||
text = "01.01.01 01:01:01"
|
||||
percent_visible = 1.0
|
||||
lines_skipped = 0
|
||||
max_lines_visible = -1
|
||||
|
||||
[node name="LastModifiedLabel" type="Label" parent="VBox/Body/Content/VBox/Container/GridContainer"]
|
||||
|
||||
visibility/opacity = 0.25
|
||||
focus/ignore_mouse = true
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 0
|
||||
margin/left = 0.0
|
||||
margin/top = 18.0
|
||||
margin/right = 91.0
|
||||
margin/bottom = 32.0
|
||||
text = "Last modified:"
|
||||
percent_visible = 1.0
|
||||
lines_skipped = 0
|
||||
max_lines_visible = -1
|
||||
|
||||
[node name="LastModifiedDate" type="Label" parent="VBox/Body/Content/VBox/Container/GridContainer"]
|
||||
|
||||
visibility/opacity = 0.25
|
||||
focus/ignore_mouse = true
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 0
|
||||
margin/left = 101.0
|
||||
margin/top = 18.0
|
||||
margin/right = 217.0
|
||||
margin/bottom = 32.0
|
||||
text = "01.01.01 01:01:01"
|
||||
percent_visible = 1.0
|
||||
lines_skipped = 0
|
||||
max_lines_visible = -1
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="VBox/Body/Content/VBox/Container"]
|
||||
|
||||
visibility/visible = false
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = false
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 2
|
||||
margin/left = 8.0
|
||||
margin/top = 0.0
|
||||
margin/right = 1103.0
|
||||
margin/bottom = 25.0
|
||||
alignment = 0
|
||||
|
||||
[node name="HSeparator" type="HSeparator" parent="VBox/Body/Content/VBox"]
|
||||
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 3
|
||||
size_flags/vertical = 2
|
||||
margin/left = 0.0
|
||||
margin/top = 29.0
|
||||
margin/right = 1103.0
|
||||
margin/bottom = 32.0
|
||||
|
||||
[node name="Spacer" type="Control" parent="VBox/Body/Content/VBox"]
|
||||
|
||||
rect/min_size = Vector2( 0, 10 )
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 3
|
||||
size_flags/vertical = 2
|
||||
margin/left = 0.0
|
||||
margin/top = 36.0
|
||||
margin/right = 1103.0
|
||||
margin/bottom = 46.0
|
||||
|
||||
[node name="InstanceDetails" type="PanelContainer" parent="VBox/Body/Content/VBox"]
|
||||
|
||||
editor/display_folded = true
|
||||
visibility/visible = false
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 3
|
||||
size_flags/vertical = 3
|
||||
margin/left = 0.0
|
||||
margin/top = 50.0
|
||||
margin/right = 1103.0
|
||||
margin/bottom = 303.0
|
||||
|
||||
[node name="HBox" type="HBoxContainer" parent="VBox/Body/Content/VBox/InstanceDetails"]
|
||||
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = false
|
||||
size_flags/horizontal = 3
|
||||
size_flags/vertical = 3
|
||||
margin/left = 7.0
|
||||
margin/top = 7.0
|
||||
margin/right = 1096.0
|
||||
margin/bottom = 246.0
|
||||
alignment = 0
|
||||
|
||||
[node name="ClassProperties" parent="VBox/Body/Content/VBox/InstanceDetails/HBox" instance=ExtResource( 15 )]
|
||||
|
||||
anchor/right = 0
|
||||
anchor/bottom = 0
|
||||
margin/right = 542.0
|
||||
margin/bottom = 239.0
|
||||
|
||||
[node name="CustomProperties" parent="VBox/Body/Content/VBox/InstanceDetails/HBox" instance=ExtResource( 16 )]
|
||||
|
||||
anchor/right = 0
|
||||
anchor/bottom = 0
|
||||
size_flags/horizontal = 3
|
||||
size_flags/vertical = 3
|
||||
margin/left = 546.0
|
||||
margin/right = 1089.0
|
||||
margin/bottom = 239.0
|
||||
|
||||
[node name="ClassOverview" parent="VBox/Body/Content/VBox" instance=ExtResource( 17 )]
|
||||
|
||||
visibility/visible = false
|
||||
anchor/right = 0
|
||||
anchor/bottom = 0
|
||||
margin/top = 50.0
|
||||
margin/right = 1103.0
|
||||
margin/bottom = 561.0
|
||||
|
||||
[node name="NoClasses" parent="VBox/Body/Content/VBox" instance=ExtResource( 18 )]
|
||||
|
||||
anchor/right = 0
|
||||
anchor/bottom = 0
|
||||
margin/left = 0.0
|
||||
margin/top = 50.0
|
||||
margin/right = 1103.0
|
||||
margin/bottom = 561.0
|
||||
|
||||
[node name="WarnDialog" type="AcceptDialog" parent="."]
|
||||
|
||||
visibility/visible = false
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 2
|
||||
margin/left = 0.0
|
||||
margin/top = 0.0
|
||||
margin/right = 350.0
|
||||
margin/bottom = 110.0
|
||||
popup/exclusive = true
|
||||
window/title = ""
|
||||
dialog/hide_on_ok = true
|
||||
script/script = ExtResource( 19 )
|
||||
|
||||
[node name="NewClassDialog" type="ConfirmationDialog" parent="."]
|
||||
|
||||
visibility/visible = false
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 2
|
||||
margin/left = 519.0
|
||||
margin/top = 193.0
|
||||
margin/right = 990.0
|
||||
margin/bottom = 316.0
|
||||
popup/exclusive = true
|
||||
window/title = "New Class"
|
||||
dialog/text = "Enter the name and the path to an optional icon"
|
||||
dialog/hide_on_ok = true
|
||||
|
||||
[node name="ClassName" type="LineEdit" parent="NewClassDialog"]
|
||||
|
||||
focus_neighbour/bottom = NodePath("../ClassIconPath")
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 2
|
||||
margin/left = 8.0
|
||||
margin/top = 27.0
|
||||
margin/right = 466.0
|
||||
margin/bottom = 51.0
|
||||
placeholder/text = "Name"
|
||||
placeholder/alpha = 0.6
|
||||
focus_mode = 2
|
||||
caret/caret_blink = false
|
||||
caret/caret_blink_speed = 0.65
|
||||
|
||||
[node name="ClassIconPath" type="LineEdit" parent="NewClassDialog"]
|
||||
|
||||
focus_neighbour/top = NodePath("../ClassName")
|
||||
focus_neighbour/right = NodePath("../NewClassIconSearchButton")
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 2
|
||||
margin/left = 8.0
|
||||
margin/top = 60.0
|
||||
margin/right = 393.0
|
||||
margin/bottom = 84.0
|
||||
placeholder/text = "Icon Path (optional)"
|
||||
placeholder/alpha = 0.6
|
||||
focus_mode = 2
|
||||
caret/caret_blink = false
|
||||
caret/caret_blink_speed = 0.65
|
||||
|
||||
[node name="ClassIconFileDialog" type="FileDialog" parent="NewClassDialog"]
|
||||
|
||||
visibility/visible = false
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 2
|
||||
margin/left = 0.0
|
||||
margin/top = 0.0
|
||||
margin/right = 393.0
|
||||
margin/bottom = 404.0
|
||||
popup/exclusive = true
|
||||
window/title = "Open a File"
|
||||
dialog/hide_on_ok = true
|
||||
mode = 0
|
||||
access = 2
|
||||
filters = StringArray( "*.png" )
|
||||
show_hidden_files = false
|
||||
|
||||
[node name="NewClassIconSearchButton" type="Button" parent="NewClassDialog"]
|
||||
|
||||
focus_neighbour/left = NodePath("../ClassIconPath")
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 2
|
||||
margin/left = 399.0
|
||||
margin/top = 61.0
|
||||
margin/right = 466.0
|
||||
margin/bottom = 83.0
|
||||
toggle_mode = false
|
||||
enabled_focus_mode = 2
|
||||
shortcut = null
|
||||
text = "Search"
|
||||
flat = false
|
||||
|
||||
[node name="OptionsDialog" parent="." instance=ExtResource( 20 )]
|
||||
|
||||
visibility/visible = false
|
||||
margin/right = 550.0
|
||||
margin/bottom = 310.0
|
||||
|
||||
[node name="InputDialog" parent="." instance=ExtResource( 21 )]
|
||||
|
||||
visibility/visible = false
|
||||
|
||||
[node name="NewCustomPropertyDialog" type="ConfirmationDialog" parent="."]
|
||||
|
||||
visibility/visible = false
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 2
|
||||
margin/left = 117.0
|
||||
margin/top = 117.0
|
||||
margin/right = 489.0
|
||||
margin/bottom = 243.0
|
||||
popup/exclusive = false
|
||||
window/title = "New custom property"
|
||||
dialog/text = "Enter the name and type of the property"
|
||||
dialog/hide_on_ok = true
|
||||
|
||||
[node name="LineEdit" type="LineEdit" parent="NewCustomPropertyDialog"]
|
||||
|
||||
focus_neighbour/bottom = NodePath("../TypeOptions")
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 3
|
||||
size_flags/vertical = 2
|
||||
margin/left = 7.0
|
||||
margin/top = 29.0
|
||||
margin/right = 363.0
|
||||
margin/bottom = 53.0
|
||||
placeholder/text = "Name"
|
||||
placeholder/alpha = 0.6
|
||||
focus_mode = 2
|
||||
caret/caret_blink = false
|
||||
caret/caret_blink_speed = 0.65
|
||||
|
||||
[node name="TypeOptions" type="OptionButton" parent="NewCustomPropertyDialog"]
|
||||
|
||||
focus_neighbour/top = NodePath("../LineEdit")
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 2
|
||||
margin/left = 7.0
|
||||
margin/top = 64.0
|
||||
margin/right = 363.0
|
||||
margin/bottom = 84.0
|
||||
toggle_mode = false
|
||||
enabled_focus_mode = 2
|
||||
shortcut = null
|
||||
text = "BOOL"
|
||||
flat = false
|
||||
align = 0
|
||||
selected = 0
|
||||
items = [ "BOOL", null, false, -1, 1, "COLOR", null, false, -1, 14, "IMAGE", null, false, -1, 15, "INT", null, false, -1, 2, "NODE_PATH", null, false, -1, 16, "OBJECT", null, false, -1, 18, "PLANE", null, false, -1, 9, "QUAT", null, false, -1, 10, "REAL", null, false, -1, 3, "RECT2", null, false, -1, 6, "STRING", null, false, -1, 4, "TRANSFORM", null, false, -1, 13, "VECTOR2", null, false, -1, 5, "VECTOR3", null, false, -1, 7 ]
|
||||
|
||||
[connection signal="button_down" from="VBox/Head/Add" to="." method="handle_actions" binds= [ "add" ]]
|
||||
|
||||
[connection signal="button_down" from="VBox/Head/Save" to="." method="handle_actions" binds= [ "save" ]]
|
||||
|
||||
[connection signal="button_down" from="VBox/Head/SaveAll" to="." method="handle_actions" binds= [ "save_all" ]]
|
||||
|
||||
[connection signal="button_down" from="VBox/Head/Rename" to="." method="handle_actions" binds= [ "rename" ]]
|
||||
|
||||
[connection signal="button_down" from="VBox/Head/Duplicate" to="." method="handle_actions" binds= [ "duplicate" ]]
|
||||
|
||||
[connection signal="button_down" from="VBox/Head/Delete" to="." method="handle_actions" binds= [ "delete" ]]
|
||||
|
||||
[connection signal="button_down" from="VBox/Head/Refresh2" to="." method="handle_actions" binds= [ "reload" ]]
|
||||
|
||||
[connection signal="button_down" from="VBox/Head/NewClass" to="." method="handle_actions" binds= [ "new_class" ]]
|
||||
|
||||
[connection signal="button_down" from="VBox/Head/Options" to="." method="handle_actions" binds= [ "options" ]]
|
||||
|
||||
[connection signal="button_down" from="VBox/Body/Content/VBox/Container/HBox/DisplayName" to="." method="handle_actions" binds= [ "change_display_name" ]]
|
||||
|
||||
[connection signal="button_down" from="VBox/Body/Content/VBox/Container/HBox/CopyId" to="." method="handle_actions" binds= [ "copy_id" ]]
|
||||
|
||||
[connection signal="button_down" from="VBox/Body/Content/VBox/Container/HBox/EditClass" to="." method="handle_actions" binds= [ "edit_class" ]]
|
||||
|
||||
[connection signal="confirmed" from="NewClassDialog" to="." method="_on_NewClassDialog_confirmed"]
|
||||
|
||||
[connection signal="file_selected" from="NewClassDialog/ClassIconFileDialog" to="." method="_on_NewClassIconFileDialog_file_selected"]
|
||||
|
||||
[connection signal="button_down" from="NewClassDialog/NewClassIconSearchButton" to="." method="_on_NewClassIconSearchButton_button_down"]
|
||||
|
||||
[connection signal="confirmed" from="NewCustomPropertyDialog" to="." method="_on_NewCustomPropertyDialog_confirmed"]
|
||||
|
||||
|
58
data_item.gd
Normal file
@ -0,0 +1,58 @@
|
||||
extends Node
|
||||
|
||||
var _items_path = ""
|
||||
|
||||
# Class information
|
||||
# No setter, that's why there is a comma
|
||||
var _class setget ,get_class
|
||||
var _class_name setget ,get_class_name
|
||||
|
||||
var _dirty = false # TODO: Exclude
|
||||
var _persistent = false # TODO: Exclude
|
||||
|
||||
|
||||
var _id = ""
|
||||
var _display_name setget set_display_name,get_display_name
|
||||
var _created = 0
|
||||
var _last_modified = 0
|
||||
|
||||
|
||||
# Instance-level custom properties, consists of arrays containing name, type, (hint and hint_text), default value
|
||||
var _custom_properties = {}
|
||||
|
||||
func _ready():
|
||||
var config = ConfigFile.new()
|
||||
config.load("res://addons/DataEditor/plugin.cfg")
|
||||
_items_path = config.has_section_key("plugin", "output_directory")
|
||||
|
||||
func get_class():
|
||||
return self.get_script().get_path().get_file().basename()
|
||||
|
||||
func get_class_name():
|
||||
return self.get_class().capitalize()
|
||||
|
||||
|
||||
func get_display_name():
|
||||
if _display_name == null or _display_name == "":
|
||||
return self._id
|
||||
else:
|
||||
return _display_name
|
||||
|
||||
func set_display_name(name):
|
||||
_display_name = name
|
||||
|
||||
|
||||
func sanitize_value(property, type, value):
|
||||
if property["type"] == TYPE_COLOR:
|
||||
value = value.to_html()
|
||||
elif property["type"] == TYPE_STRING:
|
||||
value = value.json_escape()
|
||||
return value
|
||||
|
||||
|
||||
|
||||
func _init(id):
|
||||
self._id = id
|
||||
|
||||
func _notification(what):
|
||||
print(what)
|
BIN
fonts/DroidSans.ttf
Normal file
BIN
fonts/DroidSerif-Bold.ttf
Normal file
BIN
fonts/DroidSerif-BoldItalic.ttf
Normal file
BIN
fonts/DroidSerif-Italic.ttf
Normal file
11
fonts/droid_sans_title.tres
Normal file
@ -0,0 +1,11 @@
|
||||
[gd_resource type="DynamicFont" load_steps=2 format=1]
|
||||
|
||||
[ext_resource path="res://addons/DataEditor/fonts/DroidSans.ttf" type="DynamicFontData" id=1]
|
||||
|
||||
[resource]
|
||||
|
||||
font/size = 22
|
||||
font/use_mipmaps = false
|
||||
font/use_filter = false
|
||||
font/font = ExtResource( 1 )
|
||||
|
11
fonts/droid_serif_bold.tres
Normal file
@ -0,0 +1,11 @@
|
||||
[gd_resource type="DynamicFont" load_steps=2 format=1]
|
||||
|
||||
[ext_resource path="res://addons/DataEditor/fonts/DroidSerif-Bold.ttf" type="DynamicFontData" id=1]
|
||||
|
||||
[resource]
|
||||
|
||||
font/size = 16
|
||||
font/use_mipmaps = false
|
||||
font/use_filter = false
|
||||
font/font = ExtResource( 1 )
|
||||
|
11
fonts/droid_serif_bolditalic.tres
Normal file
@ -0,0 +1,11 @@
|
||||
[gd_resource type="DynamicFont" load_steps=2 format=1]
|
||||
|
||||
[ext_resource path="res://addons/DataEditor/fonts/DroidSerif-Italic.ttf" type="DynamicFontData" id=1]
|
||||
|
||||
[resource]
|
||||
|
||||
font/size = 16
|
||||
font/use_mipmaps = false
|
||||
font/use_filter = false
|
||||
font/font = ExtResource( 1 )
|
||||
|
11
fonts/droid_serif_italic.tres
Normal file
@ -0,0 +1,11 @@
|
||||
[gd_resource type="DynamicFont" load_steps=2 format=1]
|
||||
|
||||
[ext_resource path="res://addons/DataEditor/fonts/DroidSerif-Italic.ttf" type="DynamicFontData" id=1]
|
||||
|
||||
[resource]
|
||||
|
||||
font/size = 16
|
||||
font/use_mipmaps = false
|
||||
font/use_filter = false
|
||||
font/font = ExtResource( 1 )
|
||||
|
BIN
icons/empty.png
Normal file
After Width: | Height: | Size: 939 B |
BIN
icons/gom.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
icons/icon_add.png
Normal file
After Width: | Height: | Size: 111 B |
BIN
icons/icon_copy.png
Normal file
After Width: | Height: | Size: 248 B |
BIN
icons/icon_display-name.png
Normal file
After Width: | Height: | Size: 200 B |
BIN
icons/icon_duplicate.png
Normal file
After Width: | Height: | Size: 167 B |
BIN
icons/icon_edit.png
Normal file
After Width: | Height: | Size: 346 B |
BIN
icons/icon_empty.png
Normal file
After Width: | Height: | Size: 267 B |
BIN
icons/icon_load.png
Normal file
After Width: | Height: | Size: 170 B |
BIN
icons/icon_move_down.png
Normal file
After Width: | Height: | Size: 268 B |
BIN
icons/icon_move_up.png
Normal file
After Width: | Height: | Size: 269 B |
BIN
icons/icon_multi_line.png
Normal file
After Width: | Height: | Size: 140 B |
BIN
icons/icon_options.png
Normal file
After Width: | Height: | Size: 392 B |
BIN
icons/icon_reload_small.png
Normal file
After Width: | Height: | Size: 409 B |
BIN
icons/icon_remove.png
Normal file
After Width: | Height: | Size: 166 B |
BIN
icons/icon_rename.png
Normal file
After Width: | Height: | Size: 160 B |
BIN
icons/icon_save.png
Normal file
After Width: | Height: | Size: 252 B |
BIN
icons/icon_script.png
Normal file
After Width: | Height: | Size: 262 B |
57
input_dialog.gd
Normal file
@ -0,0 +1,57 @@
|
||||
tool
|
||||
extends ConfirmationDialog
|
||||
|
||||
#var text = ""
|
||||
#var title = ""
|
||||
var placeholder_1 = ""
|
||||
var placeholder_2 = ""
|
||||
var caller = null
|
||||
var callback_method = ""
|
||||
|
||||
onready var line_edit_1 = get_node("VBox/LineEdit1")
|
||||
onready var line_edit_2 = get_node("VBox/LineEdit2")
|
||||
|
||||
|
||||
#func _init(text, placerholder_1 = "", placeholder_2 = ""):
|
||||
|
||||
func popup(caller, callback_method, title, text, placeholder_1 = "", default_text_1 = "", placeholder_2 = "", default_text_2 = ""):
|
||||
self.caller = caller
|
||||
self.callback_method = callback_method
|
||||
self.placeholder_1 = placeholder_1
|
||||
self.placeholder_2 = placeholder_2
|
||||
|
||||
caller.connect("input_dialog_confirmed", caller, callback_method, [])
|
||||
set_text(text)
|
||||
set_title(title)
|
||||
if placeholder_1 == "":
|
||||
line_edit_1.hide()
|
||||
else:
|
||||
line_edit_1.show()
|
||||
line_edit_1.set_placeholder(placeholder_1)
|
||||
|
||||
|
||||
if placeholder_2 == "":
|
||||
line_edit_2.hide()
|
||||
else:
|
||||
line_edit_2.show()
|
||||
line_edit_2.set_placeholder(placeholder_2)
|
||||
|
||||
line_edit_1.set_text(default_text_1)
|
||||
line_edit_2.set_text(default_text_2)
|
||||
self.popup_centered()
|
||||
|
||||
if not line_edit_1.is_hidden():
|
||||
line_edit_1.grab_focus()
|
||||
|
||||
func _on_ConfirmationDialog_confirmed():
|
||||
var text1 = line_edit_1.get_text().strip_edges()
|
||||
var text2 = line_edit_2.get_text().strip_edges()
|
||||
if placeholder_2:
|
||||
caller.emit_signal("input_dialog_confirmed", text1, text2)
|
||||
elif placeholder_1:
|
||||
caller.emit_signal("input_dialog_confirmed", text1)
|
||||
else:
|
||||
caller.emit_signal("input_dialog_confirmed")
|
||||
|
||||
|
||||
caller.disconnect("input_dialog_confirmed", caller, callback_method)
|
71
input_dialog.tscn
Normal file
@ -0,0 +1,71 @@
|
||||
[gd_scene load_steps=2 format=1]
|
||||
|
||||
[ext_resource path="res://addons/DataEditor/input_dialog.gd" type="Script" id=1]
|
||||
|
||||
[node name="ConfirmationDialog" type="ConfirmationDialog"]
|
||||
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 2
|
||||
margin/left = 0.0
|
||||
margin/top = 0.0
|
||||
margin/right = 440.0
|
||||
margin/bottom = 150.0
|
||||
popup/exclusive = true
|
||||
window/title = "Please Confirm..."
|
||||
dialog/text = "Text"
|
||||
dialog/hide_on_ok = true
|
||||
script/script = ExtResource( 1 )
|
||||
|
||||
[node name="VBox" type="VBoxContainer" parent="."]
|
||||
|
||||
anchor/top = 1
|
||||
anchor/right = 1
|
||||
anchor/bottom = 1
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = false
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 2
|
||||
margin/left = 8.0
|
||||
margin/top = 103.0
|
||||
margin/right = 8.0
|
||||
margin/bottom = 45.0
|
||||
custom_constants/separation = 10
|
||||
alignment = 0
|
||||
|
||||
[node name="LineEdit1" type="LineEdit" parent="VBox"]
|
||||
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 3
|
||||
size_flags/vertical = 2
|
||||
margin/left = 0.0
|
||||
margin/top = 0.0
|
||||
margin/right = 424.0
|
||||
margin/bottom = 24.0
|
||||
placeholder/text = "Hint 1"
|
||||
placeholder/alpha = 0.6
|
||||
focus_mode = 2
|
||||
caret/caret_blink = false
|
||||
caret/caret_blink_speed = 0.65
|
||||
|
||||
[node name="LineEdit2" type="LineEdit" parent="VBox"]
|
||||
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 3
|
||||
size_flags/vertical = 2
|
||||
margin/left = 0.0
|
||||
margin/top = 34.0
|
||||
margin/right = 424.0
|
||||
margin/bottom = 58.0
|
||||
placeholder/text = "Hint 2"
|
||||
placeholder/alpha = 0.6
|
||||
focus_mode = 2
|
||||
caret/caret_blink = false
|
||||
caret/caret_blink_speed = 0.65
|
||||
|
||||
[connection signal="confirmed" from="." to="." method="_on_ConfirmationDialog_confirmed"]
|
||||
|
||||
|
580
item_manager.gd
Normal file
@ -0,0 +1,580 @@
|
||||
extends Node
|
||||
|
||||
# Holds a dictionary of dictionaries with all items (class and then items)
|
||||
var items = {}
|
||||
|
||||
var class_names = []
|
||||
var classes = {}
|
||||
|
||||
var config_class_directory = ""
|
||||
var config_output_directory = ""
|
||||
var config_sanitize_ids = ""
|
||||
var config_encrypt = ""
|
||||
var config_password = ""
|
||||
var config_extension = ""
|
||||
var config_serializer = ""
|
||||
|
||||
signal item_duplication_failed(title, reason)
|
||||
signal item_insertion_failed(title, reason)
|
||||
signal class_insertion_failed(title, reason)
|
||||
signal custom_property_insertion_failed(title, reason)
|
||||
|
||||
var property_blacklist = ["_dirty"]
|
||||
|
||||
var default_type_values = {
|
||||
str(TYPE_STRING): "", # OK for simple
|
||||
str(TYPE_BOOL): false, # OK
|
||||
str(TYPE_COLOR): Color(0,0,0),
|
||||
str(TYPE_OBJECT): "res://",
|
||||
str(TYPE_IMAGE): "res://",
|
||||
str(TYPE_INT): 0,
|
||||
str(TYPE_NODE_PATH): @"",
|
||||
str(TYPE_REAL): 0.0,
|
||||
str(TYPE_RECT2): Rect2(0,0,32,32),
|
||||
str(TYPE_VECTOR2): Vector2(0,0),
|
||||
str(TYPE_VECTOR3): Vector3(0,0,0),
|
||||
str(TYPE_PLANE): Plane(0,0,0,0),
|
||||
str(TYPE_QUAT): Quat(0,0,0,0),
|
||||
str(TYPE_TRANSFORM): Transform(Vector3(0,0,0),Vector3(0,0,0),Vector3(0,0,0),Vector3(0,0,0))
|
||||
}
|
||||
|
||||
var type_names = {"STRING":TYPE_STRING, "BOOL":TYPE_BOOL, "COLOR":TYPE_COLOR, "OBJECT":TYPE_OBJECT, "IMAGE":TYPE_IMAGE, "INT":TYPE_INT, "NODE_PATH":TYPE_NODE_PATH, "REAL":TYPE_REAL, "RECT2":TYPE_RECT2, "VECTOR2":TYPE_VECTOR2, "VECTOR3":TYPE_VECTOR3, "PLANE":TYPE_PLANE, "QUAT":TYPE_QUAT, "TRANSFORM":TYPE_TRANSFORM }
|
||||
|
||||
|
||||
func _init():
|
||||
load_manager()
|
||||
|
||||
func load_manager():
|
||||
initialize_variables()
|
||||
load_config()
|
||||
load_class_names()
|
||||
load_classes()
|
||||
set_up_item_folders()
|
||||
load_items()
|
||||
Globals.set("item_manager", self)
|
||||
|
||||
func initialize_variables():
|
||||
items = {}
|
||||
class_names = []
|
||||
classes = {}
|
||||
|
||||
config_class_directory = ""
|
||||
config_output_directory = ""
|
||||
config_sanitize_ids = ""
|
||||
config_encrypt = ""
|
||||
config_password = ""
|
||||
config_extension = ""
|
||||
config_serializer = ""
|
||||
|
||||
func load_config():
|
||||
var config = ConfigFile.new()
|
||||
config.load("res://addons/DataEditor/plugin.cfg")
|
||||
self.config_class_directory = config.get_value("custom", "class_directory")
|
||||
self.config_output_directory = config.get_value("custom", "output_directory")
|
||||
self.config_sanitize_ids = config.get_value("custom", "sanitize_ids")
|
||||
self.config_encrypt = config.get_value("custom", "encrypt")
|
||||
self.config_password = config.get_value("custom", "password")
|
||||
self.config_serializer = config.get_value("custom", "serializer")
|
||||
self.config_extension = config.get_value("custom", "extension")
|
||||
|
||||
|
||||
func load_class_names():
|
||||
class_names.clear()
|
||||
var directory = Directory.new()
|
||||
if directory.open(config_class_directory) == OK:
|
||||
directory.list_dir_begin()
|
||||
var file_name = directory.get_next()
|
||||
while (file_name != ""):
|
||||
if file_name.extension() == "gd" and not directory.current_is_dir() and file_name != "data_item.gd" :
|
||||
class_names.append(file_name.replace(".gd", ""))
|
||||
file_name = directory.get_next()
|
||||
class_names.sort()
|
||||
|
||||
|
||||
func load_classes():
|
||||
classes = {}
|
||||
for item_class in class_names:
|
||||
classes[item_class] = load(config_class_directory + "/" + item_class + ".gd")
|
||||
#classes[item_class].reload(true)
|
||||
pass
|
||||
|
||||
|
||||
# Creates the directories for the items if they do not yet exist
|
||||
func set_up_item_folders():
|
||||
var directory = Directory.new()
|
||||
for item_class in classes:
|
||||
var path = config_output_directory + "/" + item_class
|
||||
if not directory.dir_exists(path):
|
||||
directory.make_dir_recursive(path)
|
||||
|
||||
func get_item_path(item):
|
||||
return config_output_directory + "/" + item._class + "/" + item._id + "." + config_extension
|
||||
|
||||
func get_full_path(item):
|
||||
return config_output_directory.replace("res://", "") + "/" + item._class + "/" + item._id + "." + config_extension
|
||||
|
||||
func load_items():
|
||||
items.clear()
|
||||
var directory = Directory.new()
|
||||
for item_class in class_names:
|
||||
items[item_class] = {}
|
||||
directory.open(config_output_directory + "/" + item_class )
|
||||
directory.list_dir_begin()
|
||||
var file_name = directory.get_next()
|
||||
while (file_name != ""):
|
||||
if file_name.extension() == config_extension and not directory.current_is_dir() :
|
||||
var id = file_name.basename()
|
||||
if config_serializer == "json":
|
||||
items[item_class][id] = load_json_item(item_class, file_name)
|
||||
elif config_serializer == "binary":
|
||||
items[item_class][id] = load_binary_item(item_class, file_name)
|
||||
else:
|
||||
pass
|
||||
file_name = directory.get_next()
|
||||
pass
|
||||
pass
|
||||
|
||||
#
|
||||
func load_binary_item(item_class, file_name):
|
||||
var file = File.new()
|
||||
var id = file_name.basename()
|
||||
var status = 0
|
||||
if not config_encrypt:
|
||||
file.open(config_output_directory + "/" + item_class + "/" + file_name, File.READ)
|
||||
else:
|
||||
file.open_encrypted_with_pass(config_output_directory + "/" + item_class + "/" + file_name, File.READ, config_password)
|
||||
|
||||
var item = classes[item_class].new(id)
|
||||
if status == OK:
|
||||
# Load all the variables
|
||||
while file.get_pos() < file.get_len():
|
||||
var property_name = str(file.get_var())
|
||||
|
||||
var value = file.get_var()
|
||||
item.set(property_name, value)
|
||||
pass
|
||||
# And now iterate over the rest of the variables and check if they have not yet been initialized
|
||||
|
||||
item._dirty = false
|
||||
item._persistent = true
|
||||
else:
|
||||
pass # TODO: Handle
|
||||
file.close()
|
||||
return item
|
||||
|
||||
|
||||
func load_json_item(item_class, file_name):
|
||||
var file = File.new()
|
||||
var id = file_name.basename()
|
||||
var status = file.open(config_output_directory + "/" + item_class + "/" + file_name, File.READ)
|
||||
var item = classes[item_class].new(id)
|
||||
if status == OK:
|
||||
var text = file.get_as_text()
|
||||
var dict = {}
|
||||
dict.parse_json(text)
|
||||
|
||||
for property_name in dict:
|
||||
|
||||
|
||||
if property_name == "_custom_properties":
|
||||
var value = dict["_custom_properties"]
|
||||
item._custom_properties = {}
|
||||
for custom_property in value:
|
||||
item._custom_properties[custom_property] = []
|
||||
var cp_value = value[custom_property][0]
|
||||
var cp_type = value[custom_property][1]
|
||||
cp_value = parse_value(cp_type, cp_value)
|
||||
item._custom_properties[custom_property].append(cp_type)
|
||||
item._custom_properties[custom_property].append(cp_value)
|
||||
else:
|
||||
var value = dict[property_name][0]
|
||||
var type = dict[property_name][1]
|
||||
value = parse_value(type, value)
|
||||
item.set(property_name, value)
|
||||
pass
|
||||
item._dirty = false
|
||||
item._persistent = true
|
||||
else:
|
||||
pass # TODO: Handle
|
||||
file.close()
|
||||
return item
|
||||
|
||||
# Handles some special cases of JSON deserialization, e.g. Color
|
||||
func parse_value(type, value):
|
||||
if type == TYPE_COLOR:
|
||||
value = Color(value)
|
||||
elif type == TYPE_PLANE:
|
||||
var split = value.replace("(", "").replace(")", "").split(",")
|
||||
value = Plane(split[0], split[1], split[2], split[3])
|
||||
elif type == TYPE_QUAT:
|
||||
var split = value.replace("(", "").replace(")", "").split(",")
|
||||
value = Quat(split[0], split[1], split[2], split[3])
|
||||
elif type == TYPE_RECT2:
|
||||
var split = value.replace("(", "").replace(")", "").split(",")
|
||||
value = Rect2(split[0], split[1], split[2], split[3])
|
||||
elif type == TYPE_TRANSFORM:
|
||||
var split = value.replace("(", "").replace(")", "").split(",")
|
||||
value = Transform(Vector3(split[0], split[1], split[2]), Vector3(split[3], split[4], split[5]), Vector3(split[6], split[7], split[8]), Vector3(split[9], split[10], split[11]))
|
||||
return value
|
||||
|
||||
|
||||
|
||||
|
||||
func save_item(item):
|
||||
if item:
|
||||
item._last_modified= OS.get_unix_time()
|
||||
if config_serializer == "json":
|
||||
save_json_item(item)
|
||||
elif config_serializer == "binary":
|
||||
save_binary_item(item)
|
||||
else:
|
||||
pass
|
||||
|
||||
|
||||
func save_binary_item(item):
|
||||
var file = File.new()
|
||||
var status = 0
|
||||
if not config_encrypt:
|
||||
status = file.open(get_item_path(item), File.WRITE)
|
||||
else:
|
||||
status = file.open_encrypted_with_pass(get_item_path(item), File.WRITE, config_password)
|
||||
if status == OK:
|
||||
for property in item.get_property_list():
|
||||
# Serialize each property, even those starting with an underscore because they might be informative to external editors
|
||||
var property_name = property["name"]
|
||||
var property_usage = property["usage"]
|
||||
if property_usage >= PROPERTY_USAGE_SCRIPT_VARIABLE:
|
||||
file.store_var(property_name)
|
||||
file.store_var(item.get(property_name))
|
||||
pass
|
||||
item._persistent = true
|
||||
item._dirty = false
|
||||
else:
|
||||
pass #TODO: Handle
|
||||
file.close()
|
||||
|
||||
func save_json_item(item):
|
||||
var file = File.new()
|
||||
var status = 0
|
||||
status = file.open(get_item_path(item), File.WRITE)
|
||||
var dict = {}
|
||||
if status == OK:
|
||||
for property in item.get_property_list():
|
||||
# Serialize each property
|
||||
var property_name = property["name"].json_escape()
|
||||
var property_usage = property["usage"]
|
||||
if property_usage >= PROPERTY_USAGE_SCRIPT_VARIABLE and not property_name in property_blacklist:
|
||||
var type = typeof(item.get(property_name))
|
||||
var value = item.get(property_name)
|
||||
|
||||
# Custom properties are handled separately since they are stored as arrays
|
||||
if property_name == "_custom_properties":
|
||||
dict["_custom_properties"] = {}
|
||||
for custom_property in value:
|
||||
var type = value[custom_property][0]
|
||||
var sanitized_value = sanitize_variant(value[custom_property][1], type)
|
||||
dict["_custom_properties"][custom_property] = [sanitized_value, type]
|
||||
pass
|
||||
# Normal properties are simply stored as type-value pairs in an array
|
||||
else:
|
||||
value = sanitize_variant(value, type)
|
||||
dict[property_name] = [value, type]
|
||||
pass
|
||||
item._persistent = true
|
||||
item._dirty = false
|
||||
else:
|
||||
#TODO: Handle
|
||||
pass
|
||||
file.store_string(dict.to_json())
|
||||
file.close()
|
||||
|
||||
func sanitize_variant(value, type):
|
||||
if type == TYPE_COLOR:
|
||||
value = value.to_html()
|
||||
elif type == TYPE_STRING:
|
||||
value = value.json_escape()
|
||||
return value
|
||||
|
||||
|
||||
|
||||
func save_all_items():
|
||||
for item_class in items:
|
||||
for id in items[item_class]:
|
||||
save_item(items[item_class][id])
|
||||
pass
|
||||
pass
|
||||
|
||||
|
||||
func delete_item(item):
|
||||
var path = get_item_path(item)
|
||||
var directory = Directory.new()
|
||||
# TODO: Check why items[item._class].erase(item) doesn't work
|
||||
var items_of_class = items[item._class]
|
||||
#items_of_class.erase(item.id)
|
||||
#items[item._class] = items_of_class
|
||||
var status = directory.remove(path)
|
||||
load_manager()
|
||||
|
||||
|
||||
|
||||
func get_item(item_class, id):
|
||||
if items.has(item_class) and items[item_class].has(id):
|
||||
return items[item_class][id]
|
||||
else:
|
||||
return null
|
||||
|
||||
func get_items(item_class):
|
||||
if items.has(item_class):
|
||||
return items[item_class]
|
||||
else:
|
||||
return null
|
||||
|
||||
|
||||
func create_and_add_new_item(item_class, id, display_name):
|
||||
id = sanitize_string(id)
|
||||
id = rename_id_if_exists(item_class, id)
|
||||
if id == "" or id == null:
|
||||
emit_signal("item_insertion_failed", "Item insertion failed", "The item must haven an ID.")
|
||||
return null
|
||||
if items[item_class].has(id):
|
||||
emit_signal("item_insertion_failed", "Item insertion failed", "The item could not be created.")
|
||||
return null
|
||||
var new_item = classes[item_class].new(id)
|
||||
if display_name:
|
||||
new_item._display_name = display_name
|
||||
items[item_class][id] = new_item
|
||||
new_item._created = OS.get_unix_time()
|
||||
return new_item
|
||||
|
||||
func duplicate_item(item, id, display_name):
|
||||
id = sanitize_string(id)
|
||||
id = rename_id_if_exists(item._class, id)
|
||||
if id == "" or id == null:
|
||||
emit_signal("item_duplication_failed", "Item duplication failed", "The item must haven an ID.")
|
||||
return null
|
||||
if items[item._class].has(id):
|
||||
emit_signal("item_duplication_failed", "Item duplication failed", "The item could not be duplicated.")
|
||||
return null
|
||||
|
||||
var new_item = classes[item._class].new(id)
|
||||
# Copy all properties
|
||||
|
||||
for property in new_item.get_property_list():
|
||||
if property["usage"] >= PROPERTY_USAGE_SCRIPT_VARIABLE:
|
||||
new_item.set(property["name"], item.get(property["name"]))
|
||||
new_item._id = id
|
||||
if display_name:
|
||||
new_item._display_name = display_name
|
||||
else:
|
||||
new_item._display_name = new_item._id
|
||||
|
||||
new_item._dirty = true
|
||||
new_item._persistent = false
|
||||
items[new_item._class][new_item._id] = new_item
|
||||
return new_item
|
||||
|
||||
# Rename the item, delete the old entrym overwrite the id and save anew
|
||||
# TODO: Consider rename, could it still be referenced/locked somewhere?
|
||||
func rename_item(item, new_id):
|
||||
new_id = sanitize_string(new_id)
|
||||
if is_id_available(new_id):
|
||||
var directory = Directory.new()
|
||||
directory.remove(get_item_path(item))
|
||||
if item._id == item._display_name:
|
||||
item._display_name = new_id
|
||||
save_item(item)
|
||||
load_manager()
|
||||
else:
|
||||
pass # TODO: Issue warning
|
||||
|
||||
|
||||
func is_id_available(id):
|
||||
return true
|
||||
|
||||
# Adds a custom property to an item.
|
||||
# Returns true if it succeeded, false if it failed
|
||||
func add_custom_property(item, name, type):
|
||||
name = sanitize_string(name.strip_edges())
|
||||
|
||||
if item.get(name):
|
||||
emit_signal("custom_property_insertion_failed", "Custom Property Insertion Failed", "There already is a property with that name.")
|
||||
return false
|
||||
if item._custom_properties.has(name):
|
||||
emit_signal("custom_property_insertion_failed", "Custom Property Insertion Failed", "There already is a custom property with that name.")
|
||||
return false
|
||||
elif name == '':
|
||||
emit_signal("custom_property_insertion_failed", "Custom Property Insertion Failed", "The custom property name cannot be empty.")
|
||||
return false
|
||||
else:
|
||||
item._custom_properties[str(name)] = [type, default_type_values[str(type)]]
|
||||
item._dirty = true
|
||||
return true
|
||||
|
||||
func delete_custom_property(item, property_name):
|
||||
item._custom_properties.erase(property_name)
|
||||
|
||||
func delete_class(item_class):
|
||||
# Delete items
|
||||
var directory = Directory.new()
|
||||
var path = config_output_directory + "/" + item_class
|
||||
var status = directory.open(path)
|
||||
if status == OK:
|
||||
directory.list_dir_begin()
|
||||
var file_name = directory.get_next()
|
||||
while (file_name != ""):
|
||||
if not directory.current_is_dir():
|
||||
directory.remove(path + "/" + file_name)
|
||||
file_name = directory.get_next()
|
||||
pass
|
||||
directory.remove(path)
|
||||
classes.erase(item_class)
|
||||
class_names.erase(item_class)
|
||||
items.erase(item_class)
|
||||
|
||||
directory.remove(config_class_directory + "/" + item_class + ".gd")
|
||||
directory.remove(config_class_directory + "/" + item_class + ".png")
|
||||
|
||||
func create_class(name, icon_path):
|
||||
# Check if the classes folder already exists. If not, create it-
|
||||
var directory = Directory.new()
|
||||
if not directory.dir_exists(config_class_directory):
|
||||
directory.make_dir(config_class_directory)
|
||||
|
||||
name = sanitize_string(name)
|
||||
if name == "":
|
||||
emit_signal("class_insertion_failed", tr("Invalid name"), tr("The class name cannot be empty."))
|
||||
return
|
||||
elif class_names.has(name):
|
||||
emit_signal("class_insertion_failed", tr("Invalid name"), tr("The class name already exists."))
|
||||
return
|
||||
|
||||
# Handle icons
|
||||
var icon_file = File.new()
|
||||
if icon_path == "" or not icon_file.file_exists(icon_path):
|
||||
icon_path = "res://addons/DataEditor/icons/icon_empty.png"
|
||||
|
||||
var icon_resource = load(icon_path)
|
||||
var icon_data = icon_resource.get_data()
|
||||
if icon_data.get_width() <= 22 and icon_data.get_height() <= 22:
|
||||
var directory = Directory.new()
|
||||
var error = directory.copy(icon_path, config_class_directory + "/" + name + ".png")
|
||||
if error != OK:
|
||||
emit_signal("class_insertion_failed", tr("Could not copy icon"), tr("There was a problem while copying the icon. Was it already opened by another program?") + "\nError code: " + str(error))
|
||||
return
|
||||
else:
|
||||
emit_signal("class_insertion_failed", tr("Invalid icon size"), tr("Icon must be smaller than 22x22 pixels."))
|
||||
return
|
||||
|
||||
# Create class
|
||||
var class_source = ""
|
||||
class_source += "extends \"res://addons/DataEditor/data_item.gd\"\n\n"
|
||||
class_source += "export(String) var your_string_property = \"\"\n"
|
||||
class_source += "export(bool) var your_boolean_property = true\n"
|
||||
class_source += "export(Color) var your_color_property = Color(1,0,1)\n"
|
||||
class_source += "\n\n\n"
|
||||
class_source += "func _init(id).(id):\n"
|
||||
class_source += "\tpass\n"
|
||||
|
||||
var script_file = File.new()
|
||||
var directory = Directory.new()
|
||||
if not directory.dir_exists(config_class_directory):
|
||||
directory.make_dir(config_class_directory)
|
||||
|
||||
script_file.open(config_class_directory + "/" + name + ".gd", File.WRITE)
|
||||
script_file.store_string(class_source)
|
||||
script_file.close()
|
||||
load_manager()
|
||||
|
||||
|
||||
func sanitize_string(string):
|
||||
if config_sanitize_ids:
|
||||
return string.replace(" ", "_").replace("\\", "_").replace("/", "_").replace(":", "_").replace("*", "_").replace("?", "_").replace("\"", "_").replace("<", "_").replace(">", "_").replace("|", "_").to_lower()
|
||||
else:
|
||||
return string
|
||||
|
||||
func rename_id_if_exists(item_class, id):
|
||||
if not items[item_class].has(id):
|
||||
return id
|
||||
else:
|
||||
var regex = RegEx.new()
|
||||
regex.compile("(\\D*)(\\d*)")
|
||||
var has_valid_name = false
|
||||
var number = 0
|
||||
var current_name = id
|
||||
while(true):
|
||||
|
||||
regex.find(current_name)
|
||||
var id_without_number = regex.get_capture(1)
|
||||
var number_at_end_string = regex.get_capture(2)
|
||||
# var id_without_number = regex.search(current_name).get_string(1)
|
||||
# var number_at_end_string = regex.search(current_name).get_string(2)
|
||||
var number_at_end = int(number_at_end_string)
|
||||
number = number + number_at_end + 1
|
||||
var new_id = id_without_number + str(number)
|
||||
if not items[item_class].has(new_id):
|
||||
return new_id
|
||||
|
||||
|
||||
func rename_class(item_class, new_item_class):
|
||||
new_item_class = sanitize_string(new_item_class)
|
||||
var directory = Directory.new()
|
||||
if new_item_class == "":
|
||||
emit_signal("class_insertion_failed", tr("Invalid name"), tr("The class name cannot be empty."))
|
||||
return
|
||||
elif class_names.has(new_item_class):
|
||||
emit_signal("class_insertion_failed", tr("Invalid name"), tr("The class name already exists."))
|
||||
return
|
||||
|
||||
directory.rename(config_class_directory + item_class + ".gd", config_class_directory + new_item_class + ".gd")
|
||||
directory.rename(config_class_directory + item_class + ".png", config_class_directory + new_item_class + ".png")
|
||||
directory.rename(config_output_directory + "/" + item_class, config_output_directory + "/" + new_item_class)
|
||||
load_manager()
|
||||
|
||||
|
||||
func rename_extension_of_all_items(new_extension, serializer):
|
||||
var directory = Directory.new()
|
||||
for item_class in class_names:
|
||||
for id in items[item_class]:
|
||||
var item = items[item_class][id]
|
||||
var original_item_path = get_item_path(item)
|
||||
var new_item_path = original_item_path.replace("." + config_extension, "." + new_extension)
|
||||
if serializer == config_serializer:
|
||||
directory.rename(original_item_path, new_item_path)
|
||||
directory.remove(original_item_path)
|
||||
load_config()
|
||||
# load_manager()
|
||||
save_all_items()
|
||||
else:
|
||||
directory.remove(original_item_path)
|
||||
load_config()
|
||||
save_all_items()
|
||||
# load_manager()
|
||||
pass
|
||||
|
||||
|
||||
func delete_and_resave(is_encrypted, password):
|
||||
var directory = Directory.new()
|
||||
for item_class in class_names:
|
||||
for id in items[item_class]:
|
||||
var item = items[item_class][id]
|
||||
var item_path = get_item_path(item)
|
||||
directory.remove(item_path)
|
||||
pass
|
||||
pass
|
||||
load_config()
|
||||
save_all_items()
|
||||
|
||||
func has_unsaved_items():
|
||||
for item_class in items:
|
||||
for id in items[item_class]:
|
||||
var item = items[item_class][id]
|
||||
if item._dirty:
|
||||
return true
|
||||
pass
|
||||
pass
|
||||
return false
|
||||
|
||||
# TODO: Lazy loading
|
||||
# TODO: Proper path handling
|
||||
# TODO: Arrays
|
||||
# TODO: Proper renaming
|
285
item_tree.gd
Normal file
@ -0,0 +1,285 @@
|
||||
# Item Tree
|
||||
tool
|
||||
extends Control
|
||||
|
||||
var dummy_root = null
|
||||
|
||||
|
||||
onready var tree = get_node("Panel/VBox/Tree") # The item tree
|
||||
onready var class_context_menu = get_node("ClassContextMenu") # Context menu when right clicking on a class
|
||||
onready var instance_context_menu = get_node("InstanceContextMenu") # Context menu when right clicking on an instance
|
||||
onready var filter_control = get_node("Panel/VBox/Margin/HBoxContainer/Filter")
|
||||
|
||||
var item_manager = null # Item Manager, used to load, modify and save items
|
||||
|
||||
var tree_roots = {} # Holds a copy of all tree roots, i.e. classes, accessible by class name
|
||||
var tree_elements = {} # Reference to all tree elements, accessible by ["_roots"][item_class] (for the roots) and [item_class][id] for the items
|
||||
|
||||
signal on_delete_pressed # Emitted when item is deleted
|
||||
signal on_rename_pressed # Emitted when item is renamed
|
||||
signal on_duplicate_pressed # Emitted when item is duplicated
|
||||
signal on_new_item_pressed # Emitted when new item is created
|
||||
signal on_open # Emitted when item is opened in OS
|
||||
signal on_item_selected(item, is_leaf) # Emitted when a tree item is selected, carries the item and wheter or not it is a leaf element
|
||||
|
||||
var last_selected = null # Reference to last selected item
|
||||
var last_selected_id = "" # Name/id of last selected item
|
||||
var last_selected_class = "" # Name/id of last selected class
|
||||
|
||||
var plugin_config
|
||||
|
||||
var filter = ""
|
||||
|
||||
# Load the tree when ready
|
||||
func _ready():
|
||||
# test = EditorPlugin.new()
|
||||
tree.set_hide_root(true)
|
||||
tree.set_select_mode(Tree.SELECT_SINGLE)
|
||||
tree.set_allow_rmb_select(true)
|
||||
load_tree()
|
||||
|
||||
|
||||
func load_tree(is_reload = false):
|
||||
plugin_config = ConfigFile.new()
|
||||
plugin_config.load("res://addons/DataEditor/plugin.cfg")
|
||||
|
||||
|
||||
self.item_manager = Globals.get("item_manager")
|
||||
tree_elements = {}
|
||||
tree_roots = {}
|
||||
#filter_control.set_text("")
|
||||
#filter = ""
|
||||
last_selected_id = ""
|
||||
last_selected_class = ""
|
||||
last_selected = null
|
||||
var tree_element_to_be_selected = null
|
||||
|
||||
|
||||
# Store the class and name of the last selected item, in case the tree is reloaded
|
||||
last_selected = tree.get_selected()
|
||||
if last_selected:
|
||||
if last_selected.has_meta("item"):
|
||||
if last_selected.get_meta("item").get("_id"):
|
||||
self.last_selected_id = last_selected.get_meta("item")._id
|
||||
else:
|
||||
self.last_selected_id = null
|
||||
self.last_selected_class = last_selected.get_meta("class")
|
||||
#print(last_selected_id)
|
||||
#print(last_selected_class)
|
||||
|
||||
tree.clear()
|
||||
|
||||
|
||||
# Create the roots for each item class, e.g. actors, monsters, crystals...
|
||||
dummy_root = tree.create_item()
|
||||
tree_elements["_roots"] = {}
|
||||
var classes = item_manager.class_names
|
||||
for item_class in classes:
|
||||
var root = create_item_root(item_class)
|
||||
tree_elements["_roots"][item_class] = root
|
||||
pass
|
||||
|
||||
# Populate the list with items
|
||||
classes.sort()
|
||||
for item_class in classes:
|
||||
tree_elements[item_class] = {}
|
||||
|
||||
var ids = item_manager.items[item_class].keys()
|
||||
ids.sort()
|
||||
for id in ids:
|
||||
var item = item_manager.get_item(item_class, id)
|
||||
if filter == "" or id.find(filter) != -1:
|
||||
var tree_item = add_leaf(item, false)
|
||||
pass
|
||||
pass
|
||||
|
||||
if last_selected_id:
|
||||
tree_element_to_be_selected = get_tree_item(last_selected_class, last_selected_id)
|
||||
elif last_selected_class:
|
||||
tree_element_to_be_selected = get_tree_item(last_selected_class)
|
||||
elif tree.get_root().get_children():
|
||||
tree_element_to_be_selected = tree.get_root().get_children()
|
||||
else:
|
||||
tree_element_to_be_selected = null # No elements to be selected
|
||||
|
||||
last_selected
|
||||
|
||||
# Handle filter
|
||||
if tree_element_to_be_selected and not filter_control.has_focus():
|
||||
tree.grab_focus()
|
||||
tree_element_to_be_selected.select(0)
|
||||
|
||||
func create_item_root(item_class):
|
||||
var tree_item = tree.create_item(dummy_root)
|
||||
tree_item.set_selectable(0, true)
|
||||
tree_item.set_icon(0, load(plugin_config.get_value("custom", "class_directory") + "/" + item_class + ".png"))
|
||||
tree_item.set_text(0, item_class.capitalize())
|
||||
tree_item.set_meta("class", item_class)
|
||||
tree_roots[item_class] = tree_item
|
||||
return tree_item
|
||||
|
||||
func select_first_element():
|
||||
var first_element = tree.get_root().get_children()
|
||||
if first_element:
|
||||
first_element.select(0)
|
||||
first_element = first_element.get_children()
|
||||
last_selected = first_element.get_meta("item")
|
||||
last_selected_id = last_selected._id
|
||||
last_selected_class = last_selected._class
|
||||
return last_selected
|
||||
|
||||
|
||||
|
||||
# Creates a new tree item, optionally with an existing item
|
||||
func add_leaf(item, update_order):
|
||||
var id = item._id
|
||||
var item_class = item._class
|
||||
var tree_item = tree.create_item(tree_roots[item_class])
|
||||
set_tree_item_label_text(item, tree_item)
|
||||
tree_item.set_selectable(0, true)
|
||||
tree_item.set_meta("class", item_class)
|
||||
tree_item.set_meta("item", item)
|
||||
|
||||
tree_elements[item_class][id] = tree_item
|
||||
|
||||
# Don't order the tree in the beginning as the ids will already be sorted
|
||||
if update_order:
|
||||
# Move the items which come before in the alphabet to the top
|
||||
var elements_name_array = tree_elements[item_class].keys()
|
||||
elements_name_array.sort()
|
||||
tree_item.move_to_top()
|
||||
var to_be_reordered = []
|
||||
for element_name in elements_name_array:
|
||||
if str(id) > str(element_name) or str(id) == str(element_name):
|
||||
to_be_reordered.append(element_name)
|
||||
else:
|
||||
break
|
||||
pass
|
||||
to_be_reordered.invert()
|
||||
for element_name in to_be_reordered:
|
||||
tree_elements[item_class][element_name].move_to_top()
|
||||
pass
|
||||
tree_item.select(0)
|
||||
return tree_item
|
||||
|
||||
|
||||
func set_tree_item_label_text(item, tree_item = null):
|
||||
if tree_item == null:
|
||||
tree_item = get_tree_item(item._class, item._id)
|
||||
|
||||
if item._dirty or not item._persistent:
|
||||
tree_item.set_text(0, " " + item._display_name + " (*)")
|
||||
tree_item.set_custom_color(0, Color(1, 0.5, 0.5))
|
||||
else:
|
||||
tree_item.set_text(0, " " + item._display_name)
|
||||
tree_item.set_custom_color(0, Color(0.7, 0.7, 0.7))
|
||||
|
||||
func get_selected_item_root():
|
||||
var selected = tree.get_selected()
|
||||
if selected.has_meta("item"):
|
||||
return selected.get_parent()
|
||||
else:
|
||||
return selected
|
||||
|
||||
|
||||
# A tree element in the tree was selected (either class or item)
|
||||
func _on_Tree_cell_selected():
|
||||
emit_signal("on_item_selected", get_selected_item(), get_selected_class())
|
||||
|
||||
func get_selected_item():
|
||||
var selected = tree.get_selected()
|
||||
if selected.has_meta("item"):
|
||||
return selected.get_meta("item")
|
||||
else:
|
||||
return null
|
||||
|
||||
|
||||
func get_selected_class():
|
||||
return tree.get_selected().get_meta("class")
|
||||
|
||||
|
||||
func get_tree_item(item_class, id = ""):
|
||||
if id != "":
|
||||
if tree_elements.has(item_class) and tree_elements[item_class].has(id):
|
||||
return tree_elements[item_class][id]
|
||||
else:
|
||||
return null
|
||||
else:
|
||||
if tree_elements["_roots"].has(item_class):
|
||||
return tree_elements["_roots"][item_class]
|
||||
else:
|
||||
return null
|
||||
|
||||
# Sets the selection to a specific tree item
|
||||
func select_item(item):
|
||||
if tree_elements.has(item._class) and tree_elements[item._class].has(item._id):
|
||||
tree_elements[item._class][item._id].select(0)
|
||||
|
||||
# Sets the selection to a specific class
|
||||
func select_class(item_class):
|
||||
if tree_elements["_roots"].has(item_class):
|
||||
tree_elements["_roots"][item_class].select(0)
|
||||
|
||||
|
||||
func _on_Filter_text_changed( text ):
|
||||
self.filter = text
|
||||
load_tree()
|
||||
|
||||
|
||||
|
||||
# Right click context menu on leafs
|
||||
# 0 = Add | 1 = Rename | 2 = Delete | 3 = Duplicate | 4 = Open
|
||||
func _on_InstanceContextMenu_item_pressed(index):
|
||||
if index == 0:
|
||||
emit_signal("on_new_item_pressed")
|
||||
elif index == 1:
|
||||
emit_signal("on_rename_pressed")
|
||||
elif index == 2:
|
||||
emit_signal("on_delete_pressed")
|
||||
elif index == 3:
|
||||
emit_signal("on_duplicate_pressed")
|
||||
elif index == 4:
|
||||
emit_signal("on_open")
|
||||
|
||||
###########################################################
|
||||
# CLASS CONTEXT MENU #
|
||||
###########################################################
|
||||
func _on_ClassContextMenu_about_to_show():
|
||||
class_context_menu.set_item_text(0, tr("Add") + " " + get_selected_class().capitalize())
|
||||
|
||||
func _on_InstanceContextMenu_about_to_show():
|
||||
pass
|
||||
|
||||
# Right click context menu on class branch
|
||||
# 0 = Add | 1 = Delete Class
|
||||
func _on_ClassContextMenu_item_pressed(index):
|
||||
if index == 0:
|
||||
emit_signal("on_new_item_pressed")
|
||||
elif index == 1:
|
||||
emit_signal("on_delete_pressed")
|
||||
elif index == 2:
|
||||
emit_signal("on_rename_pressed")
|
||||
|
||||
|
||||
|
||||
func _on_Tree_item_rmb_selected(pos):
|
||||
var is_leaf = get_selected_item() != null
|
||||
if is_leaf:
|
||||
instance_context_menu.set_pos(get_global_mouse_pos())
|
||||
instance_context_menu.popup()
|
||||
else:
|
||||
class_context_menu.set_pos(get_global_mouse_pos())
|
||||
class_context_menu.popup()
|
||||
|
||||
|
||||
func _on_DeleteItemDialog_confirmed():
|
||||
var selected_item = get_selected_item()
|
||||
if selected_item != null:
|
||||
item_manager.delete_item(selected_item)
|
||||
load_tree()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#TODO: Proper memory management?
|
196
item_tree.tscn
Normal file
@ -0,0 +1,196 @@
|
||||
[gd_scene load_steps=8 format=1]
|
||||
|
||||
[ext_resource path="res://addons/DataEditor/item_tree.gd" type="Script" id=1]
|
||||
[ext_resource path="res://addons/DataEditor/icons/icon_add.png" type="Texture" id=2]
|
||||
[ext_resource path="res://addons/DataEditor/icons/icon_remove.png" type="Texture" id=3]
|
||||
[ext_resource path="res://addons/DataEditor/icons/icon_rename.png" type="Texture" id=4]
|
||||
[ext_resource path="res://addons/DataEditor/icons/icon_duplicate.png" type="Texture" id=5]
|
||||
[ext_resource path="res://addons/DataEditor/icons/icon_load.png" type="Texture" id=6]
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id=1]
|
||||
|
||||
content_margin/left = -1.0
|
||||
content_margin/right = -1.0
|
||||
content_margin/top = -1.0
|
||||
content_margin/bottom = -1.0
|
||||
bg_color = Color( 0.172549, 0.164706, 0.196078, 1 )
|
||||
light_color = Color( 0.113725, 0.113725, 0.12549, 1 )
|
||||
dark_color = Color( 0.113725, 0.113725, 0.12549, 1 )
|
||||
border_size = 2
|
||||
border_blend = true
|
||||
draw_bg = true
|
||||
|
||||
[node name="Tree" type="Control"]
|
||||
|
||||
anchor/right = 1
|
||||
anchor/bottom = 1
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 3
|
||||
size_flags/vertical = 3
|
||||
margin/left = 0.0
|
||||
margin/top = 0.0
|
||||
margin/right = 0.0
|
||||
margin/bottom = 0.0
|
||||
script/script = ExtResource( 1 )
|
||||
|
||||
[node name="Panel" type="PanelContainer" parent="."]
|
||||
|
||||
anchor/right = 1
|
||||
anchor/bottom = 1
|
||||
rect/min_size = Vector2( 150, 0 )
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 3
|
||||
size_flags/vertical = 3
|
||||
margin/left = 0.0
|
||||
margin/top = 50.0
|
||||
margin/right = 0.0
|
||||
margin/bottom = 0.0
|
||||
|
||||
[node name="VBox" type="VBoxContainer" parent="Panel"]
|
||||
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = false
|
||||
size_flags/horizontal = 3
|
||||
size_flags/vertical = 3
|
||||
margin/left = 7.0
|
||||
margin/top = 7.0
|
||||
margin/right = 1273.0
|
||||
margin/bottom = 543.0
|
||||
alignment = 0
|
||||
|
||||
[node name="Margin" type="MarginContainer" parent="Panel/VBox"]
|
||||
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 2
|
||||
margin/left = 0.0
|
||||
margin/top = 0.0
|
||||
margin/right = 1266.0
|
||||
margin/bottom = 24.0
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="Panel/VBox/Margin"]
|
||||
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = false
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 2
|
||||
margin/left = 8.0
|
||||
margin/top = 0.0
|
||||
margin/right = 1266.0
|
||||
margin/bottom = 24.0
|
||||
alignment = 0
|
||||
|
||||
[node name="AddButton" type="ToolButton" parent="Panel/VBox/Margin/HBoxContainer"]
|
||||
|
||||
visibility/visible = false
|
||||
rect/min_size = Vector2( 28, 28 )
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 2
|
||||
margin/left = 0.0
|
||||
margin/top = 0.0
|
||||
margin/right = 28.0
|
||||
margin/bottom = 28.0
|
||||
toggle_mode = false
|
||||
enabled_focus_mode = 2
|
||||
shortcut = null
|
||||
icon = ExtResource( 2 )
|
||||
flat = true
|
||||
|
||||
[node name="AddButton2" type="ToolButton" parent="Panel/VBox/Margin/HBoxContainer"]
|
||||
|
||||
visibility/visible = false
|
||||
rect/min_size = Vector2( 28, 28 )
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 2
|
||||
margin/left = 32.0
|
||||
margin/top = 0.0
|
||||
margin/right = 60.0
|
||||
margin/bottom = 28.0
|
||||
toggle_mode = false
|
||||
enabled_focus_mode = 2
|
||||
shortcut = null
|
||||
icon = ExtResource( 3 )
|
||||
flat = true
|
||||
|
||||
[node name="Filter" type="LineEdit" parent="Panel/VBox/Margin/HBoxContainer"]
|
||||
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 3
|
||||
size_flags/vertical = 2
|
||||
margin/left = 0.0
|
||||
margin/top = 0.0
|
||||
margin/right = 1258.0
|
||||
margin/bottom = 24.0
|
||||
placeholder/text = "Search"
|
||||
placeholder/alpha = 0.6
|
||||
focus_mode = 2
|
||||
caret/caret_blink = false
|
||||
caret/caret_blink_speed = 0.65
|
||||
|
||||
[node name="Tree" type="Tree" parent="Panel/VBox"]
|
||||
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 3
|
||||
size_flags/vertical = 3
|
||||
margin/left = 0.0
|
||||
margin/top = 28.0
|
||||
margin/right = 1266.0
|
||||
margin/bottom = 536.0
|
||||
custom_styles/bg = SubResource( 1 )
|
||||
custom_colors/guide_color = Color( 0.398438, 0.398438, 0.398438, 1 )
|
||||
custom_constants/hseparation = 4
|
||||
|
||||
[node name="ClassContextMenu" type="PopupMenu" parent="."]
|
||||
|
||||
visibility/visible = false
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 2
|
||||
margin/left = 0.0
|
||||
margin/top = 0.0
|
||||
margin/right = 20.0
|
||||
margin/bottom = 20.0
|
||||
popup/exclusive = false
|
||||
items = [ "Add Item", ExtResource( 2 ), false, false, false, 0, 0, null, "", false, "Delete Class", ExtResource( 3 ), false, false, false, 1, 0, null, "", false, "Rename Class", ExtResource( 4 ), false, false, false, -1, 0, null, "", false ]
|
||||
|
||||
[node name="InstanceContextMenu" type="PopupMenu" parent="."]
|
||||
|
||||
visibility/visible = false
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 2
|
||||
margin/left = 0.0
|
||||
margin/top = 0.0
|
||||
margin/right = 20.0
|
||||
margin/bottom = 20.0
|
||||
popup/exclusive = false
|
||||
items = [ "Add", ExtResource( 2 ), false, false, false, 0, 0, null, "", false, "Rename", ExtResource( 4 ), false, false, false, 1, 0, null, "", false, "Delete", ExtResource( 3 ), false, false, false, 2, 0, null, "", false, "Duplicate", ExtResource( 5 ), false, false, false, 3, 0, null, "", false, "Open File", ExtResource( 6 ), false, false, true, 4, 0, null, "", false ]
|
||||
|
||||
[connection signal="button_down" from="Panel/VBox/Margin/HBoxContainer/AddButton" to="." method="_on_AddButton_button_down"]
|
||||
|
||||
[connection signal="text_changed" from="Panel/VBox/Margin/HBoxContainer/Filter" to="." method="_on_Filter_text_changed"]
|
||||
|
||||
[connection signal="cell_selected" from="Panel/VBox/Tree" to="." method="_on_Tree_cell_selected"]
|
||||
|
||||
[connection signal="item_rmb_selected" from="Panel/VBox/Tree" to="." method="_on_Tree_item_rmb_selected"]
|
||||
|
||||
[connection signal="about_to_show" from="ClassContextMenu" to="." method="_on_ClassContextMenu_about_to_show"]
|
||||
|
||||
[connection signal="item_pressed" from="ClassContextMenu" to="." method="_on_ClassContextMenu_item_pressed"]
|
||||
|
||||
[connection signal="about_to_show" from="InstanceContextMenu" to="." method="_on_InstanceContextMenu_about_to_show"]
|
||||
|
||||
[connection signal="item_pressed" from="InstanceContextMenu" to="." method="_on_InstanceContextMenu_item_pressed"]
|
||||
|
||||
|
115
no_classes.tscn
Normal file
@ -0,0 +1,115 @@
|
||||
[gd_scene load_steps=3 format=1]
|
||||
|
||||
[ext_resource path="res://addons/DataEditor/style/light_gray_panel.tres" type="StyleBox" id=1]
|
||||
[ext_resource path="res://addons/DataEditor/fonts/droid_serif_bold.tres" type="DynamicFont" id=2]
|
||||
|
||||
[node name="Control" type="Panel"]
|
||||
|
||||
anchor/right = 1
|
||||
anchor/bottom = 1
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 3
|
||||
size_flags/vertical = 3
|
||||
margin/left = 2.0
|
||||
margin/top = -1.0
|
||||
margin/right = -2.0
|
||||
margin/bottom = 1.0
|
||||
custom_styles/panel = ExtResource( 1 )
|
||||
|
||||
[node name="Body" type="VBoxContainer" parent="."]
|
||||
|
||||
anchor/right = 1
|
||||
anchor/bottom = 1
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = false
|
||||
size_flags/horizontal = 3
|
||||
size_flags/vertical = 3
|
||||
margin/left = 10.0
|
||||
margin/top = 10.0
|
||||
margin/right = 10.0
|
||||
margin/bottom = 10.0
|
||||
alignment = 0
|
||||
|
||||
[node name="TitleLabel" type="Label" parent="Body"]
|
||||
|
||||
focus/ignore_mouse = true
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 0
|
||||
margin/left = 0.0
|
||||
margin/top = 0.0
|
||||
margin/right = 1260.0
|
||||
margin/bottom = 2.0
|
||||
custom_fonts/font = ExtResource( 2 )
|
||||
text = "No Classes"
|
||||
uppercase = true
|
||||
percent_visible = 1.0
|
||||
lines_skipped = 0
|
||||
max_lines_visible = -1
|
||||
|
||||
[node name="HSeparator" type="HSeparator" parent="Body"]
|
||||
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 2
|
||||
margin/left = 0.0
|
||||
margin/top = 6.0
|
||||
margin/right = 1260.0
|
||||
margin/bottom = 9.0
|
||||
|
||||
[node name="Spacer" type="Control" parent="Body"]
|
||||
|
||||
rect/min_size = Vector2( 0, 15 )
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 2
|
||||
margin/left = 0.0
|
||||
margin/top = 13.0
|
||||
margin/right = 1260.0
|
||||
margin/bottom = 28.0
|
||||
|
||||
[node name="Scroll" type="ScrollContainer" parent="Body"]
|
||||
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 3
|
||||
size_flags/vertical = 3
|
||||
margin/left = 0.0
|
||||
margin/top = 32.0
|
||||
margin/right = 1260.0
|
||||
margin/bottom = 580.0
|
||||
scroll/horizontal = true
|
||||
scroll/vertical = true
|
||||
|
||||
[node name="ClassProperties" type="VBoxContainer" parent="Body/Scroll"]
|
||||
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = false
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 2
|
||||
margin/left = 0.0
|
||||
margin/top = 0.0
|
||||
margin/right = 699.0
|
||||
margin/bottom = 31.0
|
||||
alignment = 0
|
||||
|
||||
[node name="NoClassPropertiesLabel" type="Label" parent="Body/Scroll/ClassProperties"]
|
||||
|
||||
focus/ignore_mouse = true
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 3
|
||||
size_flags/vertical = 0
|
||||
margin/left = 0.0
|
||||
margin/top = 0.0
|
||||
margin/right = 699.0
|
||||
margin/bottom = 31.0
|
||||
text = "Currently, there are no classes. Click on \"Add Class\" to add a new class and make the necessary adjustments.
|
||||
Once you feel ready, press \"Refresh\" and start adding instances/items."
|
||||
percent_visible = 1.0
|
||||
lines_skipped = 0
|
||||
max_lines_visible = -1
|
||||
|
||||
|
21
notes.txt
Normal file
@ -0,0 +1,21 @@
|
||||
Add "Notes" to each element
|
||||
Add Display Name for each
|
||||
Add Refresh
|
||||
Check how to deal with reloading the tree
|
||||
Add Sub categories
|
||||
Translate
|
||||
Add "state" handling
|
||||
Add UndoRedo
|
||||
|
||||
Move short callbacks from self to the object and call directly (e.g. popup)
|
||||
|
||||
Use EditorFileSystem class to check for changes (external ones)
|
||||
|
||||
print_progress
|
||||
|
||||
|
||||
Handle when switching from encrypted to non-encrypted and vice versa
|
||||
|
||||
# Progress?
|
||||
get_status(story_item)
|
||||
set_status(story_item, value)
|
190
options.gd
Normal file
@ -0,0 +1,190 @@
|
||||
tool
|
||||
extends AcceptDialog
|
||||
|
||||
var config = null
|
||||
|
||||
onready var serializer_label = get_node("Panel/GridContainer/SerializerLabel")
|
||||
onready var serializer_option = get_node("Panel/GridContainer/SerializerOption")
|
||||
onready var extension_label = get_node("Panel/GridContainer/ExtensionLabel")
|
||||
onready var extension_line_edit = get_node("Panel/GridContainer/ExtensionLineEdit")
|
||||
onready var encrypt_label = get_node("Panel/GridContainer/EncryptLabel")
|
||||
onready var encrypt_check_box = get_node("Panel/GridContainer/EncryptCheckBox")
|
||||
onready var password_label = get_node("Panel/GridContainer/PasswordLabel")
|
||||
onready var password_line_edit = get_node("Panel/GridContainer/PasswordLineEdit")
|
||||
onready var output_directory_label = get_node("Panel/GridContainer/OutputDirectoryLabel")
|
||||
onready var output_directory_line_edit = get_node("Panel/GridContainer/OutputDirectoryHBox/OutputDirectoryLineEdit")
|
||||
onready var class_directory_label = get_node("Panel/GridContainer/OutputDirectoryLabel")
|
||||
onready var class_directory_line_edit = get_node("Panel/GridContainer/ClassDirectoryHBox/ClassDirectoryLineEdit")
|
||||
onready var sanitize_ids_label = get_node("Panel/GridContainer/SanitizeIdsLabel")
|
||||
onready var sanitize_ids_check_box = get_node("Panel/GridContainer/SanitizeIdsCheckBox")
|
||||
|
||||
onready var warn_dialog = get_node("WarnDialog")
|
||||
|
||||
var extension = ""
|
||||
var serializer = ""
|
||||
var encrypt = false
|
||||
var password = ""
|
||||
var class_directory = ""
|
||||
var output_directory = ""
|
||||
var sanitize_ids = true
|
||||
|
||||
signal extension_changed(new_extension, serializer)
|
||||
signal encryption_changed(is_encrypted, password)
|
||||
|
||||
func _ready():
|
||||
self.set_title(tr("Options"))
|
||||
self.add_cancel(tr("Cancel")) # TODO: Does this keep on adding cancels?
|
||||
serializer_label.set_text(tr("Serializer"))
|
||||
extension_label.set_text(tr("File Extension"))
|
||||
encrypt_label.set_text(tr("Encrypt Files"))
|
||||
class_directory_label.set_text(tr("Class Directory"))
|
||||
output_directory_label.set_text(tr("Output Directory"))
|
||||
sanitize_ids_label.set_text(tr("Sanitize IDs"))
|
||||
|
||||
config = ConfigFile.new()
|
||||
config.load("res://addons/DataEditor/plugin.cfg")
|
||||
serializer = config.get_value("custom", "serializer")
|
||||
extension = config.get_value("custom", "extension")
|
||||
|
||||
class_directory = config.get_value("custom", "class_directory")
|
||||
sanitize_ids = config.get_value("custom", "sanitize_ids")
|
||||
encrypt = config.get_value("custom", "encrypt")
|
||||
password = config.get_value("custom", "password")
|
||||
output_directory = config.get_value("custom", "output_directory")
|
||||
sanitize_ids = config.get_value("custom", "sanitize_ids")
|
||||
|
||||
serializer_option.clear()
|
||||
serializer_option.add_item("json", 0)
|
||||
serializer_option.add_item("binary", 1)
|
||||
if serializer == "json":
|
||||
serializer_option.select(0)
|
||||
elif serializer == "binary":
|
||||
serializer_option.select(1)
|
||||
else:
|
||||
serializer_option.select(0)
|
||||
serializer = "json"
|
||||
|
||||
extension_line_edit.set_text(extension)
|
||||
|
||||
if serializer == "binary":
|
||||
encrypt_check_box.set_disabled(false)
|
||||
password_line_edit.set_editable(true)
|
||||
else:
|
||||
encrypt = false
|
||||
password = ""
|
||||
encrypt_check_box.set_disabled(true)
|
||||
password_line_edit.set_editable(false)
|
||||
|
||||
encrypt_check_box.set_pressed(encrypt)
|
||||
encrypt_check_box.set_text(str(encrypt))
|
||||
|
||||
password_line_edit.set_text(str(password))
|
||||
|
||||
class_directory_line_edit.set_text(str(class_directory))
|
||||
output_directory_line_edit.set_text(str(output_directory))
|
||||
|
||||
sanitize_ids_check_box.set_pressed(sanitize_ids)
|
||||
sanitize_ids_check_box.set_text(str(sanitize_ids))
|
||||
|
||||
func _on_SerializerOption_item_selected(index):
|
||||
if index == 0:
|
||||
extension_line_edit.set_text("json")
|
||||
encrypt = false
|
||||
password = ""
|
||||
encrypt_check_box.set_disabled(true)
|
||||
password_line_edit.set_editable(false)
|
||||
if index == 1:
|
||||
extension_line_edit.set_text("gob")
|
||||
encrypt_check_box.set_disabled(false)
|
||||
password_line_edit.set_editable(true)
|
||||
|
||||
|
||||
|
||||
func _on_Options_confirmed():
|
||||
extract_values()
|
||||
extension = extension.strip_edges()
|
||||
if extension.begins_with("."):
|
||||
extension = extension.replace(".", "")
|
||||
|
||||
# TODO: Validate
|
||||
var error_message = ""
|
||||
# if self.serializer != "binary" or self.serializer != "json":
|
||||
# error_message = tr("Please choose either 'json' or 'binary' as serializer.\n")
|
||||
if self.extension == "":
|
||||
error_message = tr("Please choose a valid file extension, e.g. 'gob' or 'json'.")
|
||||
if self.class_directory == "" or not self.class_directory.begins_with("res://"):
|
||||
error_message = tr("The class directory must be a resource path, e.g. 'res://classes'.")
|
||||
if self.output_directory == "" or not self.output_directory.begins_with("res://"):
|
||||
error_message = tr("The output directory must be a resource path, e.g. 'res://data'.")
|
||||
|
||||
var extension_changed = false
|
||||
var encryption_changed = false
|
||||
if extension != config.get_value("custom", "extension") or serializer != config.get_value("custom", "serializer"):
|
||||
extension_changed = true
|
||||
|
||||
if encrypt != config.get_value("custom", "encrypt") or password != config.get_value("custom", "password"):
|
||||
encryption_changed = true
|
||||
|
||||
if error_message == "":
|
||||
config.set_value("custom", "extension", extension)
|
||||
config.set_value("custom", "serializer", serializer)
|
||||
config.set_value("custom", "encrypt", encrypt)
|
||||
config.set_value("custom", "password", password)
|
||||
config.set_value("custom", "class_directory", output_directory)
|
||||
config.set_value("custom", "output_directory", output_directory)
|
||||
config.set_value("custom", "sanitize_ids", sanitize_ids)
|
||||
config.save("res://addons/DataEditor/plugin.cfg")
|
||||
hide()
|
||||
else:
|
||||
warn_dialog.set_text(error_message)
|
||||
warn_dialog.popup_centered()
|
||||
|
||||
if extension_changed:
|
||||
emit_signal("extension_changed", extension, serializer)
|
||||
|
||||
if encryption_changed:
|
||||
emit_signal("encryption_changed", encrypt, password)
|
||||
# TODO: Add a tip to NOT FORGET THE PASSWORD
|
||||
|
||||
func extract_values():
|
||||
serializer = serializer_option.get_item_text(serializer_option.get_selected())
|
||||
extension = extension_line_edit.get_text()
|
||||
encrypt = encrypt_check_box.is_pressed()
|
||||
password = password_line_edit.get_text()
|
||||
output_directory = output_directory_line_edit.get_text()
|
||||
sanitize_ids = sanitize_ids_check_box.is_pressed()
|
||||
|
||||
func _on_ClassDirectoryButton_button_down():
|
||||
var dialog = EditorFileDialog.new()
|
||||
dialog.set_mode(EditorFileDialog.MODE_OPEN_DIR)
|
||||
dialog.connect("dir_selected", self, "set_class_directory", [])
|
||||
if not self.find_node("EditorFileDialog"):
|
||||
add_child(dialog)
|
||||
else:
|
||||
get_node("EditorFileDialog").popup_centered()
|
||||
dialog.popup_centered_ratio()
|
||||
|
||||
|
||||
func _on_OutputDirectoryButton_button_down():
|
||||
var dialog = EditorFileDialog.new()
|
||||
dialog.set_mode(EditorFileDialog.MODE_OPEN_DIR)
|
||||
dialog.connect("dir_selected", self, "set_output_directory", [])
|
||||
if not self.find_node("EditorFileDialog"):
|
||||
add_child(dialog)
|
||||
else:
|
||||
get_node("EditorFileDialog").popup_centered()
|
||||
dialog.popup_centered_ratio()
|
||||
|
||||
func set_class_directory(selected_directory):
|
||||
class_directory = selected_directory
|
||||
class_directory_line_edit.set_text(selected_directory)
|
||||
|
||||
func set_output_directory(selected_directory):
|
||||
output_directory = selected_directory
|
||||
output_directory_line_edit.set_text(selected_directory)
|
||||
|
||||
func _on_EncryptCheckBox_button_down():
|
||||
encrypt_check_box.set_text(str(!encrypt_check_box.is_pressed()))
|
||||
|
||||
func _on_SanitizeIdsCheckBox_button_down():
|
||||
sanitize_ids_check_box.set_text(str(!sanitize_ids_check_box.is_pressed()))
|
359
options.tscn
Normal file
@ -0,0 +1,359 @@
|
||||
[gd_scene load_steps=4 format=1]
|
||||
|
||||
[ext_resource path="res://addons/DataEditor/options.gd" type="Script" id=1]
|
||||
[ext_resource path="res://addons/DataEditor/style/light_gray_panel.tres" type="StyleBox" id=2]
|
||||
[ext_resource path="res://addons/DataEditor/icons/icon_load.png" type="Texture" id=3]
|
||||
|
||||
[node name="Options" type="AcceptDialog"]
|
||||
|
||||
rect/min_size = Vector2( 400, 300 )
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 2
|
||||
margin/left = 0.0
|
||||
margin/top = 0.0
|
||||
margin/right = 400.0
|
||||
margin/bottom = 300.0
|
||||
popup/exclusive = true
|
||||
window/title = "Options"
|
||||
dialog/hide_on_ok = false
|
||||
script/script = ExtResource( 1 )
|
||||
|
||||
[node name="Panel" type="Panel" parent="."]
|
||||
|
||||
anchor/right = 1
|
||||
anchor/bottom = 1
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 2
|
||||
margin/left = 8.0
|
||||
margin/top = 8.0
|
||||
margin/right = 8.0
|
||||
margin/bottom = 40.0
|
||||
custom_styles/panel = ExtResource( 2 )
|
||||
|
||||
[node name="GridContainer" type="GridContainer" parent="Panel"]
|
||||
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = false
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 2
|
||||
margin/left = 7.0
|
||||
margin/top = 7.0
|
||||
margin/right = 377.0
|
||||
margin/bottom = 255.0
|
||||
custom_constants/vseparation = 10
|
||||
custom_constants/hseparation = 5
|
||||
columns = 2
|
||||
|
||||
[node name="SerializerLabel" type="Label" parent="Panel/GridContainer"]
|
||||
|
||||
rect/min_size = Vector2( 150, 0 )
|
||||
focus/ignore_mouse = true
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 0
|
||||
margin/left = 0.0
|
||||
margin/top = 3.0
|
||||
margin/right = 150.0
|
||||
margin/bottom = 17.0
|
||||
text = "Serializer"
|
||||
percent_visible = 1.0
|
||||
lines_skipped = 0
|
||||
max_lines_visible = -1
|
||||
|
||||
[node name="SerializerOption" type="OptionButton" parent="Panel/GridContainer"]
|
||||
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 3
|
||||
size_flags/vertical = 2
|
||||
margin/left = 155.0
|
||||
margin/top = 0.0
|
||||
margin/right = 370.0
|
||||
margin/bottom = 20.0
|
||||
toggle_mode = false
|
||||
enabled_focus_mode = 2
|
||||
shortcut = null
|
||||
text = "json"
|
||||
flat = false
|
||||
align = 0
|
||||
selected = 0
|
||||
items = [ "json", null, false, 0, null, "binary", null, false, 1, null ]
|
||||
|
||||
[node name="ExtensionLabel" type="Label" parent="Panel/GridContainer"]
|
||||
|
||||
focus/ignore_mouse = true
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 0
|
||||
margin/left = 0.0
|
||||
margin/top = 35.0
|
||||
margin/right = 150.0
|
||||
margin/bottom = 49.0
|
||||
text = "File Extension"
|
||||
percent_visible = 1.0
|
||||
lines_skipped = 0
|
||||
max_lines_visible = -1
|
||||
|
||||
[node name="ExtensionLineEdit" type="LineEdit" parent="Panel/GridContainer"]
|
||||
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 2
|
||||
margin/left = 155.0
|
||||
margin/top = 30.0
|
||||
margin/right = 370.0
|
||||
margin/bottom = 54.0
|
||||
text = "json"
|
||||
placeholder/alpha = 0.6
|
||||
focus_mode = 2
|
||||
caret/caret_blink = false
|
||||
caret/caret_blink_speed = 0.65
|
||||
|
||||
[node name="EncryptLabel" type="Label" parent="Panel/GridContainer"]
|
||||
|
||||
focus/ignore_mouse = true
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 0
|
||||
margin/left = 0.0
|
||||
margin/top = 68.0
|
||||
margin/right = 150.0
|
||||
margin/bottom = 82.0
|
||||
text = "Encrypt Files"
|
||||
percent_visible = 1.0
|
||||
lines_skipped = 0
|
||||
max_lines_visible = -1
|
||||
|
||||
[node name="EncryptCheckBox" type="CheckBox" parent="Panel/GridContainer"]
|
||||
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 2
|
||||
margin/left = 155.0
|
||||
margin/top = 64.0
|
||||
margin/right = 370.0
|
||||
margin/bottom = 87.0
|
||||
disabled = true
|
||||
toggle_mode = true
|
||||
enabled_focus_mode = 2
|
||||
shortcut = null
|
||||
text = "False"
|
||||
flat = false
|
||||
align = 0
|
||||
|
||||
[node name="PasswordLabel" type="Label" parent="Panel/GridContainer"]
|
||||
|
||||
focus/ignore_mouse = true
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 0
|
||||
margin/left = 0.0
|
||||
margin/top = 102.0
|
||||
margin/right = 150.0
|
||||
margin/bottom = 116.0
|
||||
text = "Password"
|
||||
percent_visible = 1.0
|
||||
lines_skipped = 0
|
||||
max_lines_visible = -1
|
||||
|
||||
[node name="PasswordLineEdit" type="LineEdit" parent="Panel/GridContainer"]
|
||||
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 2
|
||||
margin/left = 155.0
|
||||
margin/top = 97.0
|
||||
margin/right = 370.0
|
||||
margin/bottom = 121.0
|
||||
placeholder/alpha = 0.6
|
||||
editable = false
|
||||
focus_mode = 2
|
||||
caret/caret_blink = false
|
||||
caret/caret_blink_speed = 0.65
|
||||
|
||||
[node name="ClassDirectoryLabel" type="Label" parent="Panel/GridContainer"]
|
||||
|
||||
focus/ignore_mouse = true
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 0
|
||||
margin/left = 0.0
|
||||
margin/top = 136.0
|
||||
margin/right = 150.0
|
||||
margin/bottom = 150.0
|
||||
text = "Class Directory"
|
||||
percent_visible = 1.0
|
||||
lines_skipped = 0
|
||||
max_lines_visible = -1
|
||||
|
||||
[node name="ClassDirectoryHBox" type="HBoxContainer" parent="Panel/GridContainer"]
|
||||
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = false
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 2
|
||||
margin/left = 155.0
|
||||
margin/top = 131.0
|
||||
margin/right = 370.0
|
||||
margin/bottom = 155.0
|
||||
alignment = 0
|
||||
|
||||
[node name="ClassDirectoryLineEdit" type="LineEdit" parent="Panel/GridContainer/ClassDirectoryHBox"]
|
||||
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 3
|
||||
size_flags/vertical = 2
|
||||
margin/left = 0.0
|
||||
margin/top = 0.0
|
||||
margin/right = 183.0
|
||||
margin/bottom = 24.0
|
||||
text = "res://classes"
|
||||
placeholder/alpha = 0.6
|
||||
focus_mode = 2
|
||||
caret/caret_blink = false
|
||||
caret/caret_blink_speed = 0.65
|
||||
|
||||
[node name="ClassDirectoryButton" type="Button" parent="Panel/GridContainer/ClassDirectoryHBox"]
|
||||
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 2
|
||||
margin/left = 187.0
|
||||
margin/top = 0.0
|
||||
margin/right = 215.0
|
||||
margin/bottom = 24.0
|
||||
toggle_mode = false
|
||||
enabled_focus_mode = 2
|
||||
shortcut = null
|
||||
icon = ExtResource( 3 )
|
||||
flat = false
|
||||
|
||||
[node name="OutputDirectoryLabel" type="Label" parent="Panel/GridContainer"]
|
||||
|
||||
focus/ignore_mouse = true
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 0
|
||||
margin/left = 0.0
|
||||
margin/top = 170.0
|
||||
margin/right = 150.0
|
||||
margin/bottom = 184.0
|
||||
text = "Output Directory"
|
||||
percent_visible = 1.0
|
||||
lines_skipped = 0
|
||||
max_lines_visible = -1
|
||||
|
||||
[node name="OutputDirectoryHBox" type="HBoxContainer" parent="Panel/GridContainer"]
|
||||
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = false
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 2
|
||||
margin/left = 155.0
|
||||
margin/top = 165.0
|
||||
margin/right = 370.0
|
||||
margin/bottom = 189.0
|
||||
alignment = 0
|
||||
|
||||
[node name="OutputDirectoryLineEdit" type="LineEdit" parent="Panel/GridContainer/OutputDirectoryHBox"]
|
||||
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 3
|
||||
size_flags/vertical = 2
|
||||
margin/left = 0.0
|
||||
margin/top = 0.0
|
||||
margin/right = 183.0
|
||||
margin/bottom = 24.0
|
||||
text = "res://data"
|
||||
placeholder/alpha = 0.6
|
||||
focus_mode = 2
|
||||
caret/caret_blink = false
|
||||
caret/caret_blink_speed = 0.65
|
||||
|
||||
[node name="OutputDirectoryButton" type="Button" parent="Panel/GridContainer/OutputDirectoryHBox"]
|
||||
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 2
|
||||
margin/left = 187.0
|
||||
margin/top = 0.0
|
||||
margin/right = 215.0
|
||||
margin/bottom = 24.0
|
||||
toggle_mode = false
|
||||
enabled_focus_mode = 2
|
||||
shortcut = null
|
||||
icon = ExtResource( 3 )
|
||||
flat = false
|
||||
|
||||
[node name="SanitizeIdsLabel" type="Label" parent="Panel/GridContainer"]
|
||||
|
||||
focus/ignore_mouse = true
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 0
|
||||
margin/left = 0.0
|
||||
margin/top = 203.0
|
||||
margin/right = 150.0
|
||||
margin/bottom = 217.0
|
||||
text = "Sanitize IDs"
|
||||
percent_visible = 1.0
|
||||
lines_skipped = 0
|
||||
max_lines_visible = -1
|
||||
|
||||
[node name="SanitizeIdsCheckBox" type="CheckBox" parent="Panel/GridContainer"]
|
||||
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 2
|
||||
margin/left = 155.0
|
||||
margin/top = 199.0
|
||||
margin/right = 370.0
|
||||
margin/bottom = 222.0
|
||||
toggle_mode = true
|
||||
is_pressed = true
|
||||
enabled_focus_mode = 2
|
||||
shortcut = null
|
||||
text = "True"
|
||||
flat = false
|
||||
align = 0
|
||||
|
||||
[node name="WarnDialog" type="AcceptDialog" parent="."]
|
||||
|
||||
visibility/visible = false
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 2
|
||||
margin/left = 0.0
|
||||
margin/top = 0.0
|
||||
margin/right = 260.0
|
||||
margin/bottom = 110.0
|
||||
popup/exclusive = false
|
||||
window/title = "Alert!"
|
||||
dialog/hide_on_ok = true
|
||||
|
||||
[connection signal="confirmed" from="." to="." method="_on_Options_confirmed"]
|
||||
|
||||
[connection signal="item_selected" from="Panel/GridContainer/SerializerOption" to="." method="_on_SerializerOption_item_selected"]
|
||||
|
||||
[connection signal="button_down" from="Panel/GridContainer/EncryptCheckBox" to="." method="_on_EncryptCheckBox_button_down"]
|
||||
|
||||
[connection signal="button_down" from="Panel/GridContainer/ClassDirectoryHBox/ClassDirectoryButton" to="." method="_on_ClassDirectoryButton_button_down"]
|
||||
|
||||
[connection signal="button_down" from="Panel/GridContainer/OutputDirectoryHBox/OutputDirectoryButton" to="." method="_on_OutputDirectoryButton_button_down"]
|
||||
|
||||
[connection signal="button_down" from="Panel/GridContainer/SanitizeIdsCheckBox" to="." method="_on_SanitizeIdsCheckBox_button_down"]
|
||||
|
||||
|
17
plugin.cfg
Normal file
@ -0,0 +1,17 @@
|
||||
[custom]
|
||||
|
||||
class_directory="res://classes"
|
||||
encrypt=false
|
||||
extension="json"
|
||||
output_directory="res://data"
|
||||
password=""
|
||||
sanitize_ids=true
|
||||
serializer="json"
|
||||
|
||||
[plugin]
|
||||
|
||||
author="Matthias Stoeckli"
|
||||
description="An editor to write and use a data from Godot"
|
||||
name="DataEditor"
|
||||
script="data_editor.gd"
|
||||
version="0.1"
|
479
property_item.gd
Normal file
@ -0,0 +1,479 @@
|
||||
tool
|
||||
extends Panel
|
||||
# This class represents an item control used to change the data values, e.g. the text boxes.
|
||||
|
||||
|
||||
var load_icon = preload("icons/icon_load.png")
|
||||
var multi_line_icon = preload("icons/icon_multi_line.png")
|
||||
|
||||
var property_name = ""
|
||||
var type = TYPE_NIL
|
||||
var hint = 0
|
||||
var hint_array = []
|
||||
var hint_text = ""
|
||||
var number_of_hints = 0
|
||||
var value = null # The value of the property
|
||||
|
||||
var has_delete_button = false
|
||||
|
||||
|
||||
var control = null
|
||||
var popup = null
|
||||
var menu = null
|
||||
var value_editor = []
|
||||
|
||||
|
||||
var dialog = null
|
||||
var object_type_line_edit = null
|
||||
|
||||
|
||||
signal on_property_value_changed(property, value)
|
||||
signal property_item_load_button_down(property_item)
|
||||
signal custom_property_delete_requested(property_name)
|
||||
|
||||
# has_delete is used for custom properties
|
||||
func initialize(property_name, type, value = null, hint = 0, hint_text = "", has_delete = false):
|
||||
self.property_name = property_name
|
||||
self.type = type
|
||||
self.value = value
|
||||
self.hint = hint
|
||||
self.hint_text = hint_text
|
||||
self.has_delete_button = has_delete
|
||||
|
||||
|
||||
|
||||
func _ready():
|
||||
# Label describing property
|
||||
var property_label = Label.new()
|
||||
property_label.set_text(property_name.capitalize())
|
||||
|
||||
# Split property hints
|
||||
self.hint_array = hint_text.split(",")
|
||||
self.number_of_hints = hint_array.size()
|
||||
|
||||
##################################################
|
||||
# For each type, one control is defined
|
||||
##################################################
|
||||
if type == TYPE_BOOL:
|
||||
create_bool()
|
||||
elif type == TYPE_INT or type == TYPE_REAL:
|
||||
create_number()
|
||||
elif type == TYPE_STRING:
|
||||
create_string()
|
||||
elif type == TYPE_COLOR:
|
||||
create_color()
|
||||
elif type == TYPE_NODE_PATH:
|
||||
create_node_path()
|
||||
elif type == TYPE_VECTOR2:
|
||||
control = create_custom_editor_button(value);
|
||||
create_custom_editor(2, 2, 10, ["x", "y"])
|
||||
elif type == TYPE_VECTOR3:
|
||||
control = create_custom_editor_button(value);
|
||||
create_custom_editor(3, 3, 10, ["x", "y", "z"])
|
||||
elif type == TYPE_RECT2:
|
||||
control = create_custom_editor_button(value);
|
||||
create_custom_editor(4, 4, 10, ["x", "y", "w", "h"])
|
||||
elif type == TYPE_PLANE:
|
||||
control = create_custom_editor_button(value);
|
||||
create_custom_editor(4, 4, 10, ["x", "y", "z", "d"])
|
||||
elif type == TYPE_QUAT:
|
||||
control = create_custom_editor_button(value);
|
||||
create_custom_editor(4, 4, 10, ["x", "y", "z", "w"])
|
||||
elif type == TYPE_TRANSFORM:
|
||||
control = create_custom_editor_button(value);
|
||||
create_custom_editor(12, 4, 16, ["xx", "xy", "xz", "xo", "yx", "yy", "yz", "yo", "zx", "zy", "zz", "zo"])
|
||||
elif type == TYPE_OBJECT or type == TYPE_IMAGE:
|
||||
create_object_or_image()
|
||||
else:
|
||||
control = get_not_yet_supported()
|
||||
|
||||
control.set_h_size_flags(Control.SIZE_EXPAND_FILL)
|
||||
control.set_margin(MARGIN_LEFT, 200)
|
||||
control.set_custom_minimum_size(Vector2(get_parent().get_parent().get_parent().get_size().x - 270, 0))
|
||||
add_child(property_label)
|
||||
add_child(control)
|
||||
|
||||
if has_delete_button:
|
||||
var delete_button = ToolButton.new()
|
||||
delete_button.set_button_icon(preload("res://addons/DataEditor/icons/icon_remove.png"))
|
||||
delete_button.set_h_size_flags(Control.SIZE_EXPAND)
|
||||
delete_button.set_margin(MARGIN_LEFT, get_parent().get_parent().get_parent().get_size().x - 34 )
|
||||
delete_button.set_custom_minimum_size(Vector2(28, 24))
|
||||
delete_button.connect("button_down", self, "route_delete_request")
|
||||
# delete_button.connect("button_down", self, "emit_signal", ["custom_property_delete_requested")
|
||||
add_child(delete_button)
|
||||
|
||||
func route_delete_request():
|
||||
emit_signal("custom_property_delete_requested")
|
||||
|
||||
func create_custom_editor_button(value):
|
||||
var button = Button.new()
|
||||
button.set_text(str(value))
|
||||
button.connect("button_down", self, "open_custom_editor")
|
||||
return button
|
||||
|
||||
func open_custom_editor():
|
||||
menu.set_pos(get_global_mouse_pos())
|
||||
menu.get_children()[1].grab_focus()
|
||||
menu.popup()
|
||||
|
||||
|
||||
##################################################
|
||||
# All types
|
||||
##################################################
|
||||
|
||||
func create_bool():
|
||||
control = CheckBox.new()
|
||||
control.set_text(str(value))
|
||||
control.set_pressed(value)
|
||||
control.connect("toggled", self, "set_checkbox_label", [])
|
||||
control.connect("toggled", self, "property_value_changed", [])
|
||||
|
||||
func create_number():
|
||||
if hint == PROPERTY_HINT_RANGE:
|
||||
var control_min = -16777216
|
||||
var control_max = 16777216
|
||||
|
||||
var control_step = 0
|
||||
if type == TYPE_INT:
|
||||
control_step = 1
|
||||
else:
|
||||
control_step = 0.00001
|
||||
|
||||
if number_of_hints >= 1:
|
||||
if not hint_array[0].empty():
|
||||
control_min = int(hint_array[0])
|
||||
|
||||
if number_of_hints >= 2:
|
||||
if not hint_array[1].empty():
|
||||
control_max = int(hint_array[1])
|
||||
|
||||
if number_of_hints >= 3:
|
||||
if not hint_array[2].empty():
|
||||
control_step = float(hint_array[2])
|
||||
|
||||
# TODO: This does not seem to be exposed in GDScript yet?
|
||||
if number_of_hints >= 4 and hint_array[3] == "slider":
|
||||
control = HSlider.new()
|
||||
control.set_min(control_min)
|
||||
control.set_max(control_max)
|
||||
control.set_step(control_step)
|
||||
control.set_value(value);
|
||||
control.connect("value_changed", self, "property_value_changed", [])
|
||||
#controlset_size(Size2(110,30)*EDSCALE);
|
||||
else:
|
||||
control = SpinBox.new()
|
||||
control.set_min(control_min)
|
||||
control.set_max(control_max)
|
||||
control.set_step(control_step)
|
||||
control.set_value(value)
|
||||
control.connect("value_changed", self, "property_value_changed", [])
|
||||
#set_size(Size2(70,35)*EDSCALE);
|
||||
elif hint == PROPERTY_HINT_ENUM:
|
||||
control = MenuButton.new()
|
||||
for i in range(0, hint_array.size()):
|
||||
control.get_popup().add_item(hint_array[i])
|
||||
control.set_flat(false)
|
||||
# control.set_pos(get_pos())
|
||||
control.set_text(control.get_popup().get_item_text(value))
|
||||
control.get_popup().connect("item_pressed", self, "int_enum_property_value_changed", [])
|
||||
|
||||
elif hint == PROPERTY_HINT_EXP_EASING:
|
||||
control = get_not_yet_supported()
|
||||
elif hint == PROPERTY_HINT_FLAGS:
|
||||
control = get_not_yet_supported()
|
||||
else:
|
||||
control = SpinBox.new()
|
||||
control.set_value(value);
|
||||
if type == TYPE_REAL:
|
||||
control.set_min(-16777216)
|
||||
control.set_max(16777216)
|
||||
control.set_step(0.00001)
|
||||
else:
|
||||
control.set_max(2147483647)
|
||||
control.set_min(-2147483647)
|
||||
control.set_step(1)
|
||||
control.connect("value_changed", self, "property_value_changed", [])
|
||||
#control = create_custom_editor_button(value);
|
||||
#create_custom_editor(1, 1, 50, ["value"])
|
||||
#custom_editor_value_applied()
|
||||
|
||||
func create_string():
|
||||
if hint == PROPERTY_HINT_ENUM:
|
||||
control = MenuButton.new()
|
||||
for i in range(0, hint_array.size()):
|
||||
control.get_popup().add_item(hint_array[i])
|
||||
control.set_flat(false)
|
||||
control.set_text(value)
|
||||
control.get_popup().connect("item_pressed", self, "string_enum_property_value_changed", [])
|
||||
elif hint == PROPERTY_HINT_MULTILINE_TEXT:
|
||||
# RABRABRAB
|
||||
control = HBoxContainer.new()
|
||||
var line_edit = LineEdit.new()
|
||||
line_edit.set_h_size_flags(SIZE_EXPAND_FILL)
|
||||
var more_button = ToolButton.new()
|
||||
more_button.set_button_icon(multi_line_icon)
|
||||
line_edit.set_text(str(value))
|
||||
control.add_child(line_edit)
|
||||
control.add_child(more_button)
|
||||
|
||||
popup = Popup.new()
|
||||
popup.set_size(Vector2(600, 400))
|
||||
var text_edit = TextEdit.new()
|
||||
text_edit.set_anchor_and_margin(MARGIN_LEFT, ANCHOR_BEGIN, 0)
|
||||
text_edit.set_anchor_and_margin(MARGIN_TOP, ANCHOR_BEGIN, 0)
|
||||
text_edit.set_anchor_and_margin(MARGIN_RIGHT, ANCHOR_END, 0)
|
||||
text_edit.set_anchor_and_margin(MARGIN_BOTTOM, ANCHOR_END, 0)
|
||||
add_child(popup)
|
||||
popup.add_child(text_edit)
|
||||
text_edit.set_text(str(value))
|
||||
|
||||
line_edit.connect("text_changed", self, "property_value_changed", [])
|
||||
more_button.connect("button_down", popup, "popup_centered_minsize", [Vector2(800, 600)])
|
||||
popup.connect("popup_hide", self, "text_edit_popup_closed", [])
|
||||
else:
|
||||
control = LineEdit.new()
|
||||
control.set_text(str(value))
|
||||
control.connect("text_changed", self, "property_value_changed", [])
|
||||
|
||||
func create_color():
|
||||
control = ColorPickerButton.new()
|
||||
# If, for some reason, the color is still (de)serialized wrongly, split the string
|
||||
if typeof(value) == TYPE_STRING and value.find(","):
|
||||
var split_color = value.split(",")
|
||||
value = Color(split_color[0], split_color[1], split_color[2], split_color[3])
|
||||
control.set_color(value)
|
||||
control.connect("color_changed", self, "property_value_changed", [])
|
||||
|
||||
func create_node_path():
|
||||
control = LineEdit.new()
|
||||
control.set_text(value)
|
||||
control.connect("text_changed", self, "property_value_changed", [])
|
||||
|
||||
|
||||
# Adapted Port of property editor
|
||||
func create_custom_editor(amount, columns, label_w, strings, read_only = false):
|
||||
self.value_editor = []
|
||||
self.menu = PopupMenu.new()
|
||||
menu.connect("popup_hide", self, "custom_editor_value_applied")
|
||||
var w = 80
|
||||
var h = 20
|
||||
var m = 10
|
||||
var MAX_VALUE_EDITORS = 12
|
||||
var value_label = []
|
||||
for i in range(0, amount):
|
||||
var line_edit = LineEdit.new()
|
||||
line_edit.set_text(str(get_custom_editor_value(i)))
|
||||
value_editor.append(line_edit)
|
||||
menu.add_child(value_editor[i])
|
||||
value_label.append(Label.new())
|
||||
menu.add_child(value_label[i])
|
||||
pass
|
||||
|
||||
var rows=((amount-1)/columns)+1
|
||||
menu.set_size(Vector2( m*(1+columns)+(w+label_w)*columns, m*(1+rows)+h*rows ) );
|
||||
for i in range(0, amount):
|
||||
var c = i % columns;
|
||||
var r = i / columns;
|
||||
value_editor[i].show()
|
||||
value_label[i].show()
|
||||
if i < strings.size():
|
||||
value_label[i].set_text(strings[i])
|
||||
else:
|
||||
value_label[i].set_text("")
|
||||
|
||||
value_editor[i].set_pos( Vector2( m+label_w+c*(w+m+label_w), m+r*(h+m) ))
|
||||
value_editor[i].set_size( Vector2( w, h ) )
|
||||
value_label[i].set_pos( Vector2( m+c*(w+m+label_w), m+r*(h+m) ) )
|
||||
value_editor[i].set_editable(!read_only)
|
||||
|
||||
pass
|
||||
|
||||
add_child(menu)
|
||||
|
||||
|
||||
func custom_editor_value_applied():
|
||||
# TODO: Validate
|
||||
var va = []
|
||||
for line in value_editor:
|
||||
var v = float(line.get_text())
|
||||
va.append(v)
|
||||
|
||||
var value = null
|
||||
if type == TYPE_VECTOR2:
|
||||
value = Vector2(va[0], va[1])
|
||||
if type == TYPE_VECTOR3:
|
||||
value = Vector3(va[0], va[1], va[2])
|
||||
if type == TYPE_RECT2:
|
||||
value = Rect2(va[0], va[1], va[2], va[3])
|
||||
if type == TYPE_PLANE:
|
||||
value = Plane(va[0], va[1], va[2], va[3])
|
||||
if type == TYPE_QUAT:
|
||||
value = Quat(va[0], va[1], va[2], va[3])
|
||||
if type == TYPE_TRANSFORM:
|
||||
value = Transform(Vector3(va[0], va[1], va[2]), Vector3(va[4], va[5], va[6]), Vector3(va[8], va[9], va[10]), Vector3(va[3], va[7], va[11]))
|
||||
|
||||
if value != self.value:
|
||||
self.value = value
|
||||
emit_signal("on_property_value_changed", property_name, value)
|
||||
control.set_text(str(value))
|
||||
|
||||
func get_custom_editor_value(index):
|
||||
if type == TYPE_VECTOR2:
|
||||
if index == 0: return value.x
|
||||
else: return value.y
|
||||
elif type == TYPE_VECTOR3:
|
||||
if index == 0: return value.x
|
||||
elif index == 1: return value.y
|
||||
else: return value.z
|
||||
elif type == TYPE_RECT2:
|
||||
if index == 0: return value.pos.x
|
||||
elif index == 1: return value.pos.y
|
||||
elif index == 2: return value.size.x
|
||||
else: return value.size.y
|
||||
elif type == TYPE_QUAT:
|
||||
if index == 0: return value.x
|
||||
elif index == 1: return value.y
|
||||
elif index == 2: return value.z
|
||||
else: return value.w
|
||||
elif type == TYPE_PLANE:
|
||||
if index == 0: return value.x
|
||||
elif index == 1: return value.y
|
||||
elif index == 2: return value.z
|
||||
else: return value.d
|
||||
elif type == TYPE_TRANSFORM:
|
||||
if index == 0: return value.basis.x.x
|
||||
elif index == 1: return value.basis.x.y
|
||||
elif index == 2: return value.basis.x.z
|
||||
elif index == 3: return value.origin.x
|
||||
elif index == 4: return value.basis.y.x
|
||||
elif index == 5: return value.basis.y.y
|
||||
elif index == 6: return value.basis.y.z
|
||||
elif index == 7: return value.origin.y
|
||||
elif index == 8: return value.basis.z.x
|
||||
elif index == 9: return value.basis.z.y
|
||||
elif index == 10: return value.basis.z.z
|
||||
else: return value.origin.z
|
||||
|
||||
func create_object_or_image():
|
||||
control = HBoxContainer.new()
|
||||
object_type_line_edit = LineEdit.new()
|
||||
object_type_line_edit.set_text(str(value))
|
||||
object_type_line_edit.set_h_size_flags(SIZE_EXPAND_FILL)
|
||||
object_type_line_edit.connect("text_changed", self, "property_value_changed", [])
|
||||
if hint_text == "Texture" or type == TYPE_IMAGE:
|
||||
var f = File.new()
|
||||
if value != null and f.file_exists(value):
|
||||
var texture = load(value)
|
||||
var texture_frame = TextureFrame.new()
|
||||
texture_frame.set_expand(true)
|
||||
texture_frame.set_custom_minimum_size(Vector2(get_parent_area_size().y, get_parent_area_size().y))
|
||||
texture_frame.set_texture(texture)
|
||||
var texture_popup = Popup.new()
|
||||
var texture_frame_full = TextureFrame.new()
|
||||
texture_frame_full.set_texture(texture)
|
||||
texture_popup.add_child(texture_frame_full)
|
||||
texture_popup.set_size(texture.get_size())
|
||||
# texture_frame.set_process_input(true)
|
||||
# texture_frame.connect("input_event", self, "open_image", [])
|
||||
control.add_child(texture_frame)
|
||||
# control.add_child(texture_popup)
|
||||
|
||||
control.add_child(object_type_line_edit)
|
||||
|
||||
|
||||
var load_button = ToolButton.new()
|
||||
load_button.set_button_icon(load_icon)
|
||||
control.add_child(load_button)
|
||||
|
||||
|
||||
if Globals.get("debug_is_editor"):
|
||||
dialog = EditorFileDialog.new()
|
||||
dialog.set_access(EditorFileDialog.ACCESS_RESOURCES)
|
||||
dialog.set_mode(EditorFileDialog.MODE_OPEN_FILE)
|
||||
load_button.connect("button_down", dialog, "popup_centered_ratio")
|
||||
|
||||
var filter = ""
|
||||
var resource_type = ""
|
||||
var extension_array = []
|
||||
|
||||
if hint == PROPERTY_HINT_RESOURCE_TYPE:
|
||||
resource_type = hint_text
|
||||
extension_array = ResourceLoader.get_recognized_extensions_for_type(resource_type)
|
||||
else:
|
||||
extension_array = hint_array
|
||||
|
||||
for extension in extension_array:
|
||||
# if filter.begins_with("."):
|
||||
# filter = "*" + extension
|
||||
# elif filter.begins_with("*"):
|
||||
# filter = "*." + extension
|
||||
# filter = filter + " ; " + extension.to_upper()
|
||||
extension.replace("*", "").replace(".", "")
|
||||
filter = "*." + extension + " ; " + extension.to_upper()
|
||||
dialog.add_filter(filter)
|
||||
pass
|
||||
dialog.connect("file_selected", self, "fill_resource_name", [])
|
||||
add_child(dialog)
|
||||
|
||||
|
||||
#.add_icon_item(get_icon("Load","EditorIcons"), "Load")
|
||||
|
||||
|
||||
|
||||
func get_not_yet_supported():
|
||||
var control = Label.new()
|
||||
control.set_text(str("This type is not yet supported."))
|
||||
return control
|
||||
|
||||
# TODO: That's all a bit too confusing...
|
||||
func fill_resource_name(resource_path):
|
||||
object_type_line_edit.set_text(resource_path)
|
||||
property_value_changed(resource_path)
|
||||
if hint_text == "Texture":
|
||||
var texture = load(resource_path)
|
||||
if texture:
|
||||
control.get_child(0).set_texture(texture)
|
||||
|
||||
# Sets the label of the checkboxe's text to the value
|
||||
func set_checkbox_label(value):
|
||||
control.set_text(str(value))
|
||||
|
||||
func property_value_changed(value):
|
||||
if type == TYPE_INT:
|
||||
value = int(value)
|
||||
if type == TYPE_REAL:
|
||||
value = float(value)
|
||||
if type == TYPE_COLOR:
|
||||
value = Color(value)
|
||||
if self.value != value:
|
||||
self.value = value
|
||||
emit_signal("on_property_value_changed", property_name, value)
|
||||
|
||||
|
||||
func text_edit_popup_closed():
|
||||
var text_edit = popup.get_child(0)
|
||||
var value = text_edit.get_text()
|
||||
if value and self.value != value:
|
||||
self.value = value
|
||||
emit_signal("on_property_value_changed", property_name, value)
|
||||
control.get_child(0).set_text(value)
|
||||
|
||||
|
||||
|
||||
func int_enum_property_value_changed(value):
|
||||
control.set_text(control.get_popup().get_item_text(value))
|
||||
if self.value != value:
|
||||
self.value = value
|
||||
emit_signal("on_property_value_changed", property_name, value)
|
||||
|
||||
# Simply changes the text of the calling menu to the selected value if it's an enum
|
||||
func string_enum_property_value_changed(value):
|
||||
control.set_text(control.get_popup().get_item_text(value))
|
||||
if str(self.value) != str(value):
|
||||
self.value = value
|
||||
emit_signal("on_property_value_changed", property_name, control.get_popup().get_item_text(value))
|
||||
|
||||
|
||||
func open_image(texture):
|
||||
pass
|
55
property_item.tscn
Normal file
@ -0,0 +1,55 @@
|
||||
[gd_scene load_steps=3 format=1]
|
||||
|
||||
[ext_resource path="res://addons/DataEditor/property_item.gd" type="Script" id=1]
|
||||
[ext_resource path="res://addons/DataEditor/icons/icon_remove.png" type="Texture" id=2]
|
||||
|
||||
[node name="Panel" type="Panel"]
|
||||
|
||||
anchor/right = 1
|
||||
anchor/bottom = 1
|
||||
rect/min_size = Vector2( 0, 26 )
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = false
|
||||
size_flags/horizontal = 3
|
||||
size_flags/vertical = 2
|
||||
margin/left = 1.0
|
||||
margin/top = 0.0
|
||||
margin/right = -1.0
|
||||
margin/bottom = 574.0
|
||||
script/script = ExtResource( 1 )
|
||||
|
||||
[node name="DeleteButton" type="ToolButton" parent="."]
|
||||
|
||||
visibility/visible = false
|
||||
anchor/left = 1
|
||||
anchor/top = 3
|
||||
anchor/right = 1
|
||||
anchor/bottom = 3
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 2
|
||||
margin/left = 1280.0
|
||||
margin/top = 13.0
|
||||
margin/right = 1252.0
|
||||
margin/bottom = -11.0
|
||||
toggle_mode = false
|
||||
enabled_focus_mode = 2
|
||||
shortcut = null
|
||||
icon = ExtResource( 2 )
|
||||
flat = true
|
||||
|
||||
[node name="PopupMenu" type="PopupMenu" parent="."]
|
||||
|
||||
focus/ignore_mouse = false
|
||||
focus/stop_mouse = true
|
||||
size_flags/horizontal = 2
|
||||
size_flags/vertical = 2
|
||||
margin/left = 0.0
|
||||
margin/top = 0.0
|
||||
margin/right = 20.0
|
||||
margin/bottom = 20.0
|
||||
popup/exclusive = false
|
||||
items = [ ]
|
||||
|
||||
|
15
style/light_gray_panel.tres
Normal file
@ -0,0 +1,15 @@
|
||||
[gd_resource type="StyleBoxFlat" format=1]
|
||||
|
||||
[resource]
|
||||
|
||||
content_margin/left = -1.0
|
||||
content_margin/right = -1.0
|
||||
content_margin/top = -1.0
|
||||
content_margin/bottom = -1.0
|
||||
bg_color = Color( 0.172549, 0.164706, 0.196078, 1 )
|
||||
light_color = Color( 0.8, 0.8, 0.8, 1 )
|
||||
dark_color = Color( 0.8, 0.8, 0.8, 1 )
|
||||
border_size = 0
|
||||
border_blend = true
|
||||
draw_bg = true
|
||||
|