mirror of
https://github.com/Relintai/godot_data_editor.git
synced 2025-02-23 09:54:19 +01:00
Updated data manager and fixed some bugs
This commit is contained in:
parent
c9434c9992
commit
fc77bab650
116
README.md
Normal file
116
README.md
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
# Godot Data Editor
|
||||||
|
This repository hosts a plugin for the [Godot Engine]. It allows users to enter data items based on Godot classes which are then serialized in either as json or binaries. Serialized items may be encrypted if desired.
|
||||||
|
|
||||||
|
# Features
|
||||||
|
* Support for binary or json serialization
|
||||||
|
* Create data classes with the click of a button
|
||||||
|
* Use gdscript to add properties and logic to your data classes, all instances will be Nodes
|
||||||
|
* Make use of the built-in [export property hints]
|
||||||
|
* Add instance-specific custom properties
|
||||||
|
* Access data with a simple API using the _data_ singleton
|
||||||
|
* ALPHA: Use the notification/observer system to react to changes in data items
|
||||||
|
* ALPHA: Encrypt your data files
|
||||||
|
|
||||||
|
# Screenshots
|
||||||
|
![editor_screenshot]
|
||||||
|
![class_screenshot]
|
||||||
|
|
||||||
|
# Installation
|
||||||
|
* Open your project folder, e.g. "../MyGame/"
|
||||||
|
* Create a folder named "addons" (if not already present)
|
||||||
|
* In addons, create a folder named "DataEditor"
|
||||||
|
* Copy the content of this repository into it. You may remove the "sceenshots" ;)
|
||||||
|
* Open your project in the editor and navigate to the plugin (Scene -> Project Settings -> Plugins)
|
||||||
|
* The plugin "DataEditor" should now appear, change the status from "Inactive" to "Active"
|
||||||
|
* Restart the editor to make sure that the _data_ singleton is loaded properly
|
||||||
|
|
||||||
|
I intend to upload the plugin to the AssetLib, once I feel it is stable enough.
|
||||||
|
|
||||||
|
# System Requirements
|
||||||
|
The plugin was written for version *2.1.3* of the Godot Engine. Upcoming minor versions should be supported as well.
|
||||||
|
It is very likely that a number of changes will be necessary, once Godot 3 is released.
|
||||||
|
|
||||||
|
# API / Tutorial
|
||||||
|
I created a little video which shows how to use the plugin to create a simple shop system: [[Link to video which does not exist yet :) ]]
|
||||||
|
|
||||||
|
Working with data is rather simple, use the provided _data_ class to access the items. The following code snippets demonstrates item retrieval as well as the observation feature:
|
||||||
|
```gdscript
|
||||||
|
extends Node
|
||||||
|
|
||||||
|
func _ready():
|
||||||
|
# Get a single item
|
||||||
|
var herb = data.get_item("shop_item", "herb")
|
||||||
|
var price = herb.price
|
||||||
|
|
||||||
|
# Get all items as dictionary (key: id, value: item)
|
||||||
|
var shop_items = data.get_items("shop_item")
|
||||||
|
for shop_item in shop_items.values():
|
||||||
|
print(shop_item.price)
|
||||||
|
pass
|
||||||
|
|
||||||
|
#######################################
|
||||||
|
# Observe Properties:
|
||||||
|
# Please note that you currently have to update properties using the "update_property" to make use of this feature
|
||||||
|
#######################################
|
||||||
|
|
||||||
|
# Be notified when something about this herb changes
|
||||||
|
data.observe_item(self, herb, "herb_changed")
|
||||||
|
herb.update_property("name", "better_herb")
|
||||||
|
|
||||||
|
# Be notified when the price of this herb changes,
|
||||||
|
data.observe_item_property(self, herb, "price", "herb_price_changed")
|
||||||
|
herb.update_property("price", 500)
|
||||||
|
|
||||||
|
# Be notified when any item changes
|
||||||
|
var doge_axe = data.get_item("shop_item", "doge_axe")
|
||||||
|
data.observe_class(self, "shop_item", "shop_item_changed")
|
||||||
|
doge_axe.update_property("price", 500)
|
||||||
|
|
||||||
|
# Overkill: be notified about everything
|
||||||
|
data.observe_all_changes(self, "something_changed")
|
||||||
|
|
||||||
|
#######################################
|
||||||
|
# Stop Observing:
|
||||||
|
# When you are no longer interested in updates, simply unsubscribe/stop observing
|
||||||
|
#######################################
|
||||||
|
data.stop_observing_item_property(self, herb, "price")
|
||||||
|
data.stop_observing_item(self, herb)
|
||||||
|
data.stop_observing_class(self, "shop_item")
|
||||||
|
data.stop_observing_changes(self)
|
||||||
|
|
||||||
|
|
||||||
|
func herb_changed(item, property, value):
|
||||||
|
print("Something about this herb changed!")
|
||||||
|
|
||||||
|
func herb_price_changed(item, property, value):
|
||||||
|
print("Herb price changed!")
|
||||||
|
|
||||||
|
func shop_item_changed(item, property, value):
|
||||||
|
print(item.name + " changed! " + property + " : " + str(value))
|
||||||
|
|
||||||
|
func something_changed(item, property, value):
|
||||||
|
print("I guess something changed.")
|
||||||
|
```
|
||||||
|
|
||||||
|
# Please Contribute!
|
||||||
|
Please feel free to contribute. Unfortunately, the code base still is not documented that well and there are a number of bugs which will need to be ironed out. I am sure that there are a number of things I have been doing wrong, e.g. performance/memory management issues.
|
||||||
|
|
||||||
|
# Known Issues
|
||||||
|
* The "Rename Class" feature may not properly rename the class file if it still in use
|
||||||
|
* There are a number of reference issues
|
||||||
|
* In some cases, the controls are not properly resized - pressing "Reload" should usually do the trick though
|
||||||
|
* A number of operations will perform a complete refresh of all data, which causes unsaved changes to disappear. This was done to prevent inconsistencies
|
||||||
|
* There is no support for undo/redo
|
||||||
|
* Pressing Ctrl+S to save while in the data editor may temporarily hide unsaved items
|
||||||
|
* The _data_ singleton is only visible in the editor when the project is being restarted. This seems to be a limitation of the engine which does not allow reload the engine.cfg file
|
||||||
|
* The "class overview" screen is lacking any kind of useful content
|
||||||
|
|
||||||
|
# HALP! Something went wrong!
|
||||||
|
Stay calm, most issues can be resolved by either pressing the "Reload" button or activating and deactivating the plugin. If the problem persists, there is likely an issue with your data. Check if the name of the class (which are stored in the "classes" folder by default) is the same as the folder name of your instances (by default called "data"). If this is the case, there might be a conflict with duplicate IDs or the like. Please post an issue here if this happened without any external influence (e.g. you edited the files manually in another editor).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[Godot Engine]: <https://github.com/godotengine/godot>
|
||||||
|
[export property hints]: <http://docs.godotengine.org/en/latest/learning/scripting/gdscript/gdscript_basics.html#exports>
|
||||||
|
[editor_screenshot]: https://github.com/Stoeoeoe/screenshots/editor.png "The Godot Data Editor"
|
||||||
|
[class_screenshot]: https://github.com/Stoeoeoe/screenshots/class.png "Example Class"
|
@ -46,6 +46,7 @@ alignment = 0
|
|||||||
|
|
||||||
[node name="ClassPropertiesLabel" type="Label" parent="Panel/Body"]
|
[node name="ClassPropertiesLabel" type="Label" parent="Panel/Body"]
|
||||||
|
|
||||||
|
visibility/visible = false
|
||||||
focus/ignore_mouse = true
|
focus/ignore_mouse = true
|
||||||
focus/stop_mouse = true
|
focus/stop_mouse = true
|
||||||
size_flags/horizontal = 2
|
size_flags/horizontal = 2
|
||||||
@ -53,7 +54,7 @@ size_flags/vertical = 0
|
|||||||
margin/left = 0.0
|
margin/left = 0.0
|
||||||
margin/top = 0.0
|
margin/top = 0.0
|
||||||
margin/right = 1246.0
|
margin/right = 1246.0
|
||||||
margin/bottom = 2.0
|
margin/bottom = 19.0
|
||||||
custom_fonts/font = ExtResource( 3 )
|
custom_fonts/font = ExtResource( 3 )
|
||||||
text = "Static Class Properties"
|
text = "Static Class Properties"
|
||||||
uppercase = true
|
uppercase = true
|
||||||
@ -63,14 +64,15 @@ max_lines_visible = -1
|
|||||||
|
|
||||||
[node name="HSeparator" type="HSeparator" parent="Panel/Body"]
|
[node name="HSeparator" type="HSeparator" parent="Panel/Body"]
|
||||||
|
|
||||||
|
visibility/visible = false
|
||||||
focus/ignore_mouse = false
|
focus/ignore_mouse = false
|
||||||
focus/stop_mouse = true
|
focus/stop_mouse = true
|
||||||
size_flags/horizontal = 2
|
size_flags/horizontal = 2
|
||||||
size_flags/vertical = 2
|
size_flags/vertical = 2
|
||||||
margin/left = 0.0
|
margin/left = 0.0
|
||||||
margin/top = 6.0
|
margin/top = 0.0
|
||||||
margin/right = 1246.0
|
margin/right = 1246.0
|
||||||
margin/bottom = 9.0
|
margin/bottom = 3.0
|
||||||
|
|
||||||
[node name="Control" type="Control" parent="Panel/Body"]
|
[node name="Control" type="Control" parent="Panel/Body"]
|
||||||
|
|
||||||
@ -80,18 +82,19 @@ focus/stop_mouse = true
|
|||||||
size_flags/horizontal = 2
|
size_flags/horizontal = 2
|
||||||
size_flags/vertical = 2
|
size_flags/vertical = 2
|
||||||
margin/left = 0.0
|
margin/left = 0.0
|
||||||
margin/top = 13.0
|
margin/top = 0.0
|
||||||
margin/right = 1246.0
|
margin/right = 1246.0
|
||||||
margin/bottom = 28.0
|
margin/bottom = 15.0
|
||||||
|
|
||||||
[node name="Scroll" type="ScrollContainer" parent="Panel/Body"]
|
[node name="Scroll" type="ScrollContainer" parent="Panel/Body"]
|
||||||
|
|
||||||
|
visibility/visible = false
|
||||||
focus/ignore_mouse = false
|
focus/ignore_mouse = false
|
||||||
focus/stop_mouse = true
|
focus/stop_mouse = true
|
||||||
size_flags/horizontal = 3
|
size_flags/horizontal = 3
|
||||||
size_flags/vertical = 3
|
size_flags/vertical = 3
|
||||||
margin/left = 0.0
|
margin/left = 0.0
|
||||||
margin/top = 32.0
|
margin/top = 19.0
|
||||||
margin/right = 1246.0
|
margin/right = 1246.0
|
||||||
margin/bottom = 566.0
|
margin/bottom = 566.0
|
||||||
scroll/horizontal = true
|
scroll/horizontal = true
|
||||||
@ -99,6 +102,7 @@ scroll/vertical = true
|
|||||||
|
|
||||||
[node name="Statics" type="VBoxContainer" parent="Panel/Body/Scroll"]
|
[node name="Statics" type="VBoxContainer" parent="Panel/Body/Scroll"]
|
||||||
|
|
||||||
|
visibility/visible = false
|
||||||
focus/ignore_mouse = false
|
focus/ignore_mouse = false
|
||||||
focus/stop_mouse = false
|
focus/stop_mouse = false
|
||||||
size_flags/horizontal = 3
|
size_flags/horizontal = 3
|
||||||
@ -106,11 +110,12 @@ size_flags/vertical = 2
|
|||||||
margin/left = 0.0
|
margin/left = 0.0
|
||||||
margin/top = 0.0
|
margin/top = 0.0
|
||||||
margin/right = 1246.0
|
margin/right = 1246.0
|
||||||
margin/bottom = 38.0
|
margin/bottom = 0.0
|
||||||
alignment = 0
|
alignment = 0
|
||||||
|
|
||||||
[node name="NoStaticsPropertiesLabel" type="Label" parent="Panel/Body/Scroll/Statics"]
|
[node name="NoStaticsPropertiesLabel" type="Label" parent="Panel/Body/Scroll/Statics"]
|
||||||
|
|
||||||
|
visibility/visible = false
|
||||||
focus/ignore_mouse = true
|
focus/ignore_mouse = true
|
||||||
focus/stop_mouse = true
|
focus/stop_mouse = true
|
||||||
size_flags/horizontal = 3
|
size_flags/horizontal = 3
|
||||||
@ -126,6 +131,7 @@ max_lines_visible = -1
|
|||||||
|
|
||||||
[node name="AddStaticPropertyButton" type="Button" parent="Panel/Body/Scroll/Statics"]
|
[node name="AddStaticPropertyButton" type="Button" parent="Panel/Body/Scroll/Statics"]
|
||||||
|
|
||||||
|
visibility/visible = false
|
||||||
focus/ignore_mouse = false
|
focus/ignore_mouse = false
|
||||||
focus/stop_mouse = true
|
focus/stop_mouse = true
|
||||||
size_flags/horizontal = 2
|
size_flags/horizontal = 2
|
||||||
|
92
data.gd
92
data.gd
@ -4,40 +4,24 @@ var item_manager = null
|
|||||||
var items = {}
|
var items = {}
|
||||||
var values = {}
|
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():
|
func _init():
|
||||||
|
# Caution: This item manager may not be in sync with the one used by the editor
|
||||||
self.item_manager = preload("item_manager.gd").new()
|
self.item_manager = preload("item_manager.gd").new()
|
||||||
self.items = item_manager.items
|
self.items = item_manager.items
|
||||||
|
|
||||||
# TODO: Allow eager loading
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
func get_item(item_class, id):
|
func get_item(item_class, id):
|
||||||
return item_manager.get_item(item_class, id)
|
return item_manager.get_item(item_class, id)
|
||||||
|
|
||||||
|
|
||||||
func get_items(item_class):
|
func get_items(item_class):
|
||||||
return item_manager.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):
|
func _load_item(item_class, id):
|
||||||
items[item_class][id] = item_manager.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):
|
func load_item_value(item, property):
|
||||||
return get_progress(item._class, item._id, property)
|
return get_progress(item._class, item._id, property)
|
||||||
@ -53,52 +37,96 @@ func set_progress(item_class, id, property, value):
|
|||||||
var has_value = item.get(property)
|
var has_value = item.get(property)
|
||||||
if item and has_value:
|
if item and has_value:
|
||||||
item.set(property, value)
|
item.set(property, value)
|
||||||
emit_signal("any_value_changed", item, property, value)
|
if has_user_signal("@any_value_changed"):
|
||||||
|
emit_signal("@any_value_changed", item, property, value)
|
||||||
|
|
||||||
var signal_name = ""
|
var signal_name = ""
|
||||||
signal_name = item_class
|
signal_name = "@" + item_class
|
||||||
# Class signal
|
# Class signal
|
||||||
if has_user_signal(signal_name):
|
if has_user_signal(signal_name):
|
||||||
emit_signal(signal_name, item, property, value)
|
emit_signal(signal_name, item, property, value)
|
||||||
|
|
||||||
# Item signal
|
# Item signal
|
||||||
signal_name = item_class + "|" + id
|
signal_name = "@" + item_class + "|" + id
|
||||||
if has_user_signal(signal_name):
|
if has_user_signal(signal_name):
|
||||||
emit_signal(signal_name, item, property, value)
|
emit_signal(signal_name, item, property, value)
|
||||||
|
|
||||||
# Property signal
|
# Property signal
|
||||||
signal_name = item_class + "|" + id + "|" + property
|
signal_name = "@" + item_class + "|" + id + "|" + property
|
||||||
if has_user_signal(signal_name):
|
if has_user_signal(signal_name):
|
||||||
emit_signal(signal_name, item, property, value)
|
emit_signal(signal_name, item, property, value)
|
||||||
|
|
||||||
return true
|
return true
|
||||||
else:
|
else:
|
||||||
return false
|
return false
|
||||||
|
|
||||||
func observe_all_changes(observer, method, binds=[], flags = 0):
|
func observe_all_changes(observer, method, binds=[], flags = 0):
|
||||||
self.connect("any_value_changed", observer, method, binds, flags)
|
var signal_name = "@any_value_changed"
|
||||||
|
self.add_user_signal(signal_name) # TODO: Args
|
||||||
|
self.connect(signal_name, observer, method, binds, flags)
|
||||||
|
|
||||||
func observe_class(observer, item_class, method, binds=[], flags = 0):
|
func observe_class(observer, item_class, method, binds=[], flags = 0):
|
||||||
self.add_user_signal(item_class) # TODO: Args
|
var signal_name = "@" + item_class
|
||||||
self.connect(item_class, observer, method, binds, flags)
|
self.add_user_signal(signal_name) # TODO: Args
|
||||||
|
self.connect(signal_name, observer, method, binds, flags)
|
||||||
|
|
||||||
func observe_item(observer, item, method, binds=[], flags = 0):
|
func observe_item(observer, item, method, binds=[], flags = 0):
|
||||||
var signal_name = item._class + "|" + item._id
|
var signal_name = "@" + item._class + "|" + item._id
|
||||||
if not has_user_signal(signal_name):
|
if not has_user_signal(signal_name):
|
||||||
self.add_user_signal(signal_name) # TODO: Args
|
self.add_user_signal(signal_name) # TODO: Args
|
||||||
self.connect(signal_name, observer, method, binds, flags)
|
self.connect(signal_name, observer, method, binds, flags)
|
||||||
|
|
||||||
|
|
||||||
func observe_item_property(observer, item, property, method, binds=[], flags = 0):
|
func observe_item_property(observer, item, property, method, binds=[], flags = 0):
|
||||||
var signal_name = item._class + "|" + item._id + "|" + property
|
var signal_name = "@" + item._class + "|" + item._id + "|" + property
|
||||||
if not has_user_signal(signal_name):
|
if not has_user_signal(signal_name):
|
||||||
self.add_user_signal(signal_name) # TODO: Args
|
self.add_user_signal(signal_name) # TODO: Args
|
||||||
self.connect(signal_name, observer, method, binds, flags)
|
self.connect(signal_name, observer, method, binds, flags)
|
||||||
|
|
||||||
func stop_observing_all_changes(observer):
|
func _get_relevant_connections():
|
||||||
pass
|
var relevant_connections = []
|
||||||
# observer.disconnect(
|
var signals = get_signal_list()
|
||||||
|
for s in signals:
|
||||||
|
var name = s["name"]
|
||||||
|
if name.begins_with("@"):
|
||||||
|
for c in get_signal_connection_list(name):
|
||||||
|
relevant_connections.append(c)
|
||||||
|
return relevant_connections
|
||||||
|
|
||||||
|
|
||||||
|
func stop_observing_class(observer, item_class):
|
||||||
|
var connection_list = _get_relevant_connections()
|
||||||
|
for connection in connection_list:
|
||||||
|
var target = connection["target"]
|
||||||
|
var signal_info = connection["signal"].replace("@", "").split("|")
|
||||||
|
if signal_info.size() == 1 and signal_info[0] == item_class and target == observer:
|
||||||
|
self.disconnect(connection["signal"], target, connection["method"])
|
||||||
|
|
||||||
|
func stop_observing_item(observer, item):
|
||||||
|
var connection_list = _get_relevant_connections()
|
||||||
|
for connection in connection_list:
|
||||||
|
var target = connection["target"]
|
||||||
|
var signal_info = connection["signal"].replace("@", "").split("|")
|
||||||
|
if signal_info.size() == 2 and signal_info[0] == item._class and signal_info[1] == item._id and target == observer:
|
||||||
|
self.disconnect(connection["signal"], target, connection["method"])
|
||||||
|
|
||||||
#TODO: func block_signals()
|
func stop_observing_item_property(observer, item, property):
|
||||||
|
var connection_list = _get_relevant_connections()
|
||||||
|
for connection in connection_list:
|
||||||
|
var target = connection["target"]
|
||||||
|
var signal_info = connection["signal"].replace("@", "").split("|")
|
||||||
|
if signal_info.size() == 3 and signal_info[0] == item._class and signal_info[1] == item._id and signal_info[2] == property and target == observer:
|
||||||
|
self.disconnect(connection["signal"], target, connection["method"])
|
||||||
|
|
||||||
|
func stop_observing_changes(observer):
|
||||||
|
var connection_list = _get_relevant_connections()
|
||||||
|
for connection in connection_list:
|
||||||
|
var target = connection["target"]
|
||||||
|
if target == observer:
|
||||||
|
self.disconnect(connection["signal"], target, connection["method"])
|
||||||
|
|
||||||
|
|
||||||
|
# observer.disconnect(
|
||||||
|
|
||||||
|
|
||||||
func set_item_progress(item, property, value):
|
func set_item_progress(item, property, value):
|
||||||
|
@ -43,19 +43,6 @@ onready var new_item_class_icon = get_node("NewClassDialog/ClassIconPath")
|
|||||||
onready var new_item_class_icon_dialog = get_node("NewClassDialog/ClassIconFileDialog")
|
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 warn_dialog = get_node("WarnDialog")
|
||||||
onready var options_screen = get_node("OptionsDialog")
|
onready var options_screen = get_node("OptionsDialog")
|
||||||
|
|
||||||
@ -68,21 +55,9 @@ signal input_dialog_confirmed(text1, text2)
|
|||||||
|
|
||||||
# First initialize the item manager which is used for loading, saving and configs
|
# First initialize the item manager which is used for loading, saving and configs
|
||||||
func _init():
|
func _init():
|
||||||
item_manager = preload("item_manager.gd").new()
|
item_manager = preload("item_manager.gd").new() # This item_manager will add itself to the globals
|
||||||
#item_manager.set_name("ItemManager")
|
|
||||||
#Globals.set("item_manager", item_manager)
|
|
||||||
# self.add_child(item_manager)
|
|
||||||
# item_manager.raise()
|
|
||||||
|
|
||||||
|
func _ready():
|
||||||
|
|
||||||
|
|
||||||
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)
|
Globals.set("debug_is_editor", false)
|
||||||
# Tree signals
|
# Tree signals
|
||||||
item_tree.connect("on_new_item_pressed", self, "handle_actions", ["add"])
|
item_tree.connect("on_new_item_pressed", self, "handle_actions", ["add"])
|
||||||
@ -136,11 +111,20 @@ func _ready():
|
|||||||
# Select the first item in the tree when loading the GUI
|
# Select the first item in the tree when loading the GUI
|
||||||
change_item_context(selected_item, selected_class)
|
change_item_context(selected_item, selected_class)
|
||||||
|
|
||||||
# TODO: Other OS...
|
# TODO: Implement
|
||||||
func open_item():
|
func open_item():
|
||||||
var item_path = item_manager.get_full_path(selected_item)
|
var item_path = item_manager.get_full_path(selected_item)
|
||||||
print(item_path)
|
var program = ""
|
||||||
OS.execute("explorer", [item_path], false)
|
var os_name = OS.get_name()
|
||||||
|
if os_name == "Windows":
|
||||||
|
program = "explorer"
|
||||||
|
item_path = item_path.replace("/", "\\") # ~_~...
|
||||||
|
# TODO: Not sure if these work... Probably add the possibility to add a custom editor
|
||||||
|
elif os_name == "OSX":
|
||||||
|
program = "open"
|
||||||
|
else:
|
||||||
|
program = "nautilus"
|
||||||
|
OS.execute(program, [item_path], false)
|
||||||
|
|
||||||
func change_item_context(selected_item, selected_class):
|
func change_item_context(selected_item, selected_class):
|
||||||
|
|
||||||
@ -170,13 +154,11 @@ func change_item_context(selected_item, selected_class):
|
|||||||
|
|
||||||
# An item was selected
|
# An item was selected
|
||||||
if selected_item:
|
if selected_item:
|
||||||
|
# Context was lost, e.g. because of changes to the classes. Reload.
|
||||||
# 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"):
|
if selected_item and not selected_item.get("_id"):
|
||||||
self.item_manager.load_manager()
|
self.item_manager.load_manager()
|
||||||
self.item_tree.load_tree(true)
|
self.item_tree.load_tree(true)
|
||||||
selected_item = item_tree.select_first_element()
|
selected_item = item_tree.select_first_element()
|
||||||
|
|
||||||
|
|
||||||
change_display_name_button.set_disabled(false)
|
change_display_name_button.set_disabled(false)
|
||||||
duplicate_button.set_disabled(false)
|
duplicate_button.set_disabled(false)
|
||||||
@ -214,7 +196,7 @@ func change_item_context(selected_item, selected_class):
|
|||||||
|
|
||||||
self.selected_item = null
|
self.selected_item = null
|
||||||
self.selected_id = null
|
self.selected_id = null
|
||||||
id_label.set_text(selected_class.capitalize() + " " + (selected_class))
|
id_label.set_text(selected_class.capitalize())
|
||||||
class_overview.show()
|
class_overview.show()
|
||||||
instance_details.hide()
|
instance_details.hide()
|
||||||
no_classes.hide()
|
no_classes.hide()
|
||||||
@ -232,10 +214,12 @@ func create_shortcut(keys):
|
|||||||
input_event.ID = keys
|
input_event.ID = keys
|
||||||
short_cut.set_shortcut(input_event)
|
short_cut.set_shortcut(input_event)
|
||||||
|
|
||||||
|
# TODO: Implement
|
||||||
func warn_about_reload():
|
func warn_about_reload():
|
||||||
if item_manager.has_unsaved_items():
|
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?"))
|
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():
|
func reload():
|
||||||
item_manager.load_manager()
|
item_manager.load_manager()
|
||||||
item_tree.load_tree(true)
|
item_tree.load_tree(true)
|
||||||
@ -243,20 +227,10 @@ func reload():
|
|||||||
item_tree.select_item(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):
|
func toggle_item_dirty_state(item):
|
||||||
item._dirty = true
|
item._dirty = true
|
||||||
item_tree.set_tree_item_label_text(item)
|
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
|
# Validation takes place in the item manager
|
||||||
func _on_NewCustomPropertyDialog_confirmed():
|
func _on_NewCustomPropertyDialog_confirmed():
|
||||||
@ -269,10 +243,8 @@ func _on_NewCustomPropertyDialog_confirmed():
|
|||||||
custom_properties.build_properties(selected_item)
|
custom_properties.build_properties(selected_item)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# TODO: Show confirmation dialog
|
# TODO: Show confirmation dialog
|
||||||
func delete_custom_property(property_name):
|
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)
|
item_manager.delete_custom_property(selected_item, property_name)
|
||||||
toggle_item_dirty_state(selected_item)
|
toggle_item_dirty_state(selected_item)
|
||||||
custom_properties.build_properties(selected_item)
|
custom_properties.build_properties(selected_item)
|
||||||
@ -301,10 +273,11 @@ func _on_NewClassDialog_confirmed():
|
|||||||
func _on_NewClassIconSearchButton_button_down():
|
func _on_NewClassIconSearchButton_button_down():
|
||||||
new_item_class_icon_dialog.popup_centered()
|
new_item_class_icon_dialog.popup_centered()
|
||||||
|
|
||||||
|
# Icon for new class was selected
|
||||||
func _on_NewClassIconFileDialog_file_selected(path):
|
func _on_NewClassIconFileDialog_file_selected(path):
|
||||||
new_item_class_icon.set_text(path)
|
new_item_class_icon.set_text(path)
|
||||||
|
|
||||||
|
# General handler for a lot of actions to centralize the GUI logic a bit
|
||||||
func handle_actions(action, argument = ""):
|
func handle_actions(action, argument = ""):
|
||||||
if action == "add":
|
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)"), "")
|
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)"), "")
|
||||||
|
32
data_item.gd
32
data_item.gd
@ -1,7 +1,5 @@
|
|||||||
extends Node
|
extends Node
|
||||||
|
|
||||||
var _items_path = ""
|
|
||||||
|
|
||||||
# Class information
|
# Class information
|
||||||
# No setter, that's why there is a comma
|
# No setter, that's why there is a comma
|
||||||
var _class setget ,get_class
|
var _class setget ,get_class
|
||||||
@ -10,7 +8,6 @@ var _class_name setget ,get_class_name
|
|||||||
var _dirty = false # TODO: Exclude
|
var _dirty = false # TODO: Exclude
|
||||||
var _persistent = false # TODO: Exclude
|
var _persistent = false # TODO: Exclude
|
||||||
|
|
||||||
|
|
||||||
var _id = ""
|
var _id = ""
|
||||||
var _display_name setget set_display_name,get_display_name
|
var _display_name setget set_display_name,get_display_name
|
||||||
var _created = 0
|
var _created = 0
|
||||||
@ -21,13 +18,13 @@ var _last_modified = 0
|
|||||||
var _custom_properties = {}
|
var _custom_properties = {}
|
||||||
|
|
||||||
func _ready():
|
func _ready():
|
||||||
var config = ConfigFile.new()
|
pass
|
||||||
config.load("res://addons/DataEditor/plugin.cfg")
|
|
||||||
_items_path = config.has_section_key("plugin", "output_directory")
|
|
||||||
|
|
||||||
func get_class():
|
func get_class():
|
||||||
return self.get_script().get_path().get_file().basename()
|
return self.get_script().get_path().get_file().basename()
|
||||||
|
|
||||||
|
|
||||||
func get_class_name():
|
func get_class_name():
|
||||||
return self.get_class().capitalize()
|
return self.get_class().capitalize()
|
||||||
|
|
||||||
@ -38,21 +35,16 @@ func get_display_name():
|
|||||||
else:
|
else:
|
||||||
return _display_name
|
return _display_name
|
||||||
|
|
||||||
|
|
||||||
func set_display_name(name):
|
func set_display_name(name):
|
||||||
_display_name = name
|
_display_name = name
|
||||||
|
|
||||||
|
|
||||||
func sanitize_value(property, type, value):
|
func update_property(property, value):
|
||||||
if property["type"] == TYPE_COLOR:
|
var data_singleton = Globals.get_singleton("data")
|
||||||
value = value.to_html()
|
if data_singleton:
|
||||||
elif property["type"] == TYPE_STRING:
|
data_singleton.set_progress(_class, _id, property, value)
|
||||||
value = value.json_escape()
|
|
||||||
return value
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
func _init(id):
|
func _init(id):
|
||||||
self._id = id
|
self._id = id
|
||||||
|
|
||||||
func _notification(what):
|
|
||||||
print(what)
|
|
@ -44,6 +44,7 @@ var type_names = {"STRING":TYPE_STRING, "BOOL":TYPE_BOOL, "COLOR":TYPE_COLOR, "O
|
|||||||
func _init():
|
func _init():
|
||||||
load_manager()
|
load_manager()
|
||||||
|
|
||||||
|
|
||||||
func load_manager():
|
func load_manager():
|
||||||
initialize_variables()
|
initialize_variables()
|
||||||
load_config()
|
load_config()
|
||||||
@ -111,7 +112,8 @@ func get_item_path(item):
|
|||||||
return config_output_directory + "/" + item._class + "/" + item._id + "." + config_extension
|
return config_output_directory + "/" + item._class + "/" + item._id + "." + config_extension
|
||||||
|
|
||||||
func get_full_path(item):
|
func get_full_path(item):
|
||||||
return config_output_directory.replace("res://", "") + "/" + item._class + "/" + item._id + "." + config_extension
|
return Globals.globalize_path(config_output_directory + "/" + item._class + "/" + item._id + "." + config_extension)
|
||||||
|
# return config_output_directory.replace("res://", "") + "/" + item._class + "/" + item._id + "." + config_extension
|
||||||
|
|
||||||
func load_items():
|
func load_items():
|
||||||
items.clear()
|
items.clear()
|
||||||
@ -373,23 +375,18 @@ func duplicate_item(item, id, display_name):
|
|||||||
items[new_item._class][new_item._id] = new_item
|
items[new_item._class][new_item._id] = new_item
|
||||||
return new_item
|
return new_item
|
||||||
|
|
||||||
# Rename the item, delete the old entrym overwrite the id and save anew
|
# Rename the item, delete the old entry, overwrite the id and save anew
|
||||||
# TODO: Consider rename, could it still be referenced/locked somewhere?
|
# TODO: Could it still be referenced/locked somewhere?
|
||||||
|
# TODO: Check for duplicate ids?
|
||||||
func rename_item(item, new_id):
|
func rename_item(item, new_id):
|
||||||
new_id = sanitize_string(new_id)
|
new_id = sanitize_string(new_id)
|
||||||
if is_id_available(new_id):
|
var directory = Directory.new()
|
||||||
var directory = Directory.new()
|
directory.remove(get_item_path(item))
|
||||||
directory.remove(get_item_path(item))
|
if item._id == item._display_name:
|
||||||
if item._id == item._display_name:
|
item._display_name = new_id
|
||||||
item._display_name = new_id
|
item._id = new_id
|
||||||
save_item(item)
|
save_item(item)
|
||||||
load_manager()
|
load_manager()
|
||||||
else:
|
|
||||||
pass # TODO: Issue warning
|
|
||||||
|
|
||||||
|
|
||||||
func is_id_available(id):
|
|
||||||
return true
|
|
||||||
|
|
||||||
# Adds a custom property to an item.
|
# Adds a custom property to an item.
|
||||||
# Returns true if it succeeded, false if it failed
|
# Returns true if it succeeded, false if it failed
|
||||||
@ -502,12 +499,9 @@ func rename_id_if_exists(item_class, id):
|
|||||||
var number = 0
|
var number = 0
|
||||||
var current_name = id
|
var current_name = id
|
||||||
while(true):
|
while(true):
|
||||||
|
|
||||||
regex.find(current_name)
|
regex.find(current_name)
|
||||||
var id_without_number = regex.get_capture(1)
|
var id_without_number = regex.get_capture(1)
|
||||||
var number_at_end_string = regex.get_capture(2)
|
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)
|
var number_at_end = int(number_at_end_string)
|
||||||
number = number + number_at_end + 1
|
number = number + number_at_end + 1
|
||||||
var new_id = id_without_number + str(number)
|
var new_id = id_without_number + str(number)
|
||||||
@ -542,13 +536,11 @@ func rename_extension_of_all_items(new_extension, serializer):
|
|||||||
directory.rename(original_item_path, new_item_path)
|
directory.rename(original_item_path, new_item_path)
|
||||||
directory.remove(original_item_path)
|
directory.remove(original_item_path)
|
||||||
load_config()
|
load_config()
|
||||||
# load_manager()
|
|
||||||
save_all_items()
|
save_all_items()
|
||||||
else:
|
else:
|
||||||
directory.remove(original_item_path)
|
directory.remove(original_item_path)
|
||||||
load_config()
|
load_config()
|
||||||
save_all_items()
|
save_all_items()
|
||||||
# load_manager()
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@ -575,6 +567,4 @@ func has_unsaved_items():
|
|||||||
return false
|
return false
|
||||||
|
|
||||||
# TODO: Lazy loading
|
# TODO: Lazy loading
|
||||||
# TODO: Proper path handling
|
# TODO: Arrays
|
||||||
# TODO: Arrays
|
|
||||||
# TODO: Proper renaming
|
|
@ -175,7 +175,7 @@ margin/top = 0.0
|
|||||||
margin/right = 20.0
|
margin/right = 20.0
|
||||||
margin/bottom = 20.0
|
margin/bottom = 20.0
|
||||||
popup/exclusive = false
|
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 ]
|
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, false, 4, 0, null, "", false ]
|
||||||
|
|
||||||
[connection signal="button_down" from="Panel/VBox/Margin/HBoxContainer/AddButton" to="." method="_on_AddButton_button_down"]
|
[connection signal="button_down" from="Panel/VBox/Margin/HBoxContainer/AddButton" to="." method="_on_AddButton_button_down"]
|
||||||
|
|
||||||
|
BIN
screenshots/class.png
Normal file
BIN
screenshots/class.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 123 KiB |
BIN
screenshots/editor.png
Normal file
BIN
screenshots/editor.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 139 KiB |
Loading…
Reference in New Issue
Block a user