From ec906117bb0151f7a817c052465b9855f57f8339 Mon Sep 17 00:00:00 2001 From: Vaillant Jeremy Date: Sat, 16 May 2026 19:21:09 +0200 Subject: [PATCH] Replace godot_db_manager plugin with native DB, port lod plugin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit godot_db_manager was incompatible with Godot 4 (used WindowDialog, Tabs, PopupPanel which were all removed). Replace with a minimal Database.gd that parses the same ahog.json format and exposes the same surface API (get_table_by_name, get_data_at_row_idx, edit_data, save_db, etc.) used by the M* model classes — no changes needed in MBase/MScene/MLevel/MSetting. Also port the lod plugin: fix class_name syntax (Godot 4 uses @icon separately from class_name extends) and Particles -> GPUParticles3D. Rewrite Global.gd async scene loading: the convert-3to4 tool mapped load_interactive -> load_threaded_request but those have different APIs (stage count, poll vs. status enum). Reimplement using the new load_threaded_get_status / load_threaded_get pair. Clean project.godot: drop the old _global_script_classes table (Godot 4 uses inline class_name declarations), remove gddb_* autoloads, and remove the godot_db_manager entry from editor_plugins. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../assets/fnt/Roboto-Regular.ttf | Bin 171272 -> 0 bytes .../assets/fnt/roboto_08.tres | 7 - .../assets/fnt/roboto_10.tres | 7 - .../assets/fnt/roboto_12.tres | 7 - .../assets/fnt/roboto_14.tres | 7 - .../assets/fnt/roboto_16.tres | 6 - .../assets/fnt/roboto_18.tres | 7 - .../assets/fnt/roboto_20.tres | 7 - .../assets/fnt/roboto_22.tres | 7 - .../assets/fnt/roboto_24.tres | 7 - .../assets/fnt/roboto_26.tres | 7 - addons/godot_db_manager/assets/tex/debug.png | Bin 1037 -> 0 bytes .../assets/tex/debug.png.import | 34 -- addons/godot_db_manager/assets/tex/gui.png | Bin 6822 -> 0 bytes .../assets/tex/gui.png.import | 34 -- addons/godot_db_manager/core/GDDBConstants.gd | 38 -- addons/godot_db_manager/core/GDDBGlobals.gd | 96 --- addons/godot_db_manager/core/GDDBTypes.gd | 45 -- addons/godot_db_manager/core/database.gd | 325 ---------- addons/godot_db_manager/core/db_data.gd | 40 -- addons/godot_db_manager/core/db_man.gd | 141 ----- addons/godot_db_manager/core/db_prop.gd | 71 --- addons/godot_db_manager/core/db_table.gd | 574 ------------------ addons/godot_db_manager/data_label.gd | 59 -- addons/godot_db_manager/data_label.tscn | 44 -- addons/godot_db_manager/db_editor.gd | 210 ------- addons/godot_db_manager/db_editor.tscn | 40 -- addons/godot_db_manager/db_interface.gd | 155 ----- addons/godot_db_manager/db_interface.tscn | 65 -- addons/godot_db_manager/db_manager.gd | 33 - addons/godot_db_manager/debug/dbg.tscn | 79 --- addons/godot_db_manager/dlgs/data_dlg.gd | 61 -- addons/godot_db_manager/dlgs/data_dlg.tscn | 25 - .../godot_db_manager/dlgs/delete_prop_dlg.gd | 41 -- .../dlgs/delete_prop_dlg.tscn | 66 -- .../godot_db_manager/dlgs/delete_table_dlg.gd | 37 -- .../dlgs/delete_table_dlg.tscn | 66 -- .../godot_db_manager/dlgs/edit_string_dlg.gd | 59 -- .../dlgs/edit_string_dlg.tscn | 60 -- addons/godot_db_manager/dlgs/error_dlg.tscn | 11 - .../dlgs/load_res_path_dlg.gd | 76 --- .../dlgs/load_res_path_dlg.tscn | 16 - addons/godot_db_manager/dlgs/new_db_dlg.gd | 63 -- addons/godot_db_manager/dlgs/new_db_dlg.tscn | 82 --- addons/godot_db_manager/dlgs/new_table_dlg.gd | 83 --- .../godot_db_manager/dlgs/new_table_dlg.tscn | 73 --- addons/godot_db_manager/icon/icon.png | Bin 1713 -> 0 bytes addons/godot_db_manager/icon/icon.png.import | 34 -- addons/godot_db_manager/icon/icon.xcf | Bin 4019 -> 0 bytes addons/godot_db_manager/license.txt | 21 - addons/godot_db_manager/menu.gd | 101 --- addons/godot_db_manager/menu.tscn | 36 -- addons/godot_db_manager/plugin.cfg | 7 - addons/godot_db_manager/table_cell.gd | 221 ------- addons/godot_db_manager/table_cell.tscn | 72 --- addons/godot_db_manager/table_editor.gd | 433 ------------- addons/godot_db_manager/table_editor.tscn | 161 ----- addons/godot_db_manager/table_item.gd | 67 -- addons/godot_db_manager/table_item.tscn | 109 ---- addons/godot_db_manager/table_property.gd | 195 ------ addons/godot_db_manager/table_property.tscn | 89 --- addons/godot_db_manager/tables_header.gd | 17 - addons/godot_db_manager/tables_header.tscn | 65 -- addons/godot_db_manager/tables_list.gd | 129 ---- addons/godot_db_manager/tables_list.tscn | 60 -- addons/lod/lod_cpu_particles.gd | 3 +- addons/lod/lod_omni_light.gd | 3 +- addons/lod/lod_particles.gd | 5 +- addons/lod/lod_spatial.gd | 3 +- addons/lod/lod_spot_light.gd | 3 +- project.godot | 214 +------ scripts/Database.gd | 169 ++++-- scripts/Global.gd | 62 +- 73 files changed, 179 insertions(+), 5071 deletions(-) delete mode 100644 addons/godot_db_manager/assets/fnt/Roboto-Regular.ttf delete mode 100644 addons/godot_db_manager/assets/fnt/roboto_08.tres delete mode 100644 addons/godot_db_manager/assets/fnt/roboto_10.tres delete mode 100644 addons/godot_db_manager/assets/fnt/roboto_12.tres delete mode 100644 addons/godot_db_manager/assets/fnt/roboto_14.tres delete mode 100644 addons/godot_db_manager/assets/fnt/roboto_16.tres delete mode 100644 addons/godot_db_manager/assets/fnt/roboto_18.tres delete mode 100644 addons/godot_db_manager/assets/fnt/roboto_20.tres delete mode 100644 addons/godot_db_manager/assets/fnt/roboto_22.tres delete mode 100644 addons/godot_db_manager/assets/fnt/roboto_24.tres delete mode 100644 addons/godot_db_manager/assets/fnt/roboto_26.tres delete mode 100644 addons/godot_db_manager/assets/tex/debug.png delete mode 100644 addons/godot_db_manager/assets/tex/debug.png.import delete mode 100644 addons/godot_db_manager/assets/tex/gui.png delete mode 100644 addons/godot_db_manager/assets/tex/gui.png.import delete mode 100644 addons/godot_db_manager/core/GDDBConstants.gd delete mode 100644 addons/godot_db_manager/core/GDDBGlobals.gd delete mode 100644 addons/godot_db_manager/core/GDDBTypes.gd delete mode 100644 addons/godot_db_manager/core/database.gd delete mode 100644 addons/godot_db_manager/core/db_data.gd delete mode 100644 addons/godot_db_manager/core/db_man.gd delete mode 100644 addons/godot_db_manager/core/db_prop.gd delete mode 100644 addons/godot_db_manager/core/db_table.gd delete mode 100644 addons/godot_db_manager/data_label.gd delete mode 100644 addons/godot_db_manager/data_label.tscn delete mode 100644 addons/godot_db_manager/db_editor.gd delete mode 100644 addons/godot_db_manager/db_editor.tscn delete mode 100644 addons/godot_db_manager/db_interface.gd delete mode 100644 addons/godot_db_manager/db_interface.tscn delete mode 100644 addons/godot_db_manager/db_manager.gd delete mode 100644 addons/godot_db_manager/debug/dbg.tscn delete mode 100644 addons/godot_db_manager/dlgs/data_dlg.gd delete mode 100644 addons/godot_db_manager/dlgs/data_dlg.tscn delete mode 100644 addons/godot_db_manager/dlgs/delete_prop_dlg.gd delete mode 100644 addons/godot_db_manager/dlgs/delete_prop_dlg.tscn delete mode 100644 addons/godot_db_manager/dlgs/delete_table_dlg.gd delete mode 100644 addons/godot_db_manager/dlgs/delete_table_dlg.tscn delete mode 100644 addons/godot_db_manager/dlgs/edit_string_dlg.gd delete mode 100644 addons/godot_db_manager/dlgs/edit_string_dlg.tscn delete mode 100644 addons/godot_db_manager/dlgs/error_dlg.tscn delete mode 100644 addons/godot_db_manager/dlgs/load_res_path_dlg.gd delete mode 100644 addons/godot_db_manager/dlgs/load_res_path_dlg.tscn delete mode 100644 addons/godot_db_manager/dlgs/new_db_dlg.gd delete mode 100644 addons/godot_db_manager/dlgs/new_db_dlg.tscn delete mode 100644 addons/godot_db_manager/dlgs/new_table_dlg.gd delete mode 100644 addons/godot_db_manager/dlgs/new_table_dlg.tscn delete mode 100644 addons/godot_db_manager/icon/icon.png delete mode 100644 addons/godot_db_manager/icon/icon.png.import delete mode 100644 addons/godot_db_manager/icon/icon.xcf delete mode 100644 addons/godot_db_manager/license.txt delete mode 100644 addons/godot_db_manager/menu.gd delete mode 100644 addons/godot_db_manager/menu.tscn delete mode 100644 addons/godot_db_manager/plugin.cfg delete mode 100644 addons/godot_db_manager/table_cell.gd delete mode 100644 addons/godot_db_manager/table_cell.tscn delete mode 100644 addons/godot_db_manager/table_editor.gd delete mode 100644 addons/godot_db_manager/table_editor.tscn delete mode 100644 addons/godot_db_manager/table_item.gd delete mode 100644 addons/godot_db_manager/table_item.tscn delete mode 100644 addons/godot_db_manager/table_property.gd delete mode 100644 addons/godot_db_manager/table_property.tscn delete mode 100644 addons/godot_db_manager/tables_header.gd delete mode 100644 addons/godot_db_manager/tables_header.tscn delete mode 100644 addons/godot_db_manager/tables_list.gd delete mode 100644 addons/godot_db_manager/tables_list.tscn diff --git a/addons/godot_db_manager/assets/fnt/Roboto-Regular.ttf b/addons/godot_db_manager/assets/fnt/Roboto-Regular.ttf deleted file mode 100644 index 2b6392ffe8712b9c5450733320cd220d6c0f4bce..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 171272 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 diff --git a/addons/godot_db_manager/assets/fnt/roboto_08.tres b/addons/godot_db_manager/assets/fnt/roboto_08.tres deleted file mode 100644 index 1e833af..0000000 --- a/addons/godot_db_manager/assets/fnt/roboto_08.tres +++ /dev/null @@ -1,7 +0,0 @@ -[gd_resource type="FontFile" load_steps=2 format=2] - -[ext_resource path="res://addons/godot_db_manager/assets/fnt/Roboto-Regular.ttf" type="FontFile" id=1] - -[resource] -size = 8 -font_data = ExtResource( 1 ) diff --git a/addons/godot_db_manager/assets/fnt/roboto_10.tres b/addons/godot_db_manager/assets/fnt/roboto_10.tres deleted file mode 100644 index 6979009..0000000 --- a/addons/godot_db_manager/assets/fnt/roboto_10.tres +++ /dev/null @@ -1,7 +0,0 @@ -[gd_resource type="FontFile" load_steps=2 format=2] - -[ext_resource path="res://addons/godot_db_manager/assets/fnt/Roboto-Regular.ttf" type="FontFile" id=1] - -[resource] -size = 10 -font_data = ExtResource( 1 ) diff --git a/addons/godot_db_manager/assets/fnt/roboto_12.tres b/addons/godot_db_manager/assets/fnt/roboto_12.tres deleted file mode 100644 index e3ade0b..0000000 --- a/addons/godot_db_manager/assets/fnt/roboto_12.tres +++ /dev/null @@ -1,7 +0,0 @@ -[gd_resource type="FontFile" load_steps=2 format=2] - -[ext_resource path="res://addons/godot_db_manager/assets/fnt/Roboto-Regular.ttf" type="FontFile" id=1] - -[resource] -size = 12 -font_data = ExtResource( 1 ) diff --git a/addons/godot_db_manager/assets/fnt/roboto_14.tres b/addons/godot_db_manager/assets/fnt/roboto_14.tres deleted file mode 100644 index 99a59a5..0000000 --- a/addons/godot_db_manager/assets/fnt/roboto_14.tres +++ /dev/null @@ -1,7 +0,0 @@ -[gd_resource type="FontFile" load_steps=2 format=2] - -[ext_resource path="res://addons/godot_db_manager/assets/fnt/Roboto-Regular.ttf" type="FontFile" id=1] - -[resource] -size = 14 -font_data = ExtResource( 1 ) diff --git a/addons/godot_db_manager/assets/fnt/roboto_16.tres b/addons/godot_db_manager/assets/fnt/roboto_16.tres deleted file mode 100644 index 2af92ea..0000000 --- a/addons/godot_db_manager/assets/fnt/roboto_16.tres +++ /dev/null @@ -1,6 +0,0 @@ -[gd_resource type="FontFile" load_steps=2 format=2] - -[ext_resource path="res://addons/godot_db_manager/assets/fnt/Roboto-Regular.ttf" type="FontFile" id=1] - -[resource] -font_data = ExtResource( 1 ) diff --git a/addons/godot_db_manager/assets/fnt/roboto_18.tres b/addons/godot_db_manager/assets/fnt/roboto_18.tres deleted file mode 100644 index 9229abc..0000000 --- a/addons/godot_db_manager/assets/fnt/roboto_18.tres +++ /dev/null @@ -1,7 +0,0 @@ -[gd_resource type="FontFile" load_steps=2 format=2] - -[ext_resource path="res://addons/godot_db_manager/assets/fnt/Roboto-Regular.ttf" type="FontFile" id=1] - -[resource] -size = 18 -font_data = ExtResource( 1 ) diff --git a/addons/godot_db_manager/assets/fnt/roboto_20.tres b/addons/godot_db_manager/assets/fnt/roboto_20.tres deleted file mode 100644 index f7fdb61..0000000 --- a/addons/godot_db_manager/assets/fnt/roboto_20.tres +++ /dev/null @@ -1,7 +0,0 @@ -[gd_resource type="FontFile" load_steps=2 format=2] - -[ext_resource path="res://addons/godot_db_manager/assets/fnt/Roboto-Regular.ttf" type="FontFile" id=1] - -[resource] -size = 20 -font_data = ExtResource( 1 ) diff --git a/addons/godot_db_manager/assets/fnt/roboto_22.tres b/addons/godot_db_manager/assets/fnt/roboto_22.tres deleted file mode 100644 index 903e6e1..0000000 --- a/addons/godot_db_manager/assets/fnt/roboto_22.tres +++ /dev/null @@ -1,7 +0,0 @@ -[gd_resource type="FontFile" load_steps=2 format=2] - -[ext_resource path="res://addons/godot_db_manager/assets/fnt/Roboto-Regular.ttf" type="FontFile" id=1] - -[resource] -size = 22 -font_data = ExtResource( 1 ) diff --git a/addons/godot_db_manager/assets/fnt/roboto_24.tres b/addons/godot_db_manager/assets/fnt/roboto_24.tres deleted file mode 100644 index f6b7ca2..0000000 --- a/addons/godot_db_manager/assets/fnt/roboto_24.tres +++ /dev/null @@ -1,7 +0,0 @@ -[gd_resource type="FontFile" load_steps=2 format=2] - -[ext_resource path="res://addons/godot_db_manager/assets/fnt/Roboto-Regular.ttf" type="FontFile" id=1] - -[resource] -size = 24 -font_data = ExtResource( 1 ) diff --git a/addons/godot_db_manager/assets/fnt/roboto_26.tres b/addons/godot_db_manager/assets/fnt/roboto_26.tres deleted file mode 100644 index 2391537..0000000 --- a/addons/godot_db_manager/assets/fnt/roboto_26.tres +++ /dev/null @@ -1,7 +0,0 @@ -[gd_resource type="FontFile" load_steps=2 format=2] - -[ext_resource path="res://addons/godot_db_manager/assets/fnt/Roboto-Regular.ttf" type="FontFile" id=1] - -[resource] -size = 26 -font_data = ExtResource( 1 ) diff --git a/addons/godot_db_manager/assets/tex/debug.png b/addons/godot_db_manager/assets/tex/debug.png deleted file mode 100644 index 19b7b7fc8c86479e4d6f28885b5c7e846e4abfe8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1037 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H1|$#LC7xzrU~J2Db`J1#c2+1T%1_J8No8Qr zm{>c}*5j~)MBDvkS)p38ZxkL)SZEa~AS$T7GD%Qp$~LFfd}`DGarjD25)pSlxb?w< z^GR0^9*qoV4|f#!!&q2U5>zr#Xumg?ZqlB|JKDeR`~LKuwZn?(GqcKi7+0l9uB`Am z^DOMh0qtEG-xOLTJ7-oY?VZs0{Ku~L{`cj9^Pa|jfBqu)lNxi4(np=kyxkRZE3z(M zvV1Sx9_bm>alX@?cbCFPz4XNPK@qz|#U)keA{Bexg#irr&cB#ligi zW>;|D8?}p(kFRl}QtJB3J-c`B&HsPRxk~Kvu>U6n+ff2)&}9(vbnWH+oqN)IUA~@qf6ZMgb5+CltFM!K3>UH9%|82MdU{X6zVKy& z_x4|xbDOa|eO|%-Y`Zg;wf!sRnV$c==eEn|_ScVRKHqWn)117&+wcGX!N13xHGn~c zfz^Q#Offu=-BZW?qf@M;{@m&LA0+38hAPi_ZyB$=r~dp;sl<8nDi8f#_tU_Cz0JeQ zYc}UU=Lw&cU)P^L@3GnG;$5{>>`-H1rd4FGzt6O9G3(vml|NL!mD@X?@n1h~ ze7XwHX!w$HO$DyyDxe&7A< zu+832EzcuA+nnF+Yf`_%ZeC^IZJqNk%f9oWI1H4P34jmt>ln|z>`4e-b@3WVsi&)- J%Q~loCIGe2$G-po diff --git a/addons/godot_db_manager/assets/tex/debug.png.import b/addons/godot_db_manager/assets/tex/debug.png.import deleted file mode 100644 index af3617f..0000000 --- a/addons/godot_db_manager/assets/tex/debug.png.import +++ /dev/null @@ -1,34 +0,0 @@ -[remap] - -importer="texture" -type="StreamTexture" -path="res://.import/debug.png-72578d6223132f8bc16d1922f9da1248.stex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://addons/godot_db_manager/assets/tex/debug.png" -dest_files=[ "res://.import/debug.png-72578d6223132f8bc16d1922f9da1248.stex" ] - -[params] - -compress/mode=0 -compress/lossy_quality=0.7 -compress/hdr_mode=0 -compress/bptc_ldr=0 -compress/normal_map=0 -flags/repeat=0 -flags/filter=true -flags/mipmaps=false -flags/anisotropic=false -flags/srgb=2 -process/fix_alpha_border=true -process/premult_alpha=false -process/HDR_as_SRGB=false -process/invert_color=false -stream=false -size_limit=0 -detect_3d=true -svg/scale=1.0 diff --git a/addons/godot_db_manager/assets/tex/gui.png b/addons/godot_db_manager/assets/tex/gui.png deleted file mode 100644 index d7e49993d5488ed1a7af1bea7637b2e73e176be5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6822 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 diff --git a/addons/godot_db_manager/assets/tex/gui.png.import b/addons/godot_db_manager/assets/tex/gui.png.import deleted file mode 100644 index d42b053..0000000 --- a/addons/godot_db_manager/assets/tex/gui.png.import +++ /dev/null @@ -1,34 +0,0 @@ -[remap] - -importer="texture" -type="StreamTexture" -path="res://.import/gui.png-1d6d47ba1ecd00882b63b6f57f502e61.stex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://addons/godot_db_manager/assets/tex/gui.png" -dest_files=[ "res://.import/gui.png-1d6d47ba1ecd00882b63b6f57f502e61.stex" ] - -[params] - -compress/mode=0 -compress/lossy_quality=0.7 -compress/hdr_mode=0 -compress/bptc_ldr=0 -compress/normal_map=0 -flags/repeat=0 -flags/filter=true -flags/mipmaps=false -flags/anisotropic=false -flags/srgb=2 -process/fix_alpha_border=true -process/premult_alpha=false -process/HDR_as_SRGB=false -process/invert_color=false -stream=false -size_limit=0 -detect_3d=true -svg/scale=1.0 diff --git a/addons/godot_db_manager/core/GDDBConstants.gd b/addons/godot_db_manager/core/GDDBConstants.gd deleted file mode 100644 index 4b2f9b5..0000000 --- a/addons/godot_db_manager/core/GDDBConstants.gd +++ /dev/null @@ -1,38 +0,0 @@ -@tool -""" -class GDDBConstants -""" - -class_name GDDBConstants - -extends Node - -# GDDB signature -const c_gddb_signature = "GDDB_ver" - -# GDDB ver -const c_gddb_ver = "2.0" - -# invalid id to initialize integer properties -const c_invalid_id = -1 - -# maximum database name length -const c_max_db_name_len = 16 - -# maximum table name length -const c_max_table_name_len = 16 - -# characters that should not be part of the database name -const c_invalid_characters = "`~!@#$%^&*()=+[]{}\\|;:'\",<.>/?" - -# addon main path -const c_addon_main_path = "res://addons/godot_db_manager/" - -# maximum tables list width -const c_max_tables_list_width = 400.0 - -# minimum cell width -const c_min_cell_width = 150.0 - -# maximum cell width -const c_max_cell_width = 400.0 diff --git a/addons/godot_db_manager/core/GDDBGlobals.gd b/addons/godot_db_manager/core/GDDBGlobals.gd deleted file mode 100644 index 24dfd1a..0000000 --- a/addons/godot_db_manager/core/GDDBGlobals.gd +++ /dev/null @@ -1,96 +0,0 @@ -@tool -""" -class GDDBGlobals -""" - -class_name GDDBGlobals - -extends Node - -# a flag that tells if the tool's interface is active or not -var m_is_interface_active : bool = false - -# sets the active flag -func set_interface_active(active : bool) -> void : - m_is_interface_active = active - -# returns true if the tool's interface is active -func is_interface_active() -> bool : - return m_is_interface_active - -# returns the name of the data type -func get_data_name(data_type : int) -> String : - if(data_type == gddb_types.e_prop_type_bool): - return "Bool" - elif(data_type == gddb_types.e_prop_type_int): - return "Integer" - elif(data_type == gddb_types.e_prop_type_float): - return "Float" - elif(data_type == gddb_types.e_prop_type_string): - return "String" - elif(data_type == gddb_types.e_prop_type_resource): - return "Resource" - - print("GDDBGlobals::get_data_name(" + str(data_type) + ")") - return "Unknown data type" - -# returns the name of the data filter -func get_data_filter_name(data_filter_type : int) -> String : - if(data_filter_type == gddb_types.e_data_filter_equal): - return "Equal" - elif(data_filter_type == gddb_types.e_data_filter_not_equal): - return "Not equal" - elif(data_filter_type == gddb_types.e_data_filter_less): - return "Less" - elif(data_filter_type == gddb_types.e_data_filter_greater): - return "Greater" - elif(data_filter_type == gddb_types.e_data_filter_lequal): - return "Less or equal" - elif(data_filter_type == gddb_types.e_data_filter_gequal): - return "Greater or equal" - - print("GDDBGlobals::get_data_filter_name(" + str(data_filter_type) + ")") - return "Unknown data filter type" - -# checks the name of the database -func check_db_name(db_name : String) -> bool : - for idx in range(0, db_name.length()): - for jdx in range(0, gddb_constants.c_invalid_characters.length()): - if(db_name[idx] == gddb_constants.c_invalid_characters[jdx]): - return false - return true - -# returns a json from a row from a table -func get_json_from_row(table : Object, row_idx : int) -> String : - var json = "{" - var row = table.get_data_at_row_idx(row_idx) - for jdx in range(0, row.size()): - var prop = table.get_prop_at(jdx) - json += "\"" + prop.get_prop_name() + "\":" - json += "\"" + row[jdx].get_data() + "\"" - if(jdx < row.size() - 1): - json += ", " - json += "}" - return json - -# returns the digits count from a number -func get_digits_count(number : int) -> int : - if(number == 0): - return 1 - - var digits_count = 0 - while(number > 0): - number /= 10 - digits_count += 1 - - return digits_count - -# replace special characters in a string to handle properly saving into database -func handle_string(text : String) -> String : - var string = "" - for idx in range(0, text.length()): - if(text[idx] == "\n"): - string += "\\n" - else: - string += text[idx] - return string diff --git a/addons/godot_db_manager/core/GDDBTypes.gd b/addons/godot_db_manager/core/GDDBTypes.gd deleted file mode 100644 index 9cbd4cd..0000000 --- a/addons/godot_db_manager/core/GDDBTypes.gd +++ /dev/null @@ -1,45 +0,0 @@ -@tool -""" -class GDDBTypes -""" - -class_name GDDBTypes - -extends Node - -# Database loading errors -enum { - e_db_invalid_file = -11, - e_db_invalid_ver = -10, - e_db_valid = 0 -} - -# Property types -enum { - e_prop_type_bool = 0 - e_prop_type_int = 1, - e_prop_type_float = 2, - e_prop_type_string = 3, - - # TODO: insert more data types here and increase e_prop_type_resource - - e_prop_type_resource = 4, - - e_prop_types_count -} - -# Data filters -enum { - e_data_filter_equal = 0, - e_data_filter_not_equal = 1, - e_data_filter_less = 2, - e_data_filter_greater = 3, - e_data_filter_lequal = 4, # less or equal - e_data_filter_gequal = 5 # greater or equal -} - -# new table dlg type -enum { - e_new_dlg_type_new = 0, - e_new_dlg_type_edit = 1 -} diff --git a/addons/godot_db_manager/core/database.gd b/addons/godot_db_manager/core/database.gd deleted file mode 100644 index 1641209..0000000 --- a/addons/godot_db_manager/core/database.gd +++ /dev/null @@ -1,325 +0,0 @@ -""" -GDDatabase class -""" - -class_name GDDatabase - -extends Object - -# database types -enum { - e_db_type_json = 0, # JSON database - e_db_type_binary = 1 # binary database - to be designed / implemented -} - -# database type -var m_db_type : int = e_db_type_json - -# the id of the database -var m_db_id : int = gddb_constants.c_invalid_id - -# the name of the database -var m_db_name : String = "" - -# tables in the database -var m_tables : Array = [] - -# the path of the database -var m_db_filepath : String = "" - -# if the database is not saved, it is dirty -var m_is_dirty : bool = false - -# sets the id of the database -func set_db_id(db_id : int) -> void : - m_db_id = db_id - -# returns the database id -func get_db_id() -> int : - return m_db_id - -# set the name of the database -# the name of the database should not contain special characters -func set_db_name(db_name : String) -> bool : - if(!gddb_globals.check_db_name(db_name)): - print("ERROR: GDDatabase::set_db_name(" + db_name + ") - the name of the database contains invalid characters") - return false - # print("GDDatabase::set_db_name(" + db_name + ")") - m_db_name = db_name - return true - -# returns the name of the database -func get_db_name() -> String : - return m_db_name - -# sets the database file path -func set_db_filepath(filepath : String) -> void : - m_db_filepath = filepath - -# returns the path of the database -func get_db_filepath() -> String : - return m_db_filepath - -# checks if a table with the name "table_name" can be added into database -func can_add_table(table_name : String, table_id : int = -1) -> bool : - # print("GDDatabase::can_add_table(" + table_name + ", " + str(table_id) + ")") - for idx in range(0, m_tables.size()): - if(m_tables[idx].get_table_name() == table_name): - if(m_tables[idx].get_table_id() == table_id): - continue - print("WARNING: GDDatabase::can_add_table(" + table_name + ", " + str(table_id) + ") - table already exists") - return false - return true - -# adds a new table -# returns the table id; if already exists, it will fire an warning on output console and returns an invalid id -func add_table(table_name : String) -> int : - if(!can_add_table(table_name)): - print("WARNING: GDDatabase::add_table(" + table_name + ") - cannot add table") - return gddb_constants.c_invalid_id - - # print("GDDatabase::add_table(" + table_name + ") to \"" + m_db_name + "\" database") - - var table_id = generate_new_table_id() - var table = load(gddb_constants.c_addon_main_path + "core/db_table.gd").new() - table.set_table_id(table_id) - table.set_table_name(table_name) - table.set_parent_database(self) - m_tables.push_back(table) - return table_id - -# edits a table name -func edit_table_name(table_name : String, table_id : int) -> bool : - if(!can_add_table(table_name, table_id)): - return false - for idx in range(0, m_tables.size()): - if(m_tables[idx].get_table_id() == table_id): - m_tables[idx].set_table_name(table_name) - break - return true - -# deletes a table at index -func delete_table_at(idx : int) -> void : - if(idx < 0 || idx > m_tables.size() - 1): - print("GDDatabase::delete_table_at(" + str(idx) + ") - index out of bounds") - return - m_tables[idx].clear() - m_tables[idx].free() - m_tables.remove(idx) - -# deletes a table by id -func delete_table_by_id(table_id: int) -> void : - for idx in range(0, m_tables.size()): - if(m_tables[idx].get_table_id() == table_id): - # print("GDDatabase::delete_table_by_id(" + str(table_id) + ")") - m_tables[idx].clear() - m_tables[idx].free() - m_tables.remove(idx) - return - print("ERROR: GDDatabase::delete_table_by_id(" + str(table_id) + ") - cannot erase table; id not found") - -# deletes a table by name -func delete_table_by_name(table_name: String) -> void : - for idx in range(0, m_tables.size()): - if(m_tables[idx].get_table_name() == table_name): - # print("GDDatabase::delete_table_by_name(" + table_name + ")") - m_tables[idx].clear() - m_tables[idx].free() - m_tables.remove(idx) - return - print("ERROR: GDDatabase::delete_table_by_name(" + table_name + ") - cannot erase table; name not found") - -# generates a new table id -func generate_new_table_id() -> int : - if(m_tables.size() == 0): - return 0 - return m_tables[m_tables.size()-1].get_table_id() + 1 - -# returns the count of the tables in the database -func get_tables_count() -> int : - return m_tables.size() - -# returns true if the table exists in the database, false otherwise -# this is equivalent with (get_table_by_name(table name) != null) function, but without firing the error in case the table doesn't exist -func is_table_exists(table_name : String) -> bool : - for idx in range(0, m_tables.size()): - if(m_tables[idx].get_table_name() == table_name): - return true - return false - -# returns a table by an index or null if the index is invalid -func get_table_at(idx: int) -> Object : - if(idx < 0 || idx >= m_tables.size()): - print("ERROR: GDDatabase::get_table_at(" + str(idx) + ") - cannot obtain table with index") - return null - return m_tables[idx] - -# returns a table by its id or null if a table with id doesn's exist -func get_table_by_id(table_id: int) -> Object : - for idx in range(0, m_tables.size()): - if(m_tables[idx].get_table_id() == table_id): - return m_tables[idx] - print("ERROR: GDDatabase::get_table_by_id(" + str(table_id) + ") - cannot obtain table with id") - return null - -# returns a table by its name or null if the name of the table doesn's exist -func get_table_by_name(table_name: String) -> Object : - for idx in range(0, m_tables.size()): - if(m_tables[idx].get_table_name() == table_name): - return m_tables[idx] - print("ERROR: GDDatabase::get_table_by_id(" + table_name + ") - cannot obtain table with name") - return null - -# deletes all the tables -func clear() -> void : - # print("GDDatabase::clear()") - m_db_name = "" - for idx in range(0, m_tables.size()): - m_tables[idx].clear() - m_tables.clear() - -# sets the database dirty; it is not saved -func set_dirty(dirty : bool) -> void : - m_is_dirty = dirty - -# returns true if a database is dirty (should be saved), false otherwise -func is_dirty() -> bool : - return m_is_dirty - -# serialization -func save_db() -> void : - if(m_db_name.is_empty()): - print("ERROR: GDDatabase::save_db() - current database doesn't have a name") - return - - if(m_db_filepath.is_empty()): - print("ERROR: GDDatabase::save_db() - current database doesn't have a path file") - return - - # print("GDDatabase::save_db() - " + m_db_name + " to: " + m_db_filepath) - var text = "{" - text += "\n\t\"" + gddb_constants.c_gddb_signature + "\":\"" + gddb_constants.c_gddb_ver + "\"," - text += "\n\t\"db_name\":\"" + m_db_name + "\"," - text += "\n\t\"tables\":[" - for idx in range(0, m_tables.size()): - text += "\n\t\t{" - text += "\n\t\t\t\"table_name\":\"" + m_tables[idx].get_table_name() + "\"," - text += "\n\t\t\t\"props\":[" - for jdx in range(0, m_tables[idx].get_props_count()): - var db_prop = m_tables[idx].get_prop_at(jdx) - text += "\n\t\t\t\t\t{" - text += "\"name\":\"" + str(db_prop.get_prop_name()) + "\"," - - var prop_type = db_prop.get_prop_type() - if(prop_type < gddb_types.e_prop_types_count): - text += "\"type\":\"" + str(prop_type) + "\"," - else: - # print("GDDatabase::save_db() - prop_type: " + str(prop_type)) - var table_id = prop_type - gddb_types.e_prop_types_count - var table = get_table_by_id(table_id) - if(null == table): - print("GDDatabase::save_db() - table not found with id: " + str(table_id)) - text += "\"type\":\"" + "table" + "\"," - text += "\"table_name\":\"" + table.get_table_name() + "\"," - - text += "\"auto_increment\":\"" + str(int(db_prop.has_autoincrement())) + "\"" - - text += "}" - if(jdx < m_tables[idx].get_props_count() - 1): - text += "," - text += "\n\t\t\t]," - - text += "\n\t\t\t\"data\":[" - for jdx in range(0, m_tables[idx].get_data_size()): - #var the_data = m_tables[idx].get_data_at(jdx) - #print("getting data at " + str(jdx) + " : " + the_data) - text += "\"" + m_tables[idx].get_data_at(jdx) + "\"" - if(jdx < m_tables[idx].get_data_size() - 1): - text += "," - text += "]" # end of data - text += "\n\t\t}" # end of table - - if(idx < m_tables.size() - 1): - text += "," - - text += "\n\t]\n}" - - var save_file = File.new() - save_file.open(m_db_filepath, File.WRITE) - save_file.store_string(text) - save_file.close() - - set_dirty(false) - -# deserialization -func load_db() -> int : - var file = File.new() - file.open(get_db_filepath(), File.READ) - var content = file.get_as_text() - file.close() - var test_json_conv = JSON.new() - test_json_conv.parse(content).result - var dictionary = test_json_conv.get_data() - - # check the signature - if(!dictionary.has(gddb_constants.c_gddb_signature)): - print("GDDatabase::load_db() - invalid database file") - return gddb_types.e_db_invalid_file - - var gddb_signature = dictionary[gddb_constants.c_gddb_signature] - if(gddb_signature != gddb_constants.c_gddb_ver): - print("GDDatabase::load_db() - invalid database version") - return gddb_types.e_db_invalid_ver - - clear() - m_db_name = dictionary["db_name"] - var tables = dictionary["tables"] - for idx in range(0, tables.size()): - var table_id = add_table(tables[idx]["table_name"]) - var table = get_table_by_id(table_id) - - var props_count = tables[idx]["props"].size() - if(props_count == 0): - continue - - for jdx in range(0, props_count): - var prop_type = tables[idx]["props"][jdx]["type"] - - var prop_id = -1 - if(prop_type == "table"): - var table_name = tables[idx]["props"][jdx]["table_name"] - prop_id = table.add_table_prop(tables[idx]["props"][jdx]["name"], table_name) - else: - prop_id = table.add_prop(prop_type.to_int(), tables[idx]["props"][jdx]["name"]) - - var prop = table.get_prop_by_id(prop_id) - var enable_autoincrement = tables[idx]["props"][jdx]["auto_increment"].to_int() - prop.enable_autoincrement(bool(enable_autoincrement)) - - var data_count = tables[idx]["data"].size() - #print("********* set data to db - begin") - for jdx in range(0, data_count / props_count): - var row_data = [] - for kdx in range(0, props_count): - var cell_data = tables[idx]["data"][jdx * props_count + kdx] - # print("cell_data: " + cell_data) - row_data.push_back(cell_data) - #print("row_data: " + str(row_data)) - table.add_row(row_data) - #print("********* set data to db - end") - - # link custom data to tables - for idx in range(0, m_tables.size()): - m_tables[idx].link_tables_props() - - return gddb_types.e_db_valid - -# dumps the database -func dump() -> String : - var dump_text = "\nDatabase dump. id: " + str(m_db_id) + ", name: " + m_db_name + ", filepath: " + m_db_filepath - dump_text += "\n------------------------------------------------------------------------------------\n" - - for idx in range(0, m_tables.size()): - dump_text += "\n" + m_tables[idx].dump() - - return dump_text diff --git a/addons/godot_db_manager/core/db_data.gd b/addons/godot_db_manager/core/db_data.gd deleted file mode 100644 index da9256d..0000000 --- a/addons/godot_db_manager/core/db_data.gd +++ /dev/null @@ -1,40 +0,0 @@ -""" -GDDBData class -""" - -class_name GDDBData - -extends Object - -var m_prop_id : int = -1 -var m_row_idx : int = -1 -var m_data : String = "" - -# sets the property id -func set_prop_id(prop_id : int) -> void : - m_prop_id = prop_id - -# returns the property id -func get_prop_id() -> int : - return m_prop_id - -# sets the row index -func set_row_idx(row_idx : int) -> void : - m_row_idx = row_idx - -# returns the row index -func get_row_idx() -> int : - return m_row_idx - -# sets the data -func set_data(data : String) -> void : - m_data = data - -# returns the data -func get_data() -> String : - return m_data - -# dumps the data -func dump() -> String : - var dump_text = "prop_id: " + str(m_prop_id) + ", row_idx: " + str(m_row_idx) + ", data: " + m_data - return dump_text diff --git a/addons/godot_db_manager/core/db_man.gd b/addons/godot_db_manager/core/db_man.gd deleted file mode 100644 index 95f4982..0000000 --- a/addons/godot_db_manager/core/db_man.gd +++ /dev/null @@ -1,141 +0,0 @@ -""" -GDDBMan class -""" - -class_name GDDBMan - -extends Object - -var m_databases = [] - -# adds a database -func add_database(db_name : String) -> int : - if(!can_add_db(db_name)): - print("ERROR: GDDBMan::add_database(" + db_name + ") already exists") - return gddb_constants.c_invalid_id - # print("GDDBMan::add_database(" + db_name + ")") - var db_id = generate_new_db_id() - var db = load(gddb_constants.c_addon_main_path + "core/database.gd").new() - db.set_db_id(db_id) - db.set_db_name(db_name) - m_databases.push_back(db) - return db_id - -# loads a database from a file -func load_database(filepath : String) -> int : - var db_id = generate_new_db_id() - var db = load(gddb_constants.c_addon_main_path + "core/database.gd").new() - db.set_db_id(db_id) - db.set_db_filepath(filepath) - - if(db.load_db() == gddb_types.e_db_invalid_file): - db.free() - return gddb_types.e_db_invalid_file - - elif(db.load_db() == gddb_types.e_db_invalid_ver): - db.free() - return gddb_types.e_db_invalid_ver - - m_databases.push_back(db) - return db_id - -# erases a database at index -# it does not erase the database file -func erase_db_at(idx : int) -> void : - if(idx < 0 || idx > m_databases.size() - 1): - print("ERROR: GDDBMan::erase_db_at(" + str(idx) + ") - index out of bounds") - - m_databases[idx].clear() - m_databases[idx].free() - m_databases.remove(idx) - -# erases a database by id -# it does not erase the database file -func erase_db_by_id(db_id : int) -> void : - var db_found = false - for idx in range(0, m_databases.size()): - if(m_databases[idx].get_db_id() == db_id): - m_databases[idx].clear() - m_databases[idx].free() - m_databases.remove(idx) - db_found = true - break - - if(!db_found): - print("ERROR: GDDBMan::erase_db_by_id(" + str(db_id) + ") - database not found") - -# erases a database by name -# it does not erase the database file -func erase_db_by_name(db_name : String) -> void : - var db_found = false - for idx in range(0, m_databases.size()): - if(m_databases[idx].get_db_name() == db_name): - m_databases[idx].clear() - m_databases[idx].free() - m_databases.remove(idx) - db_found = true - break - - if(!db_found): - print("ERROR: GDDBMan::erase_db_by_id(" + db_name + ") - database not found") - -# returns the databases count -func get_databases_count() -> int : - return m_databases.size() - -# returns a database at index -func get_db_at(idx : int) -> Object : - if(idx < 0 || idx >= m_databases.size()): - print("ERROR: GDDBMan::get_db_at(" + str(idx) + ") - invalid index") - return null - return m_databases[idx] - -# returns a database by an id -func get_db_by_id(db_id : int) -> Object : - for idx in range(0, m_databases.size()): - if(m_databases[idx].get_db_id() == db_id): - return m_databases[idx] - print("ERROR: GDDBMan::get_db_by_id(" + str(db_id) + ") - invalid id") - return null - -# returns a database by a name -func get_db_by_name(db_name : String) -> Object : - for idx in range(0, m_databases.size()): - if(m_databases[idx].get_db_name() == db_name): - return m_databases[idx] - return null - -# generates a new table id -func generate_new_db_id() -> int : - if(m_databases.size() == 0): - return 0 - return m_databases[m_databases.size()-1].get_db_id() + 1 - -# checks if a database already exists -func can_add_db(db_name : String) -> bool : - for idx in range(0, m_databases.size()): - if(m_databases[idx].get_db_name() == db_name): - print("ERROR: Database with name \"" + db_name + "\" already exists") - return false - return true - -# deletes all databases -func clear() -> void : - for idx in range(0, m_databases.size()): - m_databases[idx].clear() - m_databases[idx].free() - m_databases.clear() - -# dumps all databases -func dump(to_console : bool = false) -> String : - var dump_text = "\nDatabase manager - dump" - - dump_text += "\n====================================================================================" - for idx in range(0, m_databases.size()): - dump_text += m_databases[idx].dump() - dump_text += "====================================================================================" - - if(to_console): - print(dump_text) - - return dump_text diff --git a/addons/godot_db_manager/core/db_prop.gd b/addons/godot_db_manager/core/db_prop.gd deleted file mode 100644 index 6931ff8..0000000 --- a/addons/godot_db_manager/core/db_prop.gd +++ /dev/null @@ -1,71 +0,0 @@ -""" -GDDBProperty class -""" - -class_name GDDBProperty - -extends Object - -var m_prop_id : int = -1 -var m_prop_type : int = gddb_types.e_prop_type_int -var m_custom_type : String = "" -var m_prop_name : String = "" - -var m_autoincrement : bool = false - -# sets the property id -func set_prop_id(prop_id : int) -> void : - m_prop_id = prop_id - -# returns the property id -func get_prop_id() -> int : - return m_prop_id - -# sets the property type -func set_prop_type(prop_type : int) -> void : - m_prop_type = prop_type - -# returns the property type -func get_prop_type() -> int : - return m_prop_type - -# sets the custom type -func set_prop_custom_type(prop_type : String) -> void : - m_custom_type = prop_type - -# returns the custom type -func get_prop_custom_type() -> String : - return m_custom_type - -# sets the property name -func set_prop_name(prop_name : String) -> void : - m_prop_name = prop_name - -# returns the property name -func get_prop_name() -> String : - return m_prop_name - -# enables or disables the auto increment property -func enable_autoincrement(enable : bool) -> void : - if(enable && m_prop_type != gddb_types.e_prop_type_int): - if(m_prop_type < gddb_types.e_prop_types_count): - print("ERROR: autoincrement option can be set to integer data type only. Type is: " + gddb_types.get_data_name(m_prop_type)) - else: - print("ERROR: autoincrement option can be set to integer data type only. Custom type is: " + m_custom_type) - return - m_autoincrement = enable - -# returns if the property has auto increment -func has_autoincrement() -> bool : - return m_autoincrement - -# dumps the property -func dump() -> String : - var dump_text = "prop_id: " + str(m_prop_id) + ", prop_name: " + m_prop_name - - if(m_prop_type <= gddb_types.e_prop_type_resource): - dump_text += ", prop_type: " + gddb_globals.get_data_name(m_prop_type) - else: - dump_text += ", custom_prop_type: " + m_custom_type - - return dump_text diff --git a/addons/godot_db_manager/core/db_table.gd b/addons/godot_db_manager/core/db_table.gd deleted file mode 100644 index 568ed23..0000000 --- a/addons/godot_db_manager/core/db_table.gd +++ /dev/null @@ -1,574 +0,0 @@ -""" -GDDBTable class -""" - -class_name GDDBTable - -extends Object - -var m_table_id : int = gddb_constants.c_invalid_id -var m_table_name : String = "" -var m_props : Array = [] -var m_data : Array = [] -var m_rows_count : int = 0 - -var m_parent_database : Object = null - -# sets the table id -func set_table_id(table_id : int) -> void : - # print("GDDBDTable::set_table_id(" + str(table_id) + ")") - m_table_id = table_id - -# returns the table id -func get_table_id() -> int : - return m_table_id - -# sets the table name -func set_table_name(table_name: String) -> void : - # print("GDDBDTable::set_table_name(" + table_name + ")") - m_table_name = table_name - -# returns the table name -func get_table_name() -> String : - return m_table_name - -# sets the parent database -func set_parent_database(db : Object) -> void : - # print("GDDBTable::set_parent_database(" + str(db) + ")") - m_parent_database = db - -# returns parent database -func get_parent_database() -> Object : - return m_parent_database - -# adds a property in the table structure as a base type -# returns prop ID -func add_prop(prop_type : int, prop_name : String) -> int : - var prop_id = generate_new_prop_id() - - # print("GDDBDTable::add_prop(" + str(prop_id) + ", " + str(prop_type) + ", " + prop_name + ")") - var prop = load(gddb_constants.c_addon_main_path + "core/db_prop.gd").new() - prop.set_prop_id(prop_id) - prop.set_prop_type(prop_type) - prop.set_prop_name(prop_name) - m_props.push_back(prop) - - # adding blank data to all existing rows - # TODO: find a way to make this better; this is ugly - if(m_data.size() > 0): - var new_data_array = [] - var data_idx = 0 - var row_idx = 0 - while(true): - var data = m_data[data_idx] - row_idx = data.get_row_idx() - if(data.get_prop_id() + 1 == prop_id): - new_data_array.push_back(data) - var new_data = load(gddb_constants.c_addon_main_path + "core/db_data.gd").new() - new_data.set_prop_id(prop_id) - new_data.set_row_idx(row_idx) - - if(prop_type == gddb_types.e_prop_type_bool): - new_data.set_data("0") - - elif(prop_type == gddb_types.e_prop_type_int): - if(prop.has_autoincrement()): - new_data.set_data(str(m_rows_count+1)) - else: - new_data.set_data("0") - - elif(prop_type == gddb_types.e_prop_type_float): - new_data.set_data("0.0") - - elif(prop_type == gddb_types.e_prop_type_string): - new_data.set_data("") - - elif(prop_type == gddb_types.e_prop_type_resource): - new_data.set_data("res://") - - elif(prop_type >= gddb_types.e_prop_types_count): - new_data.set_data(str(-1)) - - new_data_array.push_back(new_data) - else: - new_data_array.push_back(data) - - data_idx += 1 - if(data_idx >= m_data.size()): - break - - m_data.clear() - for idx in range(0, new_data_array.size()): - m_data.push_back(new_data_array[idx]) - - return prop_id - -# adds a property in the table structure as a table type -# returns prop ID -func add_table_prop(prop_name : String, table_name : String) -> int : - # print("GDDBTable::add_table_prop(" + prop_name + ", " + table_name + ")") - var prop_id = generate_new_prop_id() - - # print("GDDBDTable::add_prop(" + str(prop_id) + ", " + str(prop_type) + ", " + prop_name + ")") - var prop = load(gddb_constants.c_addon_main_path + "core/db_prop.gd").new() - prop.set_prop_id(prop_id) - prop.set_prop_name(prop_name) - prop.set_prop_custom_type(table_name) - m_props.push_back(prop) - - return prop_id - -# links custom properties from tables -func link_tables_props() -> void : - for idx in range(0, m_props.size()): - var custom_prop_type = m_props[idx].get_prop_custom_type() - if(!custom_prop_type.is_empty()): - var table = m_parent_database.get_table_by_name(custom_prop_type) - m_props[idx].set_prop_type(gddb_types.e_prop_types_count + table.get_table_id()) - m_props[idx].set_prop_custom_type("") - -# edits a property in the table structure -func edit_prop(prop_id : int, prop_type : int, prop_name: String) -> void : - for idx in range(0, m_props.size()): - if(m_props[idx].get_prop_id() == prop_id): - m_props[idx].set_prop_type(prop_type) - m_props[idx].set_prop_name(prop_name) - return - - print("ERROR: GDDBDTable::edit_prop(" + str(prop_id) + ", " + str(prop_type) + ", " + prop_name + ") - property not found") - -# enables or disables autoincrement on a property -func enable_prop_autoincrement(prop_id : int, enable : bool) -> void : - for idx in range(0, m_props.size()): - if(m_props[idx].get_prop_id() == prop_id): - m_props[idx].enable_autoincrement(enable) - break - -# deletes a property and all the data from the table have the same property -func delete_prop(prop_id : int) -> void : - # print("db_table::delete_prop(" + str(prop_id) + ")") - var prop_found = false - for idx in range(0, m_props.size()): - if(m_props[idx].get_prop_id() == prop_id): - # print("Removing prop with id " + str(prop_id)) - m_props[idx].free() - m_props.remove(idx) - prop_found = true - break - - if(!prop_found): - print("ERROR: GDDBDTable::delete_prop( " + str(prop_id) + " ) - property not found !") - return - - # remove the data - # this is very ugly, but I can't erase a subarray from an array in GDscript :( - # backup what needs to be saved only - var tmp_data = [] - for idx in range(0, m_data.size()): - if(m_data[idx].get_prop_id() == prop_id): - continue - tmp_data.push_back(m_data[idx]) - # clear and restore data - m_data.clear() - for idx in range(0, tmp_data.size()): - m_data.push_back(tmp_data[idx]) - -# generates a new table id -func generate_new_prop_id() -> int : - if(m_props.size() == 0): - return 0 - - return m_props[m_props.size()-1].get_prop_id() + 1 - -# returns the properties count -func get_props_count() -> int : - return m_props.size() - -# returns the property at index or null if the index is out of bounds -func get_prop_at(idx : int) -> Object : - if(idx < 0 || idx >= m_props.size()): - print("ERROR: GDDBDTable::get_prop_id( " + str(idx) + " ) - index out of bounds; max properties: " + str(m_props.size())) - return null - - return m_props[idx] - -# returns a property by id or null if the id is not found -func get_prop_by_id(prop_id : int) -> Object : - for idx in range(0, m_props.size()): - if(m_props[idx].get_prop_id() == prop_id): - return m_props[idx] - - print("ERROR: GDDBDTable::get_prop_by_id(" + str(prop_id) + ") - property with id not found") - return null - -# returns a property by name -func get_prop_by_name(pror_name : String) -> Object : - for idx in range(0, m_props.size()): - if(m_props[idx].get_prop_name() == pror_name): - return m_props[idx] - - print("ERROR: GDDBDTable::get_prop_by_name(" + str(pror_name) + ") - property with name not found") - return null - -# adds a row with blank data -func add_blank_row() -> void : - # print("GDDBTable::add_blank_row()") - var prop_type = gddb_types.e_prop_type_bool - - for idx in range(0, m_props.size()): - var data = load(gddb_constants.c_addon_main_path + "core/db_data.gd").new() - data.set_prop_id(m_props[idx].get_prop_id()) - data.set_row_idx(m_rows_count) - - prop_type = m_props[idx].get_prop_type() - - if(prop_type == gddb_types.e_prop_type_bool): - data.set_data("0") - - elif(prop_type == gddb_types.e_prop_type_int): - if(m_props[idx].has_autoincrement()): - data.set_data(str(m_rows_count+1)) - else: - data.set_data("0") - - elif(prop_type == gddb_types.e_prop_type_float): - data.set_data("0.0") - - elif(prop_type == gddb_types.e_prop_type_string): - data.set_data("") - - elif(prop_type == gddb_types.e_prop_type_resource): - data.set_data("res://") - - elif(prop_type >= gddb_types.e_prop_types_count): - data.set_data(str(-1)) - - m_data.push_back(data) - - m_rows_count += 1 - -# adds a row with data -func add_row(data_array : Array) -> void : - if(data_array.size() != m_props.size()): - print("ERROR: GDDBDTable::add_row( " + str(data_array) + " ) - cannot add row; properties count = " + str(m_props.size()) + " and data size = " + str(data_array.size())) - return - - var prop_type = gddb_types.e_prop_type_bool - var the_data = "" - - for idx in range(0, m_props.size()): - var data = load(gddb_constants.c_addon_main_path + "core/db_data.gd").new() - # print("adding data: [" + str(m_props[idx].get_prop_id()) + ", " + data_array[idx] + "]") - # print("setting prop id: " + str(m_props[idx].get_prop_id())) - - data.set_prop_id(m_props[idx].get_prop_id()) - data.set_row_idx(m_rows_count) - - # ignore data if the property has autoincrement option - if(m_props[idx].has_autoincrement()): - data.set_data(str(m_rows_count+1)) - m_data.push_back(data) - continue - - prop_type = m_props[idx].get_prop_type() - the_data = data_array[idx] - - if(prop_type == gddb_types.e_prop_type_bool): - data.set_data(str(the_data)) - elif(prop_type == gddb_types.e_prop_type_int): - data.set_data(str(the_data)) - elif(prop_type == gddb_types.e_prop_type_float): - data.set_data(str(the_data)) - elif(prop_type == gddb_types.e_prop_type_string): - data.set_data(gddb_globals.handle_string(the_data)) - elif(prop_type == gddb_types.e_prop_type_resource): - data.set_data(the_data) - else: - print("ERROR: data type doesn't exist - " + str(m_props[idx].get_prop_type())) - - m_data.push_back(data) - - m_rows_count += 1 - -# removes a row -func remove_row(row_idx : int) -> void : - for idx in range(m_data.size()-1, 0, -1): - if(m_data[idx].get_row_idx() == row_idx): - m_data[idx].remove(idx) - m_rows_count -= 1 - -# returns the rows count -func get_rows_count() -> int : - return m_rows_count - -# edits the data -func edit_data(prop_id : int, row_idx : int, data : String) -> void : - # print("#1: GDDBTable::edit_data( " + str(prop_id) + ", " + str(row_idx) + ", " + data + " )") - for idx in range(0, m_data.size()): - # print("checking ( " + str(m_data[idx].get_row_idx()) + ", " + str(m_data[idx].get_prop_id()) + " )") - if(m_data[idx].get_row_idx() == row_idx && m_data[idx].get_prop_id() == prop_id): - # print("#2: GDDBTable::edit_data( " + str(prop_id) + ", " + str(row_idx) + ", " + data + " )") - m_data[idx].set_data(data) - return - - print("ERROR: GDDBDTable::edit_data(" + str(prop_id) + ", " + str(row_idx) + ", " + data + ") - can't find data to edit") - -# edits the data -func edit_data_by_prop_name(prop_name : String, row_idx : int, data : String) -> void : - # print("#1: GDDBTable::edit_data( " + str(prop_id) + ", " + str(row_idx) + ", " + data + " )") - - var prop_id = get_prop_by_name(prop_name).get_prop_id() - edit_data(prop_id, row_idx, data) - -# returns data count -func get_data_size() -> int : - return m_data.size() - -# returns all data -func get_all_data() -> Array : - return m_data - -# returns the data at index -func get_data_at(idx : int) -> String : - if(idx < 0 || idx >= m_data.size()): - print("ERROR: GDDBDTable::get_data_at( " + str(idx) + ") - max data size: " + str(m_data.size())) - return "" - - return m_data[idx].get_data() - -# returns a dictionary containing the data at index -func get_dictionary_at(idx : int) -> Dictionary : - var dict = {} - - if(idx < 0 || idx >= m_data.size()): - print("ERROR: GDDBDTable::get_data_at( " + str(idx) + ") - max data size: " + str(m_data.size())) - return dict - - var prop_idx = idx % m_props.size() - var data_prop_name = m_props[prop_idx].get_prop_name() - dict[data_prop_name] = m_data[idx].get_data() - - return dict - -# returns the data by a property id and a row index -func get_data(prop_id : int, row_idx : int) -> String : - for idx in range(m_data.size()-1, 0, -1): - if(m_data[idx].get_row_idx() == row_idx && m_data[idx].get_prop_id() == prop_id): - return m_data[idx].get_data() - - print("ERROR: GDDBDTable::get_data(" + str(prop_id) + ", " + str(row_idx) + ")") - return "" - -# returns a dictionary containing the data by a property id and a row index -func get_dictionary(prop_id : int, row_idx : int) -> Dictionary : - var dict = {} - - for idx in range(m_data.size()-1, 0, -1): - if(m_data[idx].get_row_idx() == row_idx && m_data[idx].get_prop_id() == prop_id): - var prop = get_prop_by_id(prop_id) - var data_prop_name = prop.get_prop_name() - dict[data_prop_name] = m_data[idx].get_data() - return dict - - print("ERROR: GDDBDTable::get_data(" + str(prop_id) + ", " + str(row_idx) + ")") - return dict - -# returns an array of data at row index -func get_data_at_row_idx(row_idx : int) -> Array : - var data = [] - for idx in range(0, m_data.size()): - if(m_data[idx].get_row_idx() == row_idx): - data.push_back(m_data[idx]) - - if(data.size() == -1): - print("ERROR: GDDBDTable::get_data_at_row_idx(" + str(row_idx) + ") - invalid row index") - - return data - -# returns a dictionary of data at row index -func get_dictionary_at_row_idx(row_idx : int) -> Dictionary : - var dict = {} - - var prop_idx = 0 - for idx in range(0, m_data.size()): - if(m_data[idx].get_row_idx() == row_idx): - var data_prop_name = m_props[prop_idx].get_prop_name() - dict[data_prop_name] = m_data[idx].get_data() - prop_idx += 1 - - if(dict.is_empty()): - print("ERROR: GDDBDTable::get_data_at_row_idx(" + str(row_idx) + ") - invalid row index") - - return dict - -# returns an array of data filtered by property id -func get_data_by_prop_id(prop_id : int, data_filter : int = gddb_types.e_data_filter_equal) -> Array : - var data = [] - - if(data_filter < gddb_types.e_data_filter_equal || data_filter >= gddb_types.e_data_filter_gequal): - print("ERROR: cannot process filter " + str(data_filter)) - return data - - for idx in range(0, m_data.size()): - if(data_filter == gddb_types.e_data_filter_equal): - if(m_data[idx].get_prop_id() == prop_id): - data.push_back(m_data[idx]) - - elif(data_filter == gddb_types.e_data_filter_not_equal): - if(m_data[idx].get_prop_id() != prop_id): - data.push_back(m_data[idx]) - - elif(data_filter == gddb_types.e_data_filter_less): - if(m_data[idx].get_prop_id() < prop_id): - data.push_back(m_data[idx]) - - elif(data_filter == gddb_types.e_data_filter_greater): - if(m_data[idx].get_prop_id() > prop_id): - data.push_back(m_data[idx]) - - elif(data_filter == gddb_types.e_data_filter_lequal): - if(m_data[idx].get_prop_id() <= prop_id): - data.push_back(m_data[idx]) - - elif(data_filter == gddb_types.e_data_filter_gequal): - if(m_data[idx].get_prop_id() >= prop_id): - data.push_back(m_data[idx]) - - if(data.size() == 0): - print("ERROR: GDDBDTable::get_data_by_prop_id(" + str(prop_id) + ", " + gddb_globals.get_data_filter_name(data_filter) + ") - filtered data not found") - - return data - -# returns an array of data by property name -func get_data_by_prop_name(prop_name : String) -> Array : - var prop_id = -1 - for idx in range(0, m_props.size()): - if(m_props[idx].get_prop_name() == prop_name): - prop_id = m_props[idx].get_prop_id() - break - - if(prop_id == -1): - print("ERROR: GDDBDTable::get_data_by_prop_name(" + prop_name + ") - property not found") - return [] - - return get_data_by_prop_id(prop_id) - -# returns an array of data filtered by data -# filters "<", ">", "<=" and ">=" are working for integer and float data types -func get_data_by_data(data_value : String, data_filter : int = gddb_types.e_data_filter_equal) -> Array : - var data = [] - - if(data_filter < gddb_types.e_data_filter_equal || data_filter >= gddb_types.e_data_filter_gequal): - print("ERROR: cannot process filter " + str(data_filter)) - return data - - for idx in range(0, m_data.size()): - if(data_filter == gddb_types.e_data_filter_equal): - if(m_data[idx].get_data() == data_value): - data.push_back(m_data[idx]) - - elif(data_filter == gddb_types.e_data_filter_not_equal): - if(m_data[idx].get_data() != data_value): - data.push_back(m_data[idx]) - - elif(data_filter == gddb_types.e_data_filter_less): - if(m_props[idx].get_prop_type() < gddb_types.e_prop_type_resource): - if(m_data[idx].get_data() < data_value): - data.push_back(m_data[idx]) - - elif(data_filter == gddb_types.e_data_filter_greater): - if(m_props[idx].get_prop_type() < gddb_types.e_prop_type_resource): - if(m_data[idx].get_data() > data_value): - data.push_back(m_data[idx]) - - elif(data_filter == gddb_types.e_data_filter_lequal): - if(m_props[idx].get_prop_type() < gddb_types.e_prop_type_resource): - if(m_data[idx].get_data() <= data_value): - data.push_back(m_data[idx]) - - elif(data_filter == gddb_types.e_data_filter_gequal): - if(m_props[idx].get_prop_type() < gddb_types.e_prop_type_resource): - if(m_data[idx].get_data() >= data_value): - data.push_back(m_data[idx]) - - if(data.size() == 0): - print("ERROR: GDDBDTable::get_data_by_data(" + data_value + ", " + gddb_globals.get_data_filter_name(data_filter) + ") - filtered data not found") - - return data - -# returns an array of data by a property name and a data value -# similar to: select * from users where user_id = 1 -func get_data_by_prop_name_and_data(prop_name : String, data_value : String) -> Array : - var the_array = [] - - var prop_idx = -1 - for idx in range(0, m_props.size()): - if(m_props[idx].get_prop_name() == prop_name): - prop_idx = idx - break - - for idx in range(0, m_rows_count): - var row_data = get_data_at_row_idx(idx) - # print("Comparing row: " + row_data[0].get_data() + ", " + row_data[1].get_data() + ", " + row_data[2].get_data() + ", " + row_data[3].get_data() + ", " + row_data[4].get_data()) - if(row_data[prop_idx].get_data() == data_value): - var dict = get_dictionary_at_row_idx(idx) - the_array.push_back(row_data) - - return the_array - -# returns a dictionary of data by a property name and a data value -# similar to: select * from users where user_id = 1 -func get_dictionary_by_prop_name_and_data(prop_name : String, data_value : String) -> Array : - var the_array = [] - - var prop_idx = -1 - for idx in range(0, m_props.size()): - if(m_props[idx].get_prop_name() == prop_name): - prop_idx = idx - break - - for idx in range(0, m_rows_count): - var row_data = get_data_at_row_idx(idx) - # print("Comparing row: " + row_data[0].get_data() + ", " + row_data[1].get_data() + ", " + row_data[2].get_data() + ", " + row_data[3].get_data() + ", " + row_data[4].get_data()) - if(row_data[prop_idx].get_data() == data_value): - var dict = get_dictionary_at_row_idx(idx) - the_array.push_back(get_dictionary_at_row_idx(idx)) - - return the_array - -# clears the table's structure and data -func clear() -> void : - # clear data - clear_data() - - # clear properties - for idx in range(0, m_props.size()): - m_props[idx].free() - m_props.clear() - -# clears the table's data -func clear_data() -> void : - for idx in range(0, m_data.size()): - m_data[idx].free() - m_data.clear() - m_rows_count = 0 - -# dumps the table -func dump() -> String : - var dump_text = "Table dump. id: " + str(m_table_id) + ", name: " + m_table_name + ", props_count: " + str(m_props.size()) + ", rows_count: " + str(m_rows_count) - dump_text += "\n------------------------------------------------------------------------------------\nProperties:" - - for idx in range(0, m_props.size()): - dump_text += "\n" + m_props[idx].dump() - - dump_text += "\n------------------------------------------------------------------------------------\nData:\n" - - for idx in range(0, m_rows_count): - var tmp_text = "row_idx: " + "%" + str(gddb_globals.get_digits_count(m_rows_count)) + "d" - dump_text += tmp_text % idx - var row = get_data_at_row_idx(idx) - for jdx in range(0, row.size()): - dump_text += " | " + row[jdx].get_data() - dump_text += "\n" - - return dump_text diff --git a/addons/godot_db_manager/data_label.gd b/addons/godot_db_manager/data_label.gd deleted file mode 100644 index 58992ca..0000000 --- a/addons/godot_db_manager/data_label.gd +++ /dev/null @@ -1,59 +0,0 @@ -@tool -""" -class GDDBDataLabel -""" - -class_name GDDBDataLabel - -extends Label - -signal resize_property - -var m_prop_id : int = gddb_constants.c_invalid_id -var m_prop_type : int = gddb_constants.c_invalid_id - -var m_mouse_pos_pressed : Vector2 = Vector2() -var m_mouse_pressed : bool = false - -# Called when the node enters the scene tree for the first time. -func _ready() -> void: - set_custom_minimum_size(Vector2(150.0, 24.0)) - -# called when the node gets an input -func _input(event : InputEvent) -> void : - if(!gddb_globals.is_interface_active()): - return - - var evLocal = $resize_ctrl.make_input_local(event) - - if event is InputEventMouseButton : - if(event.button_index == MOUSE_BUTTON_LEFT): - if(event.pressed): - var rect = Rect2(Vector2(0, 0), $resize_ctrl.get_size()) - var inside = rect.has_point(evLocal.position) - if(inside): - m_mouse_pressed = true - m_mouse_pos_pressed = evLocal.position - else: - m_mouse_pressed = false - - elif event is InputEventMouseMotion : - if(m_mouse_pressed): - var diff_x = evLocal.position.x - m_mouse_pos_pressed.x - emit_signal("resize_property", m_prop_id, diff_x) - -# sets property id -func set_prop_id(id : int) -> void: - m_prop_id = id - -# returns property id -func get_prop_id() -> int: - return m_prop_id - -# sets property type -func set_prop_type(prop_type : int) -> void : - m_prop_type = prop_type - -# returns property type -func get_prop_type() -> int : - return m_prop_type diff --git a/addons/godot_db_manager/data_label.tscn b/addons/godot_db_manager/data_label.tscn deleted file mode 100644 index c5045a2..0000000 --- a/addons/godot_db_manager/data_label.tscn +++ /dev/null @@ -1,44 +0,0 @@ -[gd_scene load_steps=5 format=2] - -[ext_resource path="res://addons/godot_db_manager/data_label.gd" type="Script" id=1] -[ext_resource path="res://addons/godot_db_manager/assets/fnt/roboto_18.tres" type="FontFile" id=2] -[ext_resource path="res://addons/godot_db_manager/assets/tex/gui.png" type="Texture2D" id=3] -[ext_resource path="res://addons/godot_db_manager/assets/tex/debug.png" type="Texture2D" id=4] - -[node name="lbl" type="Label"] -anchor_right = 0.167 -offset_right = -0.300018 -offset_bottom = 11.52 -custom_minimum_size = Vector2( 150, 32 ) -mouse_filter = 0 -theme_override_fonts/font = ExtResource( 2 ) -text = "ID" -valign = 1 -script = ExtResource( 1 ) -__meta__ = { -"_edit_horizontal_guides_": [ 24.0 ], -"_edit_use_anchors_": true -} - -[node name="dbg" type="NinePatchRect" parent="."] -visible = false -anchor_right = 1.0 -anchor_bottom = 1.0 -texture = ExtResource( 4 ) -region_rect = Rect2( 86, 26, 10, 10 ) -__meta__ = { -"_edit_use_anchors_": false -} - -[node name="resize_ctrl" type="NinePatchRect" parent="."] -anchor_left = 1.0 -anchor_right = 1.0 -anchor_bottom = 1.0 -offset_left = -3.0 -mouse_filter = 0 -mouse_default_cursor_shape = 10 -texture = ExtResource( 3 ) -region_rect = Rect2( 80, 12, 24, 24 ) -__meta__ = { -"_edit_use_anchors_": false -} diff --git a/addons/godot_db_manager/db_editor.gd b/addons/godot_db_manager/db_editor.gd deleted file mode 100644 index 94e8b27..0000000 --- a/addons/godot_db_manager/db_editor.gd +++ /dev/null @@ -1,210 +0,0 @@ -@tool -""" -class GDDBEditor -""" - -class_name GDDBEditor - -extends TabBar - -var m_name = "" -var m_database = null - -var m_filepath = "" - -# Called when the node enters the scene tree for the first time. -func _ready() -> void : - set_tab_alignment(TabBar.ALIGN_LEFT) - set_tab_close_display_policy(TabBar.CLOSE_BUTTON_SHOW_ALWAYS) - - $tables_list.connect("resize_tables_list", Callable(self, "on_resize_the_table_list")) - $tables_list.connect("add_table", Callable(self, "on_add_table")) - $tables_list.connect("edit_table_name", Callable(self, "on_edit_table")) - $tables_list.connect("delete_table", Callable(self, "on_delete_table")) - $tables_list.connect("select_table", Callable(self, "on_select_table")) - - $table_editor.connect("set_dirty", Callable(self, "on_set_dirty")) - - $delete_table_dlg.connect("delete_table", Callable(self, "on_confirm_delete_table")) - - $new_table_dlg.connect("cancel_dialog", Callable(self, "on_close_new_table_dlg")) - $new_table_dlg.get_close_button().connect("pressed", Callable(self, "on_close_new_table_dlg")) - - $error_dlg.connect("confirmed", Callable(self, "on_retry_create_table")) - -# resizing the tables list -func on_resize_the_table_list(diff_x : float) -> void : - var size = $tables_list.get_size() - var min_size = $tables_list.get_custom_minimum_size() - size.x += diff_x - - if(size.x < min_size.x): - return - - if(size.x > gddb_constants.c_max_tables_list_width): - return - - $tables_list.resize_content(size) - - var pos = $table_editor.get_position() - size = $table_editor.get_size() - pos.x += diff_x - size.x -= diff_x - $table_editor.set_position(pos) - $table_editor.set_size(size) - -# overrides the member from base class -func set_name(ctrl_name) -> void : - m_name = ctrl_name - super.set_name(ctrl_name) - -# sets the database; for easy access -func set_database(db) -> void : - # print("GDDBEditor::set_database(" + db.get_db_name() + ") to " + get_name()) - m_database = db - set_dirty(true) - - var tables_count = db.get_tables_count() - - $table_editor.hide() - - for idx in range(0, tables_count): - var table = db.get_table_at(idx) - $tables_list.create_table(table, idx == 0) - if(idx == 0): - $table_editor.set_table(table) - $table_editor.link_props() - $table_editor.show() - -# returns the database id -func get_db_id() -> int : - if(null == m_database): - return gddb_constants.c_invalid_id - return m_database.get_db_id() - -# returns the database name -func get_db_name() -> String : - if(null == m_database): - return "" - return m_database.get_db_name() - -# sets the database to be dirty; should be saved -func set_dirty(dirty) -> void : - if(dirty): - var title = m_name + "*" - super.set_name(title) - else: - super.set_name(m_name) - -# called when the user presses the "add_table" button from the "tables_list/tables_header" -func on_add_table() -> void : - # print("GDDBEditor::on_add_table()") - $new_table_dlg.set_dld_type(gddb_types.e_new_dlg_type_new) - $new_table_dlg.set_table_id(gddb_constants.c_invalid_id) - $new_table_dlg.set_init_name("") - $new_table_dlg.connect("create_new_table", Callable(self, "on_create_table")) - $new_table_dlg.popup_centered() - -# called when the user accepts the name of the table in the "new_table_dlg" -func on_create_table(table_name : String) -> void : - # print("GDDBEditor::on_create_table(" + table_name + ")") - $new_table_dlg.disconnect("create_new_table", Callable(self, "on_create_table")) - var table_id = m_database.add_table(table_name) - if(table_id == gddb_constants.c_invalid_id): - $error_dlg.set_text("Table with the name \"" + table_name + "\" already exists" ) - $error_dlg.popup_centered() - return - var table = m_database.get_table_by_id(table_id) - $tables_list.create_table(table) - $table_editor.set_table(table) - $table_editor.show() - m_database.set_dirty(true) - set_dirty(true) - -# called when the user retryes to create a table (changed the name) -func on_retry_create_table() -> void : - $new_table_dlg.set_init_name("") - $new_table_dlg.popup_centered() - -# called when the user presses the "edit_table_name" from the "tables/list/table" -func on_edit_table(table_id : int, table_name : String) -> void : - # print("GDDBEditor::on_edit_table(" + str(table_id) + ", " + table_name + ")") - $new_table_dlg.set_dld_type(gddb_types.e_new_dlg_type_edit) - $new_table_dlg.set_table_id(table_id) - $new_table_dlg.set_init_name(table_name) - $new_table_dlg.connect("create_new_table", Callable(self, "on_table_name_edited")) - $new_table_dlg.popup_centered() - -# gets called when canceling the new_table_dlg -func on_close_new_table_dlg() -> void : - # print("GDDBEditor::on_close_new_table_dlg()") - var dlg_type = $new_table_dlg.get_dlg_type() - if(dlg_type == gddb_types.e_new_dlg_type_new): - $new_table_dlg.disconnect("create_new_table", Callable(self, "on_create_table")) - elif(dlg_type == gddb_types.e_new_dlg_type_edit): - $new_table_dlg.disconnect("create_new_table", Callable(self, "on_table_name_edited")) - -# called when the user presses the "delete_table" from the "tables/list/table" -func on_delete_table(table_id : int) -> void : - # print("GDDBEditor::on_delete_table(" + str(table_id) + ")") - var table = m_database.get_table_by_id(table_id) - $delete_table_dlg.set_table_id(table_id) - $delete_table_dlg.set_table_name(table.get_table_name()) - $delete_table_dlg.popup_centered() - -# called when the user accepts the name of the table in the "new_table_dlg" -func on_table_name_edited(table_name : String) -> void : - # print("GDDBEditor::on_table_name_edited(" + table_name + ")") - $new_table_dlg.disconnect("create_new_table", Callable(self, "on_table_name_edited")) - var table_id = $new_table_dlg.get_table_id() - if(!m_database.edit_table_name(table_name, table_id)): - $error_dlg.set_text("Table with the name \"" + table_name + "\" already exists" ) - $error_dlg.popup_centered() - return - # print("GDDBEditor::on_table_name_edited(" + str(table_id) + ", " + table_name + ")") - $tables_list.edit_table_name(table_id, table_name) - m_database.set_dirty(true) - set_dirty(true) - -# called when the user confirms to delete a table -func on_confirm_delete_table() -> void : - # print("GDDBEditor::on_confirm_delete_table()") - var table_id = $delete_table_dlg.get_table_id() - var selected_table = $tables_list.get_selected_item() - if(null == selected_table): - return - var selected_table_id = selected_table.get_table_id() - m_database.delete_table_by_id(table_id) - $tables_list.delete_table(table_id) - if(selected_table_id == table_id): - $tables_list.select_item_at(0) - var table = m_database.get_table_at(0) - $table_editor.set_table(table) - - m_database.set_dirty(true) - set_dirty(true) - -# called when the user selects a table from the table_list -func on_select_table(table_id : int) -> void : - var table = m_database.get_table_by_id(table_id) - if(null != table): - $table_editor.set_table(table) - $table_editor.link_props() - -# saves current database -func save_database() -> void: - m_database.save_db() - set_dirty(false) - -# returns true if the database can be saved, otherwise false -func can_save_database() -> bool: - return !m_database.get_db_filepath().is_empty() - -# sets the database's path -func set_database_filepath(filepath : String) -> void: - m_database.set_db_filepath(filepath) - -# called when a table is modified -func on_set_dirty() -> void: - m_database.set_dirty(true) - set_dirty(true) diff --git a/addons/godot_db_manager/db_editor.tscn b/addons/godot_db_manager/db_editor.tscn deleted file mode 100644 index bf68af3..0000000 --- a/addons/godot_db_manager/db_editor.tscn +++ /dev/null @@ -1,40 +0,0 @@ -[gd_scene load_steps=8 format=2] - -[ext_resource path="res://addons/godot_db_manager/db_editor.gd" type="Script" id=1] -[ext_resource path="res://addons/godot_db_manager/tables_list.tscn" type="PackedScene" id=2] -[ext_resource path="res://addons/godot_db_manager/dlgs/new_table_dlg.tscn" type="PackedScene" id=3] -[ext_resource path="res://addons/godot_db_manager/table_editor.tscn" type="PackedScene" id=4] -[ext_resource path="res://addons/godot_db_manager/dlgs/error_dlg.tscn" type="PackedScene" id=5] -[ext_resource path="res://addons/godot_db_manager/dlgs/delete_table_dlg.tscn" type="PackedScene" id=6] -[ext_resource path="res://addons/godot_db_manager/debug/dbg.tscn" type="PackedScene" id=7] - -[node name="db_editor" type="TabBar"] -anchor_right = 1.0 -anchor_bottom = 1.0 -tab_alignment = 0 -tab_close_display_policy = 2 -script = ExtResource( 1 ) -__meta__ = { -"_edit_use_anchors_": true, -"_edit_vertical_guides_": [ 170.0, 900.0 ] -} - -[node name="tables_list" parent="." instance=ExtResource( 2 )] -anchor_right = 0.0 -offset_right = 170.0 -custom_minimum_size = Vector2( 180, 30 ) - -[node name="table_editor" parent="." instance=ExtResource( 4 )] -offset_left = 181.0 - -[node name="dbg" parent="." instance=ExtResource( 7 )] -visible = false - -[node name="new_table_dlg" parent="." instance=ExtResource( 3 )] -visible = false - -[node name="delete_table_dlg" parent="." instance=ExtResource( 6 )] -visible = false - -[node name="error_dlg" parent="." instance=ExtResource( 5 )] -visible = false diff --git a/addons/godot_db_manager/db_interface.gd b/addons/godot_db_manager/db_interface.gd deleted file mode 100644 index fc1d2e6..0000000 --- a/addons/godot_db_manager/db_interface.gd +++ /dev/null @@ -1,155 +0,0 @@ -@tool -""" -class GDDBInterface -""" - -class_name GDDBInterface - -extends Control - -var m_db_manager = null - -# Called when the node enters the scene tree for the first time. -func _ready() -> void: - # init db_manager - m_db_manager = load(gddb_constants.c_addon_main_path + "core/db_man.gd").new() - - # menu connections - $dlg/menu.connect("new_database", Callable(self, "on_menu_new_database")) - $dlg/menu.connect("load_database", Callable(self, "on_menu_load_database")) - $dlg/menu.connect("save_database", Callable(self, "on_menu_save_database")) - $dlg/menu.connect("save_database_as", Callable(self, "on_menu_save_database_as")) - - # dialod notifications - $dlg.connect("about_to_popup", Callable(self, "on_about_to_show")) - $dlg.get_close_button().connect("pressed", Callable(self, "on_close")) - - # new database connections - $dlg/new_db_dlg.connect("create_new_db", Callable(self, "on_new_database")) - - # save / load connections - $dlg/load_db_dlg.connect("file_selected", Callable(self, "on_file_selected")) - -# Called when the node is about to be shown. -func on_about_to_show() -> void : - gddb_globals.set_interface_active(true) - -func on_close() -> void : - gddb_globals.set_interface_active(false) - -# called when creating a new database from the menu -func on_menu_new_database() -> void: - $dlg/new_db_dlg/v_layout/db_info/db_edt.set_text("") - $dlg/new_db_dlg.popup_centered() - -# called when loading a database from the menu -func on_menu_load_database() -> void: - $dlg/load_db_dlg.set_mode(FileDialog.FILE_MODE_OPEN_FILE) - $dlg/load_db_dlg.set_title("Load Database ...") - $dlg/load_db_dlg.set_current_file("") - $dlg/load_db_dlg.popup_centered() - -# called when saving a database from the menu -func on_menu_save_database() -> void: - # print("on_menu_save_database") - var currnet_tab = $dlg/databases.get_current_tab_control() - if(currnet_tab.can_save_database()): - currnet_tab.save_database() - else: - on_menu_save_database_as() - -# called when saving a database as another from the menu -func on_menu_save_database_as(): - # print("on_menu_save_database_as") - $dlg/load_db_dlg.set_mode(FileDialog.FILE_MODE_SAVE_FILE) - $dlg/load_db_dlg.set_title("Save Database As ...") - - var currnet_tab = $dlg/databases.get_current_tab_control() - - $dlg/load_db_dlg.set_current_file(currnet_tab.get_db_name()) - $dlg/load_db_dlg.popup_centered() - -# called when adding a new database -func on_new_database(db_name : String) -> void: - var tmp_name = db_name.to_lower() - var db_id = m_db_manager.add_database(db_name) - if(db_id == gddb_constants.c_invalid_id): - $dlg/error_dlg.set_text("Database with name \"" + db_name + "\" already exists") - $dlg/error_dlg.popup_centered() - return - - var db = m_db_manager.get_db_by_id(db_id) - # print("new DB added: " + str(db)) - - var db_editor = load(gddb_constants.c_addon_main_path + "db_editor.tscn").instantiate() - $dlg/databases.add_child(db_editor) - db_editor.set_name(db_name) - db.set_dirty(true) - db_editor.set_database(db) - - $dlg/menu.enable_file_save(true) - $dlg/menu.enable_file_save_as(true) - - $dlg/new_db_dlg.hide() - -# called when selecting a file from save / load dialog -func on_file_selected(filepath : String) -> void: - # print("GDDBInterface::on_file_selected(" + filepath + ")") - - if($dlg/load_db_dlg.get_mode() == FileDialog.FILE_MODE_SAVE_FILE): - var filepath_low = filepath.to_lower() - - # check for the file extension - if(!filepath_low.ends_with(".json")): - $dlg/error_dlg.set_text("The extension of the file must be \".json\"") - $dlg/error_dlg.popup_centered() - return - - var current_filename = $dlg/load_db_dlg.get_current_file().to_lower() - var filename_dot = current_filename.length() - 5 - current_filename.erase(filename_dot, 5) - - # check the filename - if(!gddb_globals.check_db_name(current_filename)): - $dlg/error_dlg.set_text("Invalid characters in the database filename.\n\nThe filename cannot contain any of these characters: " + gddb_constants.c_invalid_characters) - $dlg/error_dlg.popup_centered() - return - - save_database_as(filepath) - - elif($dlg/load_db_dlg.get_mode() == FileDialog.FILE_MODE_OPEN_FILE): - load_database(filepath) - -# saves a database to a given file path -func save_database_as(filepath : String) -> void: - # print("GDDBInterface::save_database_as(" + filepath + ")") - var currnet_tab = $dlg/databases.get_current_tab_control() - currnet_tab.set_database_filepath(filepath) - currnet_tab.save_database() - -func load_database(filepath : String) -> void: - var db_id = m_db_manager.load_database(filepath) - - if(db_id == gddb_types.e_db_invalid_file): - $dlg/error_dlg.set_text("Invalid database") - $dlg/error_dlg.popup_centered() - return - - if(db_id == gddb_types.e_db_invalid_ver): - $dlg/error_dlg.set_text("Wrong database version. Currently is: " + gddb_constants.c_gddb_ver) - $dlg/error_dlg.popup_centered() - return - - var db = m_db_manager.get_db_by_id(db_id) - db.set_dirty(false) - # print("new DB added: " + str(db)) - - var db_editor = load(gddb_constants.c_addon_main_path + "db_editor.tscn").instantiate() - $dlg/databases.add_child(db_editor) - db_editor.set_name(db.get_db_name()) - db_editor.set_database(db) - - $dlg/menu.enable_file_save(true) - $dlg/menu.enable_file_save_as(true) - - db_editor.set_dirty(false) diff --git a/addons/godot_db_manager/db_interface.tscn b/addons/godot_db_manager/db_interface.tscn deleted file mode 100644 index 02e46d2..0000000 --- a/addons/godot_db_manager/db_interface.tscn +++ /dev/null @@ -1,65 +0,0 @@ -[gd_scene load_steps=7 format=2] - -[ext_resource path="res://addons/godot_db_manager/debug/dbg.tscn" type="PackedScene" id=1] -[ext_resource path="res://addons/godot_db_manager/assets/fnt/roboto_18.tres" type="FontFile" id=2] -[ext_resource path="res://addons/godot_db_manager/dlgs/error_dlg.tscn" type="PackedScene" id=3] -[ext_resource path="res://addons/godot_db_manager/db_interface.gd" type="Script" id=4] -[ext_resource path="res://addons/godot_db_manager/dlgs/new_db_dlg.tscn" type="PackedScene" id=5] -[ext_resource path="res://addons/godot_db_manager/menu.tscn" type="PackedScene" id=7] - -[node name="db_interface" type="Control"] -script = ExtResource( 4 ) -__meta__ = { -"_edit_horizontal_guides_": [ 30.0, 674.0 ], -"_edit_use_anchors_": false -} - -[node name="dlg" type="Window" parent="."] -visible = true -offset_right = 1147.0 -offset_bottom = 768.0 -exclusive = true -window_title = "Godot Database Manager" -resizable = true -__meta__ = { -"_edit_use_anchors_": false -} - -[node name="menu" parent="dlg" instance=ExtResource( 7 )] - -[node name="databases" type="TabContainer" parent="dlg"] -anchor_right = 1.0 -anchor_bottom = 1.0 -offset_top = 30.0 -theme_override_fonts/font = ExtResource( 2 ) -tab_alignment = 0 -drag_to_rearrange_enabled = true -__meta__ = { -"_edit_use_anchors_": false -} - -[node name="new_db_dlg" parent="dlg" instance=ExtResource( 5 )] -visible = false -offset_right = 3.832 - -[node name="error_dlg" parent="dlg" instance=ExtResource( 3 )] -visible = false -anchor_right = 0.187 -anchor_bottom = 0.136 -offset_right = 0.469986 -offset_bottom = -0.0800018 - -[node name="load_db_dlg" type="FileDialog" parent="dlg"] -anchor_right = 0.349 -anchor_bottom = 0.418 -offset_right = -0.309998 -offset_bottom = 0.209991 -window_title = "Open a File" -mode = 0 -filters = PackedStringArray( "*.json" ) -__meta__ = { -"_edit_use_anchors_": false -} - -[node name="dbg" parent="dlg" instance=ExtResource( 1 )] -visible = false diff --git a/addons/godot_db_manager/db_manager.gd b/addons/godot_db_manager/db_manager.gd deleted file mode 100644 index 9e31374..0000000 --- a/addons/godot_db_manager/db_manager.gd +++ /dev/null @@ -1,33 +0,0 @@ -@tool -""" -class GDDBManager -""" - -class_name GDDBManager - -extends EditorPlugin - -var DBInterface = preload("./db_interface.tscn") -var db_interface : Node - -func _enter_tree(): - add_autoload_singleton("gddb_constants", "res://addons/godot_db_manager/core/GDDBConstants.gd") - add_autoload_singleton("gddb_types", "res://addons/godot_db_manager/core/GDDBTypes.gd") - add_autoload_singleton("gddb_globals", "res://addons/godot_db_manager/core/GDDBGlobals.gd") - - # Initialization of the plugin goes here - db_interface = DBInterface.instantiate() - - get_editor_interface().get_base_control().add_child(db_interface) - add_tool_menu_item("Godot Database Manager", self, "open_config") - -func _exit_tree(): - # Clean-up of the plugin goes here - remove_tool_menu_item("Godot Database Manager") - if(db_interface): - db_interface.queue_free() - -func open_config(UD): - var window = db_interface.get_node("dlg") as Window - if(window): - window.popup_centered() diff --git a/addons/godot_db_manager/debug/dbg.tscn b/addons/godot_db_manager/debug/dbg.tscn deleted file mode 100644 index db539e9..0000000 --- a/addons/godot_db_manager/debug/dbg.tscn +++ /dev/null @@ -1,79 +0,0 @@ -[gd_scene load_steps=2 format=2] - -[ext_resource path="res://addons/godot_db_manager/assets/tex/debug.png" type="Texture2D" id=1] - -[node name="dbg" type="Control"] -anchor_right = 1.0 -anchor_bottom = 1.0 -mouse_filter = 2 -__meta__ = { -"_edit_use_anchors_": false -} - -[node name="1" type="Control" parent="."] -offset_right = 40.0 -offset_bottom = 40.0 -mouse_filter = 2 -__meta__ = { -"_edit_use_anchors_": false -} - -[node name="Sprite2D" type="Sprite2D" parent="1"] -scale = Vector2( 4, 4 ) -texture = ExtResource( 1 ) -centered = false -region_enabled = true -region_rect = Rect2( 2, 50, 10, 10 ) - -[node name="2" type="Control" parent="."] -anchor_left = 1.0 -anchor_right = 1.0 -offset_left = -40.0 -offset_bottom = 40.0 -mouse_filter = 2 -__meta__ = { -"_edit_use_anchors_": false -} - -[node name="Sprite2D" type="Sprite2D" parent="2"] -scale = Vector2( 4, 4 ) -texture = ExtResource( 1 ) -centered = false -region_enabled = true -region_rect = Rect2( 2, 50, 10, 10 ) - -[node name="3" type="Control" parent="."] -anchor_top = 1.0 -anchor_bottom = 1.0 -offset_top = -40.0 -offset_right = 40.0 -mouse_filter = 2 -__meta__ = { -"_edit_use_anchors_": false -} - -[node name="Sprite2D" type="Sprite2D" parent="3"] -scale = Vector2( 4, 4 ) -texture = ExtResource( 1 ) -centered = false -region_enabled = true -region_rect = Rect2( 2, 50, 10, 10 ) - -[node name="4" type="Control" parent="."] -anchor_left = 1.0 -anchor_top = 1.0 -anchor_right = 1.0 -anchor_bottom = 1.0 -offset_left = -40.0 -offset_top = -40.0 -mouse_filter = 2 -__meta__ = { -"_edit_use_anchors_": false -} - -[node name="Sprite2D" type="Sprite2D" parent="4"] -scale = Vector2( 4, 4 ) -texture = ExtResource( 1 ) -centered = false -region_enabled = true -region_rect = Rect2( 2, 50, 10, 10 ) diff --git a/addons/godot_db_manager/dlgs/data_dlg.gd b/addons/godot_db_manager/dlgs/data_dlg.gd deleted file mode 100644 index 8d3b681..0000000 --- a/addons/godot_db_manager/dlgs/data_dlg.gd +++ /dev/null @@ -1,61 +0,0 @@ -@tool -""" -class GDDBDataPanel -""" - -class_name GDDBDataPanel - -extends PopupPanel - -signal select_data - -var m_prop_id = gddb_constants.c_invalid_id -var m_row_idx = gddb_constants.c_invalid_id - -var m_table = null - -# Called when the node enters the scene tree for the first time. -func _ready(): - connect("about_to_popup", Callable(self, "on_about_to_show")) - $ItemList.connect("item_selected", Callable(self, "on_item_selected")) - -# sets property id -func set_prop_id(prop_id : int) -> void : - m_prop_id = prop_id - -# returns property id -func get_prop_id() -> int : - return m_prop_id - -# sets row index -func set_row_idx(row_idx : int) -> void : - m_row_idx = row_idx - -# returns row index -func get_row_idx() -> int : - return m_row_idx - -# sets the table from the database -func set_table(table) -> void: - m_table = table - -# returns the table from the database -func get_table() -> Object: - return m_table - -# called when the popup is about to show -func on_about_to_show() -> void : - $ItemList.clear() - $ItemList.add_item("None") - for idx in range(0, m_table.get_rows_count()): - var option = gddb_globals.get_json_from_row(m_table, idx) - $ItemList.add_item(option) - -# called when an item is selected -func on_item_selected(idx : int) -> void: - # the first index is "" - if(idx == 0): - emit_signal("select_data", m_prop_id, m_row_idx, -1, "{}" ) - else: - emit_signal("select_data", m_prop_id, m_row_idx, idx-1, $ItemList.get_item_text(idx) ) - hide() diff --git a/addons/godot_db_manager/dlgs/data_dlg.tscn b/addons/godot_db_manager/dlgs/data_dlg.tscn deleted file mode 100644 index d6cc3de..0000000 --- a/addons/godot_db_manager/dlgs/data_dlg.tscn +++ /dev/null @@ -1,25 +0,0 @@ -[gd_scene load_steps=3 format=2] - -[ext_resource path="res://addons/godot_db_manager/dlgs/data_dlg.gd" type="Script" id=1] -[ext_resource path="res://addons/godot_db_manager/assets/fnt/roboto_12.tres" type="FontFile" id=2] - -[node name="data_dlg" type="PopupPanel"] -anchor_right = 0.285625 -anchor_bottom = 0.351111 -script = ExtResource( 1 ) -__meta__ = { -"_edit_use_anchors_": false -} - -[node name="ItemList" type="ItemList" parent="."] -anchor_right = 1.0 -anchor_bottom = 1.0 -offset_left = 4.0 -offset_top = 4.0 -offset_right = -4.0 -offset_bottom = -4.0 -theme_override_fonts/font = ExtResource( 2 ) -items = [ "ababab", null, false, "cdcdcd", null, false, "efefef", null, false ] -__meta__ = { -"_edit_use_anchors_": false -} diff --git a/addons/godot_db_manager/dlgs/delete_prop_dlg.gd b/addons/godot_db_manager/dlgs/delete_prop_dlg.gd deleted file mode 100644 index 925e921..0000000 --- a/addons/godot_db_manager/dlgs/delete_prop_dlg.gd +++ /dev/null @@ -1,41 +0,0 @@ -@tool -""" -class GDDBDeletePropDlg -""" - -class_name GDDBDeletePropDlg - -extends Window - -signal delete_prop - -var m_prop_id = gddb_constants.c_invalid_id - -# Called when the node enters the scene tree for the first time. -func _ready() -> void : - $v_layout/buttons/ok_btn.connect("pressed", Callable(self, "on_ok_btn_pressed")) - $v_layout/buttons/cancel_btn.connect("pressed", Callable(self, "on_cancel_btn_pressed")) - -# sets the property id -func set_prop_id(prop_id : int) -> void : - m_prop_id = prop_id - -# returns the property id -func get_prop_id() -> int : - return m_prop_id - -func set_prop_name(prop_name : String) -> void: - var text = "Delete property with name \"" + prop_name + "\" ?" - $v_layout/prop_info/prop_lbl.set_text(text) - -# returns the table id -func get_table_id() -> int : - return m_prop_id - -# called when the OK button is pressed -func on_ok_btn_pressed() -> void : - emit_signal("delete_prop") - hide() - -func on_cancel_btn_pressed() -> void: - hide() diff --git a/addons/godot_db_manager/dlgs/delete_prop_dlg.tscn b/addons/godot_db_manager/dlgs/delete_prop_dlg.tscn deleted file mode 100644 index 7f3e263..0000000 --- a/addons/godot_db_manager/dlgs/delete_prop_dlg.tscn +++ /dev/null @@ -1,66 +0,0 @@ -[gd_scene load_steps=4 format=2] - -[ext_resource path="res://addons/godot_db_manager/dlgs/delete_prop_dlg.gd" type="Script" id=1] -[ext_resource path="res://addons/godot_db_manager/assets/fnt/roboto_14.tres" type="FontFile" id=2] -[ext_resource path="res://addons/godot_db_manager/assets/fnt/roboto_12.tres" type="FontFile" id=3] - -[node name="delete_prop_dlg" type="Window"] -visible = true -anchor_right = 0.257 -anchor_bottom = 0.084 -offset_right = 0.799988 -offset_bottom = 0.399994 -focus_next = NodePath("v_layout/buttons/ok_btn") -focus_mode = 1 -theme_override_fonts/title_font = ExtResource( 2 ) -exclusive = true -window_title = "Delete property" -script = ExtResource( 1 ) -__meta__ = { -"_edit_use_anchors_": false -} - -[node name="v_layout" type="VBoxContainer" parent="."] -anchor_right = 1.0 -anchor_bottom = 1.0 -theme_override_constants/separation = 10 -alignment = 1 -__meta__ = { -"_edit_use_anchors_": false -} - -[node name="prop_info" type="HBoxContainer" parent="v_layout"] -offset_top = 13.0 -offset_right = 412.0 -offset_bottom = 30.0 -alignment = 1 - -[node name="prop_lbl" type="Label" parent="v_layout/prop_info"] -offset_left = 152.0 -offset_right = 259.0 -offset_bottom = 17.0 -theme_override_fonts/font = ExtResource( 2 ) -text = "Property name: " - -[node name="buttons" type="HBoxContainer" parent="v_layout"] -offset_top = 40.0 -offset_right = 412.0 -offset_bottom = 61.0 -theme_override_constants/separation = 50 -alignment = 1 - -[node name="ok_btn" type="Button" parent="v_layout/buttons"] -offset_left = 81.0 -offset_right = 181.0 -offset_bottom = 21.0 -custom_minimum_size = Vector2( 100, 0 ) -theme_override_fonts/font = ExtResource( 3 ) -text = "OK" - -[node name="cancel_btn" type="Button" parent="v_layout/buttons"] -offset_left = 231.0 -offset_right = 331.0 -offset_bottom = 21.0 -custom_minimum_size = Vector2( 100, 0 ) -theme_override_fonts/font = ExtResource( 3 ) -text = "Cancel" diff --git a/addons/godot_db_manager/dlgs/delete_table_dlg.gd b/addons/godot_db_manager/dlgs/delete_table_dlg.gd deleted file mode 100644 index c841510..0000000 --- a/addons/godot_db_manager/dlgs/delete_table_dlg.gd +++ /dev/null @@ -1,37 +0,0 @@ -@tool -""" -class GDDBDeleteTableDlg -""" - -class_name GDDBDeleteTableDlg - -extends Window - -signal delete_table - -var m_table_id = gddb_constants.c_invalid_id - -# Called when the node enters the scene tree for the first time. -func _ready(): - $v_layout/buttons/ok_btn.connect("pressed", Callable(self, "on_ok_btn_pressed")) - $v_layout/buttons/cancel_btn.connect("pressed", Callable(self, "on_cancel_btn_pressed")) - -# sets the table id -func set_table_id(table_id : int) -> void: - m_table_id = table_id - -func set_table_name(table_name : String) -> void: - var text = "Delete table with name \"" + table_name + "\" ?" - $v_layout/table_info/table_lbl.set_text(text) - -# returns the table id -func get_table_id() -> int: - return m_table_id - -# called when the OK button is pressed -func on_ok_btn_pressed() -> void: - emit_signal("delete_table") - hide() - -func on_cancel_btn_pressed() -> void: - hide() diff --git a/addons/godot_db_manager/dlgs/delete_table_dlg.tscn b/addons/godot_db_manager/dlgs/delete_table_dlg.tscn deleted file mode 100644 index 685435c..0000000 --- a/addons/godot_db_manager/dlgs/delete_table_dlg.tscn +++ /dev/null @@ -1,66 +0,0 @@ -[gd_scene load_steps=4 format=2] - -[ext_resource path="res://addons/godot_db_manager/dlgs/delete_table_dlg.gd" type="Script" id=1] -[ext_resource path="res://addons/godot_db_manager/assets/fnt/roboto_14.tres" type="FontFile" id=2] -[ext_resource path="res://addons/godot_db_manager/assets/fnt/roboto_12.tres" type="FontFile" id=3] - -[node name="delete_table_dlg" type="Window"] -visible = true -anchor_right = 0.257 -anchor_bottom = 0.084 -offset_right = 0.799988 -offset_bottom = 0.399994 -focus_next = NodePath("v_layout/buttons/ok_btn") -focus_mode = 1 -theme_override_fonts/title_font = ExtResource( 2 ) -exclusive = true -window_title = "Delete table" -script = ExtResource( 1 ) -__meta__ = { -"_edit_use_anchors_": false -} - -[node name="v_layout" type="VBoxContainer" parent="."] -anchor_right = 1.0 -anchor_bottom = 1.0 -theme_override_constants/separation = 10 -alignment = 1 -__meta__ = { -"_edit_use_anchors_": false -} - -[node name="table_info" type="HBoxContainer" parent="v_layout"] -offset_top = 13.0 -offset_right = 412.0 -offset_bottom = 30.0 -alignment = 1 - -[node name="table_lbl" type="Label" parent="v_layout/table_info"] -offset_left = 162.0 -offset_right = 249.0 -offset_bottom = 17.0 -theme_override_fonts/font = ExtResource( 2 ) -text = "Table name: " - -[node name="buttons" type="HBoxContainer" parent="v_layout"] -offset_top = 40.0 -offset_right = 412.0 -offset_bottom = 61.0 -theme_override_constants/separation = 50 -alignment = 1 - -[node name="ok_btn" type="Button" parent="v_layout/buttons"] -offset_left = 81.0 -offset_right = 181.0 -offset_bottom = 21.0 -custom_minimum_size = Vector2( 100, 0 ) -theme_override_fonts/font = ExtResource( 3 ) -text = "OK" - -[node name="cancel_btn" type="Button" parent="v_layout/buttons"] -offset_left = 231.0 -offset_right = 331.0 -offset_bottom = 21.0 -custom_minimum_size = Vector2( 100, 0 ) -theme_override_fonts/font = ExtResource( 3 ) -text = "Cancel" diff --git a/addons/godot_db_manager/dlgs/edit_string_dlg.gd b/addons/godot_db_manager/dlgs/edit_string_dlg.gd deleted file mode 100644 index 5a81399..0000000 --- a/addons/godot_db_manager/dlgs/edit_string_dlg.gd +++ /dev/null @@ -1,59 +0,0 @@ -@tool -""" -class GDDBEditStringDlg -""" - -class_name GDDBEditStringDlg - -extends Window - -signal string_edited - -var m_prop_id = gddb_constants.c_invalid_id -var m_row_idx = gddb_constants.c_invalid_id -var m_data_text = "" - -# Called when the node enters the scene tree for the first time. -func _ready(): - $v_layout/btns/ok_btn.connect("pressed", Callable(self, "on_ok_btn_pressed")) - $v_layout/btns/cancel_btn.connect("pressed", Callable(self, "on_cancel_btn_pressed")) - - $v_layout/text.connect("text_changed", Callable(self, "on_text_changed")) - -# sets property id -func set_prop_id(prop_id : int) -> void: - m_prop_id = prop_id - -# returns property id -func get_prop_id() -> int : - return m_prop_id - -# sets row index -func set_row_idx(row_idx : int) -> void : - m_row_idx = row_idx - -# returns row index -func get_row_idx() -> int : - return m_row_idx - -# sets data text -func set_data_text(text : String) -> void : - m_data_text = text - $v_layout/text.set_text(text) - -# returns data text -func get_data_text() -> String : - return m_data_text - -# Called when the OK button is pressed -func on_ok_btn_pressed() -> void : - emit_signal("string_edited") - hide() - -# Called when the Cancel button is pressed -func on_cancel_btn_pressed() -> void : - hide() - -# Called when text is changed -func on_text_changed() -> void: - m_data_text = $v_layout/text.get_text() diff --git a/addons/godot_db_manager/dlgs/edit_string_dlg.tscn b/addons/godot_db_manager/dlgs/edit_string_dlg.tscn deleted file mode 100644 index 4937df4..0000000 --- a/addons/godot_db_manager/dlgs/edit_string_dlg.tscn +++ /dev/null @@ -1,60 +0,0 @@ -[gd_scene load_steps=4 format=2] - -[ext_resource path="res://addons/godot_db_manager/dlgs/edit_string_dlg.gd" type="Script" id=1] -[ext_resource path="res://addons/godot_db_manager/assets/fnt/roboto_14.tres" type="FontFile" id=2] -[ext_resource path="res://addons/godot_db_manager/assets/fnt/roboto_12.tres" type="FontFile" id=3] - -[node name="edit_string_dlg" type="Window"] -anchor_right = 0.430625 -anchor_bottom = 0.423333 -focus_next = NodePath("v_layout/text") -theme_override_fonts/title_font = ExtResource( 3 ) -window_title = "Edit string" -script = ExtResource( 1 ) -__meta__ = { -"_edit_use_anchors_": true -} - -[node name="v_layout" type="VBoxContainer" parent="."] -anchor_right = 1.0 -anchor_bottom = 1.0 -__meta__ = { -"_edit_use_anchors_": false -} - -[node name="edit_text_btns" type="HBoxContainer" parent="v_layout"] -offset_right = 689.0 -offset_bottom = 30.0 -custom_minimum_size = Vector2( 0, 30 ) - -[node name="text" type="TextEdit" parent="v_layout"] -offset_top = 34.0 -offset_right = 689.0 -offset_bottom = 334.0 -custom_minimum_size = Vector2( 0, 300 ) -theme_override_fonts/font = ExtResource( 2 ) -caret_blink = true - -[node name="btns" type="HBoxContainer" parent="v_layout"] -offset_top = 338.0 -offset_right = 689.0 -offset_bottom = 368.0 -custom_minimum_size = Vector2( 0, 30 ) -theme_override_constants/separation = 200 -alignment = 1 - -[node name="ok_btn" type="Button" parent="v_layout/btns"] -offset_left = 144.0 -offset_right = 244.0 -offset_bottom = 30.0 -custom_minimum_size = Vector2( 100, 0 ) -theme_override_fonts/font = ExtResource( 3 ) -text = "OK" - -[node name="cancel_btn" type="Button" parent="v_layout/btns"] -offset_left = 444.0 -offset_right = 544.0 -offset_bottom = 30.0 -custom_minimum_size = Vector2( 100, 0 ) -theme_override_fonts/font = ExtResource( 3 ) -text = "Cancel" diff --git a/addons/godot_db_manager/dlgs/error_dlg.tscn b/addons/godot_db_manager/dlgs/error_dlg.tscn deleted file mode 100644 index e3700dd..0000000 --- a/addons/godot_db_manager/dlgs/error_dlg.tscn +++ /dev/null @@ -1,11 +0,0 @@ -[gd_scene format=2] - -[node name="error_dlg" type="AcceptDialog"] -visible = true -anchor_right = 0.139 -anchor_bottom = 0.099 -offset_right = 0.599976 -offset_bottom = -0.100006 -exclusive = true -window_title = "Error" -dialog_autowrap = true diff --git a/addons/godot_db_manager/dlgs/load_res_path_dlg.gd b/addons/godot_db_manager/dlgs/load_res_path_dlg.gd deleted file mode 100644 index 5a7f908..0000000 --- a/addons/godot_db_manager/dlgs/load_res_path_dlg.gd +++ /dev/null @@ -1,76 +0,0 @@ -@tool -""" -class GDDBLoadResourcePathDlg -""" - -class_name GDDBLoadResourcePathDlg - -extends FileDialog - -# TODO: put this list in a config file -const file_filters = [ - # Godot resource file types - "*.res, *.tres ; Godot resource file types", - - # Godot scene files - "*.scn, *.tscn, *escn ; Godot scene file types", - - # Code file types - "*.gd, *.cs, *.h, *.c, *.hpp, *.cpp ; Code file types", - - # Shader file types - "*.shader ; Shader file types", - - # material file types - "*.mat ; Material file types", - - # mesh file types - "*.dae, *.gltf, *.obj, *.fbx ; Mesh file types", - - # animation file types - "*.anim ; Animation file types", - - # font file types - "*.ttf, *.otf ; Font file types", - - # image file types - "*.png, *.jpg, *.jpeg, *.tiff, *.tga, *.bmp, *.webp, *.gif, *.hdr ; Images file types", - - # soung file types - "*.snd, *.wav, *.ogg, *.mp3 ; Sound file types", - - # video file types - "*.ogg, *.mpg, *.mpeg, *.avi, *.mov, *.mp4, *.webm ; Video file types", - - # text file types - "*.txt, *.csv, *.json, *.xml, *.cfg, *.ini ; Text file types", - - # document file types - "*.doc, *.docx, *.xls, *.xlsx, *.odt, *.ods, *.pdf ; Doc file types", - - # binary data file types - "*.dat, *.raw ; Binary data file types" -] - -var m_prop_id = gddb_constants.c_invalid_id -var m_row_idx = gddb_constants.c_invalid_id - -# Called when the node enters the scene tree for the first time. -func _ready(): - set_filters(PackedStringArray(file_filters)) - -# sets the property id -func set_prop_id(prop_id : int) -> void : - m_prop_id = prop_id - -# returns the property id -func get_prop_id() -> int : - return m_prop_id - -# sets the row index -func set_row_idx(row_idx : int) -> void : - m_row_idx = row_idx - -# returns the row index -func get_row_idx() -> int : - return m_row_idx diff --git a/addons/godot_db_manager/dlgs/load_res_path_dlg.tscn b/addons/godot_db_manager/dlgs/load_res_path_dlg.tscn deleted file mode 100644 index 4f6444b..0000000 --- a/addons/godot_db_manager/dlgs/load_res_path_dlg.tscn +++ /dev/null @@ -1,16 +0,0 @@ -[gd_scene load_steps=2 format=2] - -[ext_resource path="res://addons/godot_db_manager/dlgs/load_res_path_dlg.gd" type="Script" id=1] - -[node name="load_res_path_dlg" type="FileDialog"] -anchor_right = 0.404 -anchor_bottom = 0.431 -offset_right = -0.400024 -offset_bottom = 0.0999756 -window_title = "Open a File" -resizable = true -mode = 0 -script = ExtResource( 1 ) -__meta__ = { -"_edit_use_anchors_": false -} diff --git a/addons/godot_db_manager/dlgs/new_db_dlg.gd b/addons/godot_db_manager/dlgs/new_db_dlg.gd deleted file mode 100644 index 7aae1fd..0000000 --- a/addons/godot_db_manager/dlgs/new_db_dlg.gd +++ /dev/null @@ -1,63 +0,0 @@ -@tool -""" -class GDDBNewDBDlg -""" - -class_name GDDBNewDBDlg - -extends Window - -signal create_new_db - -var m_current_db_name = "" - -# Called when the node enters the scene tree for the first time. -func _ready() -> void : - $v_layout/buttons/ok_btn.connect("pressed", Callable(self, "on_ok_btn_pressed")) - $v_layout/buttons/cancel_btn.connect("pressed", Callable(self, "on_cancel_btn_pressed")) - connect("about_to_popup", Callable(self, "on_about_to_show")) - $v_layout/db_info/db_edt.connect("text_changed", Callable(self, "on_text_changed")) - $v_layout/db_info/db_edt.connect("text_submitted", Callable(self, "on_text_confirmed")) - -# Called when the node is about to be shown. -func on_about_to_show() -> void : - m_current_db_name = "" - $v_layout/db_info/db_edt.set_text(m_current_db_name) - -# called everytime the text is changed -func on_text_changed(new_text: String) -> void : - var change_text = true - if(!gddb_globals.check_db_name(new_text)): - change_text = false - else: - if(new_text.length() > gddb_constants.c_max_db_name_len): - change_text = false - else: - change_text = true - - if(change_text): - m_current_db_name = $v_layout/db_info/db_edt.get_text() - else: - $v_layout/db_info/db_edt.set_text(m_current_db_name) - $v_layout/db_info/db_edt.set_caret_column(m_current_db_name.length()) - -# called when the user presses the ENTER key -func on_text_confirmed(text : String) -> void : - # print("GDDBNewDBDlg::on_text_confirmed(" + text + ")") - if(m_current_db_name.is_empty()): - return - handle_db_name() - hide() - -# called when the OK button is pressed -func on_ok_btn_pressed() -> void : - if(!m_current_db_name.is_empty()): - handle_db_name() - -# called when the Cancel button is pressed -func on_cancel_btn_pressed() -> void : - hide() - -# handles the name of the database -func handle_db_name() -> void : - emit_signal("create_new_db", m_current_db_name) diff --git a/addons/godot_db_manager/dlgs/new_db_dlg.tscn b/addons/godot_db_manager/dlgs/new_db_dlg.tscn deleted file mode 100644 index b2eeb94..0000000 --- a/addons/godot_db_manager/dlgs/new_db_dlg.tscn +++ /dev/null @@ -1,82 +0,0 @@ -[gd_scene load_steps=4 format=2] - -[ext_resource path="res://addons/godot_db_manager/assets/fnt/roboto_14.tres" type="FontFile" id=1] -[ext_resource path="res://addons/godot_db_manager/dlgs/new_db_dlg.gd" type="Script" id=2] -[ext_resource path="res://addons/godot_db_manager/assets/fnt/roboto_12.tres" type="FontFile" id=3] - -[node name="new_db_dlg" type="Window"] -visible = true -anchor_right = 0.257 -anchor_bottom = 0.1 -offset_right = 0.799988 -focus_next = NodePath("v_layout/db_info/db_edt") -focus_mode = 2 -theme_override_fonts/title_font = ExtResource( 1 ) -exclusive = true -window_title = "New database (max 16 characters)" -script = ExtResource( 2 ) -__meta__ = { -"_edit_horizontal_guides_": [ 90.0781 ], -"_edit_use_anchors_": false -} - -[node name="v_layout" type="VBoxContainer" parent="."] -anchor_right = 1.0 -anchor_bottom = 1.0 -theme_override_constants/separation = 20 -alignment = 1 -__meta__ = { -"_edit_use_anchors_": false -} - -[node name="db_info" type="HBoxContainer" parent="v_layout"] -offset_top = 11.0 -offset_right = 412.0 -offset_bottom = 38.0 -alignment = 1 -__meta__ = { -"_edit_use_anchors_": false -} - -[node name="db_lbl" type="Label" parent="v_layout/db_info"] -offset_left = 18.0 -offset_top = 5.0 -offset_right = 89.0 -offset_bottom = 22.0 -theme_override_fonts/font = ExtResource( 1 ) -text = "DB name: " - -[node name="db_edt" type="LineEdit" parent="v_layout/db_info"] -offset_left = 93.0 -offset_right = 393.0 -offset_bottom = 27.0 -custom_minimum_size = Vector2( 300, 20 ) -theme_override_fonts/font = ExtResource( 1 ) -caret_blink = true -caret_blink_interval = 0.5 - -[node name="buttons" type="HBoxContainer" parent="v_layout"] -offset_top = 58.0 -offset_right = 412.0 -offset_bottom = 79.0 -theme_override_constants/separation = 80 -alignment = 1 -__meta__ = { -"_edit_use_anchors_": false -} - -[node name="ok_btn" type="Button" parent="v_layout/buttons"] -offset_left = 66.0 -offset_right = 166.0 -offset_bottom = 21.0 -custom_minimum_size = Vector2( 100, 0 ) -theme_override_fonts/font = ExtResource( 3 ) -text = "OK" - -[node name="cancel_btn" type="Button" parent="v_layout/buttons"] -offset_left = 246.0 -offset_right = 346.0 -offset_bottom = 21.0 -custom_minimum_size = Vector2( 100, 0 ) -theme_override_fonts/font = ExtResource( 3 ) -text = "Cancel" diff --git a/addons/godot_db_manager/dlgs/new_table_dlg.gd b/addons/godot_db_manager/dlgs/new_table_dlg.gd deleted file mode 100644 index 6bde601..0000000 --- a/addons/godot_db_manager/dlgs/new_table_dlg.gd +++ /dev/null @@ -1,83 +0,0 @@ -@tool -""" -class GDDBNewDBDlg -""" - -class_name GDDBNewTableDlg - -extends Window - -signal create_new_table -signal cancel_dialog - -var m_dlg_type : int = gddb_types.e_new_dlg_type_new -var m_table_id = gddb_constants.c_invalid_id -var m_current_table_name = "" - -# Called when the node enters the scene tree for the first time. -func _ready(): - $v_layout/buttons/ok_btn.connect("pressed", Callable(self, "on_ok_btn_pressed")) - $v_layout/buttons/cancel_btn.connect("pressed", Callable(self, "on_cancel_btn_pressed")) - $v_layout/table_info/table_edt.connect("text_changed", Callable(self, "on_text_changed")) - $v_layout/table_info/table_edt.connect("text_submitted", Callable(self, "on_text_confirmed")) - m_current_table_name = "" - -# sets the type of the dialog -func set_dld_type(dlg_type : int) -> void : - m_dlg_type = dlg_type - -# returns the dialog type -func get_dlg_type() -> int : - return m_dlg_type - -# sets the table id -func set_table_id(table_id : int) -> void: - m_table_id = table_id - -# returns the table id -func get_table_id() -> int: - return m_table_id - -# sets the table name -func set_init_name(table_name : String) -> void: - # print("cNewTableDlg::set_init_name(" + name + ")") - m_current_table_name = table_name - $v_layout/table_info/table_edt.set_text(m_current_table_name) - -func on_text_changed(new_text: String) -> void: - var change_text = true - if(!gddb_globals.check_db_name(new_text)): - change_text = false - else: - if(new_text.length() > gddb_constants.c_max_db_name_len): - change_text = false - else: - change_text = true - - if(change_text): - m_current_table_name = $v_layout/table_info/table_edt.get_text() - else: - $v_layout/table_info/table_edt.set_text(m_current_table_name) - $v_layout/table_info/table_edt.set_caret_column(m_current_table_name.length()) - -# called when the user presses the ENTER key -func on_text_confirmed(text : String) -> void: - if(m_current_table_name.is_empty()): - return - handle_table_name() - hide() - -# called when the OK button is pressed -func on_ok_btn_pressed() -> void: - if(!m_current_table_name.is_empty()): - handle_table_name() - hide() - -# called when the Cancel button is pressed -func on_cancel_btn_pressed() -> void : - hide() - emit_signal("cancel_dialog") - -# handles the text in the EditLine -func handle_table_name() -> void: - emit_signal("create_new_table", m_current_table_name) diff --git a/addons/godot_db_manager/dlgs/new_table_dlg.tscn b/addons/godot_db_manager/dlgs/new_table_dlg.tscn deleted file mode 100644 index 6114519..0000000 --- a/addons/godot_db_manager/dlgs/new_table_dlg.tscn +++ /dev/null @@ -1,73 +0,0 @@ -[gd_scene load_steps=4 format=2] - -[ext_resource path="res://addons/godot_db_manager/assets/fnt/roboto_12.tres" type="FontFile" id=1] -[ext_resource path="res://addons/godot_db_manager/dlgs/new_table_dlg.gd" type="Script" id=2] -[ext_resource path="res://addons/godot_db_manager/assets/fnt/roboto_14.tres" type="FontFile" id=3] - -[node name="new_table_dlg" type="Window"] -visible = true -anchor_right = 0.257 -anchor_bottom = 0.084 -offset_right = 0.799988 -offset_bottom = 0.399994 -focus_next = NodePath("v_layout/table_info/table_edt") -focus_mode = 1 -theme_override_fonts/title_font = ExtResource( 3 ) -exclusive = true -window_title = "New table (max 16 characters)" -script = ExtResource( 2 ) - -[node name="v_layout" type="VBoxContainer" parent="."] -anchor_right = 1.0 -anchor_bottom = 1.0 -theme_override_constants/separation = 10 -alignment = 1 -__meta__ = { -"_edit_use_anchors_": false -} - -[node name="table_info" type="HBoxContainer" parent="v_layout"] -offset_top = 8.0 -offset_right = 412.0 -offset_bottom = 35.0 -alignment = 1 - -[node name="table_lbl" type="Label" parent="v_layout/table_info"] -offset_left = 10.0 -offset_top = 5.0 -offset_right = 97.0 -offset_bottom = 22.0 -theme_override_fonts/font = ExtResource( 3 ) -text = "Table name: " - -[node name="table_edt" type="LineEdit" parent="v_layout/table_info"] -offset_left = 101.0 -offset_right = 401.0 -offset_bottom = 27.0 -custom_minimum_size = Vector2( 300, 20 ) -theme_override_fonts/font = ExtResource( 3 ) -caret_blink = true -caret_blink_interval = 0.5 - -[node name="buttons" type="HBoxContainer" parent="v_layout"] -offset_top = 45.0 -offset_right = 412.0 -offset_bottom = 66.0 -theme_override_constants/separation = 80 -alignment = 1 - -[node name="ok_btn" type="Button" parent="v_layout/buttons"] -offset_left = 66.0 -offset_right = 166.0 -offset_bottom = 21.0 -custom_minimum_size = Vector2( 100, 0 ) -theme_override_fonts/font = ExtResource( 1 ) -text = "OK" - -[node name="cancel_btn" type="Button" parent="v_layout/buttons"] -offset_left = 246.0 -offset_right = 346.0 -offset_bottom = 21.0 -custom_minimum_size = Vector2( 100, 0 ) -theme_override_fonts/font = ExtResource( 1 ) -text = "Cancel" diff --git a/addons/godot_db_manager/icon/icon.png b/addons/godot_db_manager/icon/icon.png deleted file mode 100644 index 831cc07f3872412341c54e754e5f557e8a72b7f6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1713 zcmV;i22S~jP)EX>4Tx04R}tkv&MmKpe$iQ>7x64i*$~2vVIah>AE$6^me@v=v%)FuC*#nlvOS zE{=k0!NHHks)LKOt`4q(Aou~|}?mh0_0Yam~RI_UWP&La) z#baVNw<-o+As~bSj6jr_sVCBl8F-Gbd-(Wz7v)*r=l&dhO5S9EPb8jWx?vG-5YKE{ zI_G`jFe^z4@j3ChK^G)`eSad^gZEa<4bO1wgWnpw> zWFU8GbZ8()Nlj2!fese{00fCiL_t(|+U;3QOk7nI{_eZ)4P_=Ul%HXkcCbI7H9)~4 zm`0G6G}RI`ZmcmeP2>{9o_+FC^zPKmnitP;D@`+o0+KiUSk}C=O5@pg2HrfZ_nf0g3|@ z2dGbCsst1O3IGLw0zd(v08juZ02BZU00n>oKmnit$PLlczEYU5=7%6c1E7U5V09YI zeGJS~2kvbEbCyY$o1@(jL!Zbx3=dd=AFQkfny(3(t6W-+4O{~SuS2VO9s1Gs2byD! zbU_SXO*qna1zLeGVeayMm_2VMBK_DU=+&K>0YIBeFzPQMioivUHJVFi2b^tBdYh5DgCawfUrA|tTIz6V8y!Mu&&N7|(n z-_q|t4+`2gQ0*&VJP%}UfTnK5*PjIiyD|qrOy05wu4?G@m%$8IB7K}?U^OqpBw^Qd zan&Bt(!lcE(5o&WHaiH4{0f@>4cGyI82b)lZ8VY26Zc^TzlGlVKD5HavPzd1C~pOY zdO<58Fn1+rHDs?SgbcXghVHgHQp`?xq^&zIHhuM3bBO>A=D;GQfm zrTboDB)}qJf^nYE<2wglUT=?`rO>K7Vg1qvn&|;WFM&A<rJ0-PMu$H^6;QK2#;} z=C{EP6eTYd)P*{N@66@N-n#&E6u>xn8KUwTSm6O+VH~vlH(1G| z_Pan?4OpH#jonJX+{eK@wa~n$prtU9pLh|V7a%5n0xjGF^EHDXJ(u!&+eR0d=V?&* z7MQm!Wu_n%yQsgz>U)3!KmnitPyi?Z6aWeU1%MJIxg`Gpu~D>J`Opj(00000NkvXX Hu0mjf0FVh( diff --git a/addons/godot_db_manager/icon/icon.png.import b/addons/godot_db_manager/icon/icon.png.import deleted file mode 100644 index 177a504..0000000 --- a/addons/godot_db_manager/icon/icon.png.import +++ /dev/null @@ -1,34 +0,0 @@ -[remap] - -importer="texture" -type="StreamTexture" -path="res://.import/icon.png-8476557a42180e1e377aacb0cfda943e.stex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://addons/godot_db_manager/icon/icon.png" -dest_files=[ "res://.import/icon.png-8476557a42180e1e377aacb0cfda943e.stex" ] - -[params] - -compress/mode=0 -compress/lossy_quality=0.7 -compress/hdr_mode=0 -compress/bptc_ldr=0 -compress/normal_map=0 -flags/repeat=0 -flags/filter=true -flags/mipmaps=false -flags/anisotropic=false -flags/srgb=2 -process/fix_alpha_border=true -process/premult_alpha=false -process/HDR_as_SRGB=false -process/invert_color=false -stream=false -size_limit=0 -detect_3d=true -svg/scale=1.0 diff --git a/addons/godot_db_manager/icon/icon.xcf b/addons/godot_db_manager/icon/icon.xcf deleted file mode 100644 index fda4ddda9d1d63205b67ea332b56685334a6ccdb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4019 zcmeHH-)~cO6#w?Nv|GCgLzxL_@Y-SQVk+A}6XP}tgUnwun)rgw;B9-`U1@J;dkZTu zR`Cz;M~vYCH4&mjP>}4Q@lAcjI0cyz*fPX9C&Y)DY^>XkpL6eRcOg8e@xeIH%{kxC z_k2I+d%x#<&b=uuGbj(mlk(oS_I3e@+RFHCXcN@sf>{gQ4P9qW*fg=Zg-s(HKbuww zW&@ibnJ#4y7!#>G0JiQfK*Mm2+LT-b4Y;CqjCa;6eKpRuYAQI1J zGOBK1E_iWmPfk?~H6icQi~+g#;l3w$WsOPIGD=E~q;gtM}9Z&f0vChh>eU1 zg0aPct3Bd|4~e?M7_DL(>W~phD@8R27w6(?2xpYsp2DCU>a;+2C#s^$d2PSi)gRij zrDJn{sGD!8a}zIgcW$zIA(Sea)eSkcEvG4Id3!dUsQMxtF5ll_Q_QJVxIt}5O>>nM z-9VJ4?9hn~eFWQn^P6u&yoOMCN9dGA+>=Nt_$S2-Yh^B(X&La zfl5=tRo7M~I&AW_3v*vTE{UsU`j+T+v7YD~4jQAG7PpuUo_i5ZhZx}cUucp;es|OK z(o;f|>Es0M68&^>7!mbEr~G0ORF4=tPE-#j9az&fP2(=6zE6k5d-M)dgG6s~YHpgJ zGB-!WA%6J?{eYG%5`{S;M&n|Fp5=%-q7yen%pD_YM8qeO_~}uiogC3Z^bpYoju79| zEIys(cOU`JW%`v9I3q=U^5Own2Mf_>)an`SFJEbc~d?_jQ5%muVBex5yl@Ls!FDpQ~Kz&k>vS1(=rYNsS-`0V0GToDJ9 z#CP0wQCs;R<=r=+87jcP>W{|fVfnj%H2dqbHo-QiT1}!rtTxAo3e+SD#0v3Uu|^?E zE|2h&%hvUO5kLJW`2Q5A EAI_|-$^ZZW diff --git a/addons/godot_db_manager/license.txt b/addons/godot_db_manager/license.txt deleted file mode 100644 index fa51052..0000000 --- a/addons/godot_db_manager/license.txt +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2020 Radu Bolovan - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/addons/godot_db_manager/menu.gd b/addons/godot_db_manager/menu.gd deleted file mode 100644 index 098afb9..0000000 --- a/addons/godot_db_manager/menu.gd +++ /dev/null @@ -1,101 +0,0 @@ -@tool -""" -class GDDBMenu -""" - -class_name GDDBMenu - -extends Control - -signal new_database -signal load_database -signal save_database -signal save_database_as - -enum { - e_file_new_id = 0, - e_file_load_id = 1 - e_file_save_id = 2, - e_file_save_as_id = 3, - - e_option_autosave_on_close_id = 4 -} - -var m_enable_new_db = true -var m_enable_load_db = true -var m_enable_save_db = false -var m_enable_save_as_db = false - -var m_check_autosave_on_load = false - -func _ready() -> void: - $layout/File.connect("about_to_popup", Callable(self, "on_about_to_show_file_menu")) - $layout/File.get_popup().clear() - $layout/File.get_popup().add_item("New DB", e_file_new_id) - $layout/File.get_popup().add_item("Load DB", e_file_load_id) - $layout/File.get_popup().add_item("Save DB", e_file_save_id) - $layout/File.get_popup().add_item("Save DB As ...", e_file_save_as_id) - $layout/File.get_popup().connect("id_pressed", Callable(self, "on_file_id_pressed")) - - $layout/Options.connect("about_to_popup", Callable(self, "on_about_to_show_options_menu")) - $layout/Options.get_popup().clear() - $layout/Options.get_popup().add_check_item("Autosave on close", e_option_autosave_on_close_id) - $layout/Options.get_popup().connect("id_pressed", Callable(self, "on_options_id_pressed")) - -# called before showing the file menu -func on_about_to_show_file_menu() -> void: - for idx in range(0, $layout/File.get_popup().get_item_count()): - var item_id = $layout/File.get_popup().get_item_id(idx) - if(item_id == e_file_new_id): - $layout/File.get_popup().set_item_disabled(idx, !m_enable_new_db) - elif(item_id == e_file_load_id): - $layout/File.get_popup().set_item_disabled(idx, !m_enable_load_db) - elif(item_id == e_file_save_id): - $layout/File.get_popup().set_item_disabled(idx, !m_enable_save_db) - elif(item_id == e_file_save_as_id): - $layout/File.get_popup().set_item_disabled(idx, !m_enable_save_as_db) - raise() - -# enables / disables the possibility to create a new databbase -func enable_file_new(enable) -> void: - m_enable_new_db = enable - -# enables / disables the possibility to load a databbase -func enable_file_load(enable) -> void: - m_enable_load_db = enable - -# enables / disables the possibility to save a databbase -func enable_file_save(enable) -> void: - m_enable_save_db = enable - -# enables / disables the possibility to save a databbase as another -func enable_file_save_as(enable) -> void: - m_enable_save_as_db = enable - -# called when the user clicks on a file menu item -func on_file_id_pressed(id : int) -> void: - if(id == e_file_new_id): - emit_signal("new_database") - elif(id == e_file_load_id): - emit_signal("load_database") - elif(id == e_file_save_id): - emit_signal("save_database") - elif(id == e_file_save_as_id): - emit_signal("save_database_as") - -# called before showing the option menu -func on_about_to_show_options_menu() -> void: - for idx in range(0, $layout/Options.get_popup().get_item_count()): - var item_id = $layout/Options.get_popup().get_item_id(idx) - if(item_id == e_option_autosave_on_close_id): - $layout/Options.get_popup().set_item_checked(idx, m_check_autosave_on_load) - raise() - -# enables / disables the possibility to autosave all databases on close -func enable_autosave_on_close(enable) -> void: - m_check_autosave_on_load = enable - -# called when the user clicks on a options menu item -func on_options_id_pressed(id : int) -> void: - if(id == e_option_autosave_on_close_id): - enable_autosave_on_close(!m_check_autosave_on_load) diff --git a/addons/godot_db_manager/menu.tscn b/addons/godot_db_manager/menu.tscn deleted file mode 100644 index 73fbaf1..0000000 --- a/addons/godot_db_manager/menu.tscn +++ /dev/null @@ -1,36 +0,0 @@ -[gd_scene load_steps=3 format=2] - -[ext_resource path="res://addons/godot_db_manager/menu.gd" type="Script" id=1] -[ext_resource path="res://addons/godot_db_manager/assets/fnt/roboto_18.tres" type="FontFile" id=2] - -[node name="menu" type="Control"] -anchor_right = 1.0 -custom_minimum_size = Vector2( 0, 30 ) -script = ExtResource( 1 ) -__meta__ = { -"_edit_horizontal_guides_": [ 30.0 ], -"_edit_use_anchors_": false, -"_edit_vertical_guides_": [ 1024.0 ] -} - -[node name="layout" type="HBoxContainer" parent="."] -offset_right = 1024.0 -offset_bottom = 30.0 -__meta__ = { -"_edit_use_anchors_": false -} - -[node name="File" type="MenuButton" parent="layout"] -offset_right = 40.0 -offset_bottom = 30.0 -theme_override_fonts/font = ExtResource( 2 ) -text = "File" -items = [ "New DB", null, 0, false, false, 0, 0, null, "", false, "Load DB", null, 0, false, false, 1, 0, null, "", false, "Save DB", null, 0, false, false, 2, 0, null, "", false, "Save DB As ...", null, 0, false, false, 3, 0, null, "", false ] - -[node name="Options" type="MenuButton" parent="layout"] -offset_left = 44.0 -offset_right = 117.0 -offset_bottom = 30.0 -theme_override_fonts/font = ExtResource( 2 ) -text = "Options" -items = [ "Autosave on close", null, 1, false, false, 4, 0, null, "", false ] diff --git a/addons/godot_db_manager/plugin.cfg b/addons/godot_db_manager/plugin.cfg deleted file mode 100644 index b93178f..0000000 --- a/addons/godot_db_manager/plugin.cfg +++ /dev/null @@ -1,7 +0,0 @@ -[plugin] - -name="Godot Database Manager" -description="A database manager plugin for Google Engine." -author="Radu Bolovan" -version="2.1.a" -script="db_manager.gd" diff --git a/addons/godot_db_manager/table_cell.gd b/addons/godot_db_manager/table_cell.gd deleted file mode 100644 index 996a63b..0000000 --- a/addons/godot_db_manager/table_cell.gd +++ /dev/null @@ -1,221 +0,0 @@ -@tool -""" -class GDDBTableCell -""" - -class_name GDDBTableCell - -extends Control - -signal edit_data -signal choose_resource -signal choose_data -signal edit_string - -const c_cell_min_width = 150 - -var m_prop_id : int = -1 -var m_prop_type : int = gddb_constants.c_invalid_id -var m_row_idx : int = -1 -var m_text : String = "" - -func _ready() -> void : - $LineEdit.connect("text_changed", Callable(self, "on_text_changed")) - $LineEdit/edit_btn.connect("pressed", Callable(self, "on_edit_string")) - - $Button.connect("pressed", Callable(self, "on_button_pressed")) - $Button.set_clip_text(true) - - $CheckBox.connect("toggled", Callable(self, "on_toggle_button")) - -func _exit_tree() -> void : - $LineEdit.disconnect("text_changed", Callable(self, "on_text_changed")) - -# sets the property id -func set_prop_id(id : int) -> void : - m_prop_id = id - -# returns the property id -func get_prop_id() -> int : - return m_prop_id - -# sets property type -func set_prop_type(data_type : int) -> void : - # print("GDDBTableCell::set_prop_type(" + str(data_type) + ")") - - var data_type_changed = false - - if(m_prop_type != data_type): - m_prop_type = data_type - data_type_changed = true - - if(!data_type_changed): - return - - if(m_prop_type == gddb_types.e_prop_type_bool): - $LineEdit.hide() - $Button.hide() - $CheckBox.show() - set_text("") - elif(m_prop_type == gddb_types.e_prop_type_int): - $LineEdit.show() - $LineEdit/edit_btn.hide() - $Button.hide() - $CheckBox.hide() - set_text("0") - elif(m_prop_type == gddb_types.e_prop_type_float): - $LineEdit.show() - $LineEdit/edit_btn.hide() - $Button.hide() - $CheckBox.hide() - set_text("0.0") - elif(m_prop_type == gddb_types.e_prop_type_string): - $LineEdit.show() - $LineEdit/edit_btn.show() - $Button.hide() - $CheckBox.hide() - set_text("") - elif(m_prop_type == gddb_types.e_prop_type_resource): - $LineEdit.hide() - $Button.show() - $CheckBox.hide() - set_text("res://") - elif(m_prop_type >= gddb_types.e_prop_types_count): - $LineEdit.hide() - $Button.show() - $CheckBox.hide() - set_text("{}") - -func get_prop_type() -> int : - return m_prop_type - -# sets the row index -func set_row_idx(idx : int) -> void : - m_row_idx = idx - -# returns the property index -func get_row_idx() -> int : - return m_row_idx - -# sets the text -func set_text(text : String) -> void : - # print("GDDBTableCell::set_text(" + text + ")") - m_text = text - $LineEdit.set_text(m_text) - $Button.set_text(m_text) - if(m_prop_type == gddb_types.e_prop_type_bool): - $CheckBox.set_pressed((text == "1")) - -# sets autoincrement -func enable_autoincrement(enable : bool) -> void : - $LineEdit.set_editable(!enable) - -# refreshes the width by the property name -func refresh_width(prop_name : String) -> void : - var size = get_custom_minimum_size() - size.x = max(c_cell_min_width, get_font("normal_font").get_string_size(prop_name).x + 10) - set_custom_minimum_size(size) - -# called when the checkbox is toggled/untoggled -func on_toggle_button(enable : bool) -> void : - var data = "0" - if(enable): - data = "1" - emit_signal("edit_data", m_prop_id, m_row_idx, data) - -# called when the button is pressed -func on_button_pressed() -> void : - if(m_prop_type == gddb_types.e_prop_type_resource): - # print("GDDBTableCell::on_button_pressed()") - emit_signal("choose_resource", m_prop_id, m_row_idx) - elif(m_prop_type >= gddb_types.e_prop_types_count): - emit_signal("choose_data", m_prop_id, m_row_idx, m_prop_type) - -# called when the edit string button is pressed -func on_edit_string() -> void : - emit_signal("edit_string", m_prop_id, m_row_idx, m_text) - -# called when edit the data -func on_text_changed(new_text : String) -> void : - if(new_text.is_empty()): - m_text = "" - $LineEdit.set_text(m_text) - $LineEdit.set_caret_column(0) - return - - if(m_prop_type == gddb_types.e_prop_type_int): - if(!check_integer(new_text)): - return - - if(m_prop_type == gddb_types.e_prop_type_float): - if(!check_float(new_text)): - return - - emit_signal("edit_data", m_prop_id, m_row_idx, new_text) - -# checks if the text is integer -func check_integer(text : String) -> bool : - var is_negative = false - - # check if the string is probably a number, but starts with "-" - if(text.begins_with("-")): - text.erase(0, 1) - is_negative = true - - # check if the current string is only "-" - if(text.is_empty()): - m_text = "-" - $LineEdit.set_text(m_text) - $LineEdit.set_caret_column(m_text.length()) - return true - - if(text.is_valid_int()): - if(text.begins_with("0")): - # a negative integer cannot start with "0" - if(is_negative): - m_text = "-" - $LineEdit.set_text(m_text) - $LineEdit.set_caret_column(1) - return true - - # a positive number starting with "0" can be only "0" - m_text = "0" - $LineEdit.set_text(m_text) - $LineEdit.set_caret_column(1) - return true - - # don't add more "-" in front of the number - if(text.begins_with("-")): - text.erase(0, 1) - - # add back the "-" - if(is_negative): - m_text = "-" + text - else: - m_text = text - - $LineEdit.set_text(m_text) - $LineEdit.set_caret_column(m_text.length()) - return true - - $LineEdit.set_text(m_text) - $LineEdit.set_caret_column(m_text.length()) - return false - -func check_float(text : String) -> bool : - if(text.is_valid_float()): - if(text.begins_with("00") || text.begins_with("01") || text.begins_with("02") - || text.begins_with("03") || text.begins_with("04") || text.begins_with("05") - || text.begins_with("06") || text.begins_with("07") || text.begins_with("08") - || text.begins_with("09")): - m_text = "0" - $LineEdit.set_text(m_text) - $LineEdit.set_caret_column(1) - return true - - m_text = text - return true - - $LineEdit.set_text(m_text) - $LineEdit.set_caret_column(m_text.length()) - return false diff --git a/addons/godot_db_manager/table_cell.tscn b/addons/godot_db_manager/table_cell.tscn deleted file mode 100644 index 8725d84..0000000 --- a/addons/godot_db_manager/table_cell.tscn +++ /dev/null @@ -1,72 +0,0 @@ -[gd_scene load_steps=7 format=2] - -[ext_resource path="res://addons/godot_db_manager/assets/tex/gui.png" type="Texture2D" id=1] -[ext_resource path="res://addons/godot_db_manager/table_cell.gd" type="Script" id=2] -[ext_resource path="res://addons/godot_db_manager/assets/fnt/roboto_18.tres" type="FontFile" id=3] - -[sub_resource type="AtlasTexture" id=1] -atlas = ExtResource( 1 ) -region = Rect2( 28, 1.86734, 24, 24.1327 ) - -[sub_resource type="AtlasTexture" id=2] -atlas = ExtResource( 1 ) -region = Rect2( 28, 54, 24, 24 ) - -[sub_resource type="AtlasTexture" id=3] -atlas = ExtResource( 1 ) -region = Rect2( 28, 28, 24, 24 ) - -[node name="table_cell" type="Control"] -anchor_right = 1.0 -offset_bottom = 15.36 -custom_minimum_size = Vector2( 150, 32 ) -script = ExtResource( 2 ) -__meta__ = { -"_edit_horizontal_guides_": [ 32.0 ], -"_edit_use_anchors_": true -} - -[node name="LineEdit" type="LineEdit" parent="."] -anchor_right = 1.0 -anchor_bottom = 0.914286 -theme_override_fonts/font = ExtResource( 3 ) -text = "TESTING WWW" -caret_blink = true -caret_blink_interval = 0.5 -__meta__ = { -"_edit_use_anchors_": true -} - -[node name="edit_btn" type="TextureButton" parent="LineEdit"] -anchor_left = 1.0 -anchor_right = 1.0 -anchor_bottom = 1.0 -offset_left = -29.0 -offset_top = 4.0 -offset_right = -5.0 -offset_bottom = -4.0 -texture_normal = SubResource( 1 ) -texture_pressed = SubResource( 2 ) -texture_hover = SubResource( 3 ) -__meta__ = { -"_edit_use_anchors_": true -} - -[node name="Button" type="Button" parent="."] -visible = false -anchor_right = 1.0 -anchor_bottom = 1.0 -theme_override_fonts/font = ExtResource( 3 ) -clip_text = true -__meta__ = { -"_edit_use_anchors_": false -} - -[node name="CheckBox" type="CheckBox" parent="."] -visible = false -anchor_right = 1.0 -anchor_bottom = 1.0 -theme_override_fonts/font = ExtResource( 3 ) -__meta__ = { -"_edit_use_anchors_": false -} diff --git a/addons/godot_db_manager/table_editor.gd b/addons/godot_db_manager/table_editor.gd deleted file mode 100644 index 885f04a..0000000 --- a/addons/godot_db_manager/table_editor.gd +++ /dev/null @@ -1,433 +0,0 @@ -@tool -""" -class GDDBTableEditor -""" - -class_name GDDBTableEditor - -extends Control - -signal set_dirty - -var m_parent_table = null - -# Called when the node enters the scene tree for the first time. -func _ready() -> void: - $tabs/structure/header/new_property_btn.connect("pressed", Callable(self, "on_new_property_btn_pressed")) - $tabs/data/add_data_btn.connect("pressed", Callable(self, "on_add_row_data_btn_pressed")) - $tabs/data/add_data_btn.set_disabled(true) - - $load_res_path_dlg.connect("file_selected", Callable(self, "on_select_res_path")) - - $data_dlg.connect("select_data", Callable(self, "on_select_data")) - - $edit_string_dlg.connect("string_edited", Callable(self, "on_text_edited")) - - $delete_prop_dlg.connect("delete_prop", Callable(self, "on_confirm_delete_property")) - -# called when resizing a property -func on_resize_property(prop_id : int, diff_x : float) -> void : - var current_prop = null - for idx in range(0, $tabs/data/scroll/data_holder/data_header.get_child_count()): - var prop = $tabs/data/scroll/data_holder/data_header.get_child(idx) - if(prop.get_prop_id() == prop_id): - current_prop = prop - break - - var prop_size : Vector2 = current_prop.get_child(0).get_size() - - # print("diff_x: " + str(diff_x)) - prop_size.x += diff_x - - if(prop_size.x < gddb_constants.c_min_cell_width): - return - if(prop_size.x > gddb_constants.c_max_cell_width): - return - - # print("prop_size.x = " + str(prop_size.x)) - - var prop_idx = -1 - - var data_size = $tabs/data/scroll.get_size() - data_size.x += diff_x - $tabs/data/scroll/data_holder/data_header.set_size(data_size) - - for idx in range(0, $tabs/data/scroll/data_holder/data_header.get_child_count()): - var prop = $tabs/data/scroll/data_holder/data_header.get_child(idx) - if(prop.get_prop_id() == prop_id): - var current_prop_size = prop.get_custom_minimum_size() - current_prop_size.x += diff_x - prop.set_custom_minimum_size(current_prop_size) - prop_idx = idx - - if(prop_idx != -1 && idx > prop_idx): - var pos = prop.get_position() - pos.x += diff_x - prop.set_position(pos) - - for idx in range(0, $tabs/data/scroll/data_holder/data_container.get_child_count()): - var row = $tabs/data/scroll/data_holder/data_container.get_child(idx) - for jdx in range(0, row.get_child_count()): - var cell = row.get_child(jdx) - if(cell.get_prop_id() == prop_id): - cell.set_custom_minimum_size(prop_size) - if(jdx > prop_idx): - var pos = cell.get_position() - pos.x += diff_x - cell.set_position(pos) - -# called when the new_property button is pressed -func on_new_property_btn_pressed() -> void: - # print("GDDBTableEditor::on_new_property_btn_pressed()") - if(null == m_parent_table): - print("ERROR: GDDBTableEditor::on_new_property_btn_pressed() - m_parent_table is null") - return - var prop_idx = m_parent_table.get_props_count() - var prop_type = gddb_types.e_prop_type_bool - var prop_name = "Property_" + str(prop_idx+1) - var prop_id = m_parent_table.add_prop(prop_type, prop_name) - add_prop_to_structure(prop_id, prop_type, prop_name) - add_prop_to_data(prop_id, prop_type, prop_name, false) - - # enable add data btn - $tabs/data/add_data_btn.set_disabled(false) - - emit_signal("set_dirty") - -# adds a property to structure tab -func add_prop_to_structure(prop_id : int, prop_type : int, prop_name : String) -> void: - # print("GDDBTableEditor::add_prop_to_structure(" + str(prop_id) + ", " + str(prop_type) + ", " + prop_name + ")") - var prop = load(gddb_constants.c_addon_main_path + "table_property.tscn").instantiate() - $tabs/structure/scroll/properties.add_child(prop) - prop.set_parent_table(m_parent_table) - prop.setup(prop_id, prop_type, prop_name) - prop.connect("edit_property", Callable(self, "on_edit_property")) - prop.connect("delete_property", Callable(self, "on_delete_property")) - prop.connect("enable_autoincrement", Callable(self, "on_enable_prop_autoincrement")) - -# adds a property to data tab -func add_prop_to_data(prop_id : int, prop_type : int, prop_name : String, has_autoincrement : bool) -> void: - var prop = load(gddb_constants.c_addon_main_path + "data_label.tscn").instantiate() - $tabs/data/scroll/data_holder/data_header.add_child(prop) - prop.set_prop_id(prop_id) - prop.set_text(prop_name) - prop.connect("resize_property", Callable(self, "on_resize_property")) - - # add property to the existing rows - for idx in range(0, $tabs/data/scroll/data_holder/data_container.get_child_count()): - var row = $tabs/data/scroll/data_holder/data_container.get_child(idx) - var cell = load(gddb_constants.c_addon_main_path + "table_cell.tscn").instantiate() - row.add_child(cell) - cell.set_prop_id(prop_id) - cell.set_row_idx(idx) - cell.set_prop_type(prop_type) - cell.set_text("") - cell.enable_autoincrement(has_autoincrement) - cell.connect("edit_data", Callable(self, "on_edit_data")) - cell.connect("choose_resource", Callable(self, "on_choose_resource")) - cell.connect("choose_data", Callable(self, "on_choose_data")) - cell.connect("edit_string", Callable(self, "on_edit_string")) - -# called when the add data button is pressed -func on_add_row_data_btn_pressed() -> void: - # print("GDDBTableEditor::on_add_row_data_btn_pressed") - # add blank row in the table - var row_idx = m_parent_table.get_rows_count() - m_parent_table.add_blank_row() - - # add row in the interface - var row = HBoxContainer.new() - $tabs/data/scroll/data_holder/data_container.add_child(row) - for idx in range(0, $tabs/structure/scroll/properties.get_child_count()): - var cell = load(gddb_constants.c_addon_main_path + "table_cell.tscn").instantiate() - var prop = $tabs/structure/scroll/properties.get_child(idx) - var db_prop = m_parent_table.get_prop_by_id(idx) - row.add_child(cell) - cell.set_prop_id(idx) - cell.set_row_idx(row_idx) - cell.set_prop_type(prop.get_prop_type()) - var autoincrement = db_prop.has_autoincrement() - cell.enable_autoincrement(db_prop.has_autoincrement()) - if(autoincrement): - cell.set_text(str(row_idx+1)) - cell.connect("edit_data", Callable(self, "on_edit_data")) - cell.connect("choose_resource", Callable(self, "on_choose_resource")) - cell.connect("choose_data", Callable(self, "on_choose_data")) - cell.connect("edit_string", Callable(self, "on_edit_string")) - - emit_signal("set_dirty") - -# sets the table from database -func set_table(table : Object) -> void: - # print("GDDBTableEditor::set_table(" + table.get_table_name() + ")") - clear_current_layout() - - m_parent_table = table - fill_properties() - fill_data() - -# fills the interface with current table's properties -func fill_properties() -> void: - # print("GDDBTableEditor::fill_properties()") - var props_count = m_parent_table.get_props_count() - for idx in range(0, props_count): - var db_prop = m_parent_table.get_prop_at(idx) - add_prop_to_structure(db_prop.get_prop_id(), db_prop.get_prop_type(), db_prop.get_prop_name()) - var prop = load(gddb_constants.c_addon_main_path + "data_label.tscn").instantiate() - $tabs/data/scroll/data_holder/data_header.add_child(prop) - prop.set_prop_id(db_prop.get_prop_id()) - prop.set_prop_type(db_prop.get_prop_type()) - prop.set_text(db_prop.get_prop_name()) - prop.connect("resize_property", Callable(self, "on_resize_property")) - if(props_count > 0): - $tabs/data/add_data_btn.set_disabled(false) - -# fills the interface with current table's data -func fill_data() -> void: - #print("GDDBTableEditor::fill_data()") - var rows_count = m_parent_table.get_rows_count() - #print("Table name: " + m_parent_table.get_table_name()) - #print("rows_count: " + str(rows_count)) - for idx in range(0, rows_count): - var row = HBoxContainer.new() - $tabs/data/scroll/data_holder/data_container.add_child(row) - var data_row = m_parent_table.get_data_at_row_idx(idx) - for jdx in range(0, data_row.size()): - var db_prop = m_parent_table.get_prop_at(jdx) - #print("Prop id: " + str(db_prop.get_prop_id())) - #print("Prop type: " + str(db_prop.get_prop_type())) - #print("Prop name: " + str(db_prop.get_prop_name())) - - var cell = load(gddb_constants.c_addon_main_path + "table_cell.tscn").instantiate() - var cell_data = data_row[jdx].get_data() - - var prop_type = db_prop.get_prop_type() - if(prop_type >= gddb_types.e_prop_types_count): - var db = m_parent_table.get_parent_database() - var table = db.get_table_by_id(prop_type - gddb_types.e_prop_types_count) - var data_row_idx = cell_data.to_int() - cell_data = gddb_globals.get_json_from_row(table, data_row_idx) - - row.add_child(cell) - - cell.set_prop_id(data_row[jdx].get_prop_id()) - cell.set_row_idx(idx) - cell.set_prop_type(prop_type) - cell.set_text(cell_data) - cell.enable_autoincrement(db_prop.has_autoincrement()) - cell.connect("edit_data", Callable(self, "on_edit_data")) - cell.connect("choose_resource", Callable(self, "on_choose_resource")) - cell.connect("choose_data", Callable(self, "on_choose_data")) - cell.connect("edit_string", Callable(self, "on_edit_string")) - -# links properties -func link_props() -> void : - # print("GDDBTableEditor::link_props() for table with name: " + m_parent_table.get_table_name()) - for idx in range(0, $tabs/structure/scroll/properties.get_child_count()): - var prop = $tabs/structure/scroll/properties.get_child(idx) - prop.link() - -# refreshes autoincrement props -func refresh_autoincrement_props(prop_id, enable) -> void: - for idx in range(0, $tabs/data/scroll/data_holder/data_container.get_child_count()): - var row = $tabs/data/scroll/data_holder/data_container.get_child(idx) - for jdx in range(0, row.get_child_count()): - var cell = row.get_child(jdx) - if(cell.get_prop_id() == prop_id): - cell.enable_autoincrement(enable) - -# cleares current layout -func clear_current_layout() -> void: - # clear structure tab - for idx in range(0, $tabs/structure/scroll/properties.get_child_count()): - $tabs/structure/scroll/properties.get_child(idx).queue_free() - - # clear data from data tab - for idx in range(0, $tabs/data/scroll/data_holder/data_container.get_child_count()): - var row = $tabs/data/scroll/data_holder/data_container.get_child(idx) - for jdx in range(0, row.get_child_count()): - row.get_child(jdx).queue_free() - row.queue_free() - - # clear properties from data tab - for idx in range(0, $tabs/data/scroll/data_holder/data_header.get_child_count()): - $tabs/data/scroll/data_holder/data_header.get_child(idx).queue_free() - - $tabs/data/add_data_btn.set_disabled(true) - -# called when a property is edited -func on_edit_property(prop_id : int, prop_type : int, prop_name : String) -> void: - """ - print("GDDBTableEditor::on_edit_property(" + str(prop_id) + ", " + str(prop_type) + ", " + prop_name + ")") - if(prop_type >= gddb_types.e_prop_types_count): - var db = m_parent_table.get_parent_database() - var selected_table = db.get_table_by_id(gddb_types.e_prop_types_count - prop_type) - print("GDDBTableEditor::on_edit_property(" + str(prop_id) + ", " + selected_table.get_table_name() + ", " + prop_name + ")") - else: - print("GDDBTableEditor::on_edit_property(" + str(prop_id) + ", " + gddb_globals.get_data_name(prop_type) + ", " + prop_name + ")") - #""" - # edit prop in the table - m_parent_table.edit_prop(prop_id, prop_type, prop_name) - - # refresh the prop name in data tab - for idx in range(0, $tabs/data/scroll/data_holder/data_header.get_child_count()): - var prop = $tabs/data/scroll/data_holder/data_header.get_child(idx) - if(prop.get_prop_id() == prop_id): - prop.set_text(prop_name) - - # update data type - for idx in range(0, $tabs/data/scroll/data_holder/data_container.get_child_count()): - var row = $tabs/data/scroll/data_holder/data_container.get_child(idx) - for jdx in range(0, row.get_child_count()): - var cell = row.get_child(jdx) - if(cell.get_prop_id() == prop_id): - """ - if(prop_type < gddb_types.e_prop_types_count): - print("Prop type: " + gddb_globals.get_data_name(prop_type)) - else: - print("Prop type: custom") - """ - cell.set_prop_type(prop_type) - - emit_signal("set_dirty") - -# called when a property is deleted -func on_delete_property(prop_id : int) -> void: - # print("GDDBTableEditor::on_delete_property(" + str(prop_id) + ")") - var prop = m_parent_table.get_prop_by_id(prop_id) - $delete_prop_dlg.set_prop_id(prop_id) - $delete_prop_dlg.set_prop_name(prop.get_prop_name()) - $delete_prop_dlg.popup_centered() - -# called when a property is deleted -func on_confirm_delete_property() -> void: - var prop_id = $delete_prop_dlg.get_prop_id() - - # deletes a property from table; also all data by this property - m_parent_table.delete_prop(prop_id) - - # delete cells from data tab - for idx in range(0, $tabs/data/scroll/data_holder/data_container.get_child_count()): - var row = $tabs/data/scroll/data_holder/data_container.get_child(idx) - for jdx in range(0, row.get_child_count()): - var cell = row.get_child(jdx) - if(cell.get_prop_id() == prop_id): - cell.disconnect("edit_data", Callable(self, "on_edit_data")) - cell.queue_free() - break - - # delete property from data tab - for idx in range(0, $tabs/data/scroll/data_holder/data_header.get_child_count()): - var prop = $tabs/data/scroll/data_holder/data_header.get_child(idx) - if(prop.get_prop_id() == prop_id): - prop.queue_free() - break - - # delete prop from structure - for idx in range(0, $tabs/structure/scroll/properties.get_child_count()): - var prop = $tabs/structure/scroll/properties.get_child(idx) - if(prop.get_prop_id() == prop_id): - prop.queue_free() - break - - # refresh the add data button - var props_count = m_parent_table.get_props_count() - if(props_count == 0): - $tabs/data/add_data_btn.set_disabled(true) - - emit_signal("set_dirty") - -# called when the property has autoincrement or not -func on_enable_prop_autoincrement(prop_id : int, enable : bool) -> void : - m_parent_table.enable_prop_autoincrement(prop_id, enable) - emit_signal("set_dirty") - refresh_autoincrement_props(prop_id, enable) - - # reindex all data - if(enable): - for idx in range(0, $tabs/data/scroll/data_holder/data_container.get_child_count()): - var row = $tabs/data/scroll/data_holder/data_container.get_child(idx) - for jdx in range(0, row.get_child_count()): - var cell = row.get_child(jdx) - if(cell.get_prop_id() == prop_id): - cell.set_text(str(idx + 1)) - -# called when edit data -func on_edit_data(prop_id : int, row_idx : int, data : String) -> void: - m_parent_table.edit_data(prop_id, row_idx, data) - emit_signal("set_dirty") - -# called when choosing a resource -func on_choose_resource(prop_id : int, row_idx : int) -> void: - # print("GDDBTableEditor::on_choose_resource(" + str(prop_id) + ", " + str(row_idx) + ")") - $load_res_path_dlg.set_prop_id(prop_id) - $load_res_path_dlg.set_row_idx(row_idx) - $load_res_path_dlg.popup_centered() - -# called when choosing a data -func on_choose_data(prop_id : int, row_idx : int, prop_type : int) -> void: - # print("GDDBTableEditor::on_choose_data(" + str(prop_id) + ", " + str(row_idx) + ", " + str(prop_type) + ")") - $data_dlg.set_prop_id(prop_id) - $data_dlg.set_row_idx(row_idx) - var table_id = prop_type - gddb_types.e_prop_types_count - var db = m_parent_table.get_parent_database() - var tbl = db.get_table_by_id(table_id) - $data_dlg.set_table(tbl) - $data_dlg.popup_centered() - -# called when choosing to edit string -func on_edit_string(prop_id : int, row_idx : int, text : String) -> void: - $edit_string_dlg.set_prop_id(prop_id) - $edit_string_dlg.set_row_idx(row_idx) - $edit_string_dlg.set_data_text(text) - $edit_string_dlg.popup_centered() - -# called when selecting a resource filepath -func on_select_res_path(filepath : String) -> void: - # print("GDDBTableEditor::on_select_res_path(" + filepath + ")") - var prop_id = $load_res_path_dlg.get_prop_id() - var row_idx = $load_res_path_dlg.get_row_idx() - m_parent_table.edit_data(prop_id, row_idx, filepath) - var row = $tabs/data/scroll/data_holder/data_container.get_child(row_idx) - for idx in range(0, row.get_child_count()): - var cell = row.get_child(idx) - if(cell.get_prop_id() == prop_id): - cell.set_text(filepath) - - emit_signal("set_dirty") - -# called when data from a table is choosen -func on_select_data(prop_id : int, row_idx : int, data_row_idx : int, data : String) -> void: - # set the data in the databes / table - m_parent_table.edit_data(prop_id, row_idx, str(data_row_idx)) - - # fill in the interface cell with data - var row = $tabs/data/scroll/data_holder/data_container.get_child(row_idx) - for idx in range(0, row.get_child_count()): - var cell = row.get_child(idx) - if(cell.get_prop_id() == prop_id): - cell.set_text(data) - break - - emit_signal("set_dirty") - -# called when the text is edited -func on_text_edited(): - var prop_id = $edit_string_dlg.get_prop_id() - var row_idx = $edit_string_dlg.get_row_idx() - var text_data = $edit_string_dlg.get_data_text() - - var data = gddb_globals.handle_string(text_data) - - # print("GDDBTableEditor::on_text_edited() - prop_id: " + str(prop_id) + ", row_idx: " + str(row_idx) + ", data: " + text_data) - - m_parent_table.edit_data(prop_id, row_idx, data) - - var row = $tabs/data/scroll/data_holder/data_container.get_child(row_idx) - for idx in range(0, row.get_child_count()): - var cell = row.get_child(idx) - if(cell.get_prop_id() == prop_id): - cell.set_text(text_data) - break - - emit_signal("set_dirty") diff --git a/addons/godot_db_manager/table_editor.tscn b/addons/godot_db_manager/table_editor.tscn deleted file mode 100644 index a9545ea..0000000 --- a/addons/godot_db_manager/table_editor.tscn +++ /dev/null @@ -1,161 +0,0 @@ -[gd_scene load_steps=16 format=2] - -[ext_resource path="res://addons/godot_db_manager/dlgs/error_dlg.tscn" type="PackedScene" id=1] -[ext_resource path="res://addons/godot_db_manager/dlgs/load_res_path_dlg.tscn" type="PackedScene" id=2] -[ext_resource path="res://addons/godot_db_manager/table_editor.gd" type="Script" id=3] -[ext_resource path="res://addons/godot_db_manager/dlgs/data_dlg.tscn" type="PackedScene" id=4] -[ext_resource path="res://addons/godot_db_manager/dlgs/edit_string_dlg.tscn" type="PackedScene" id=5] -[ext_resource path="res://addons/godot_db_manager/dlgs/delete_prop_dlg.tscn" type="PackedScene" id=6] -[ext_resource path="res://addons/godot_db_manager/assets/fnt/roboto_18.tres" type="FontFile" id=7] -[ext_resource path="res://addons/godot_db_manager/assets/tex/gui.png" type="Texture2D" id=8] - -[sub_resource type="AtlasTexture" id=1] -atlas = ExtResource( 8 ) -region = Rect2( 2, 2, 24, 24 ) - -[sub_resource type="AtlasTexture" id=2] -atlas = ExtResource( 8 ) -region = Rect2( 2, 54, 24, 24 ) - -[sub_resource type="AtlasTexture" id=3] -atlas = ExtResource( 8 ) -region = Rect2( 2, 28, 24, 24 ) - -[sub_resource type="AtlasTexture" id=4] -atlas = ExtResource( 8 ) -region = Rect2( 2, 2, 24, 24 ) - -[sub_resource type="AtlasTexture" id=5] -atlas = ExtResource( 8 ) -region = Rect2( 2, 54, 24, 24 ) - -[sub_resource type="AtlasTexture" id=6] -atlas = ExtResource( 8 ) -region = Rect2( 2, 28, 24, 24 ) - -[sub_resource type="AtlasTexture" id=7] -atlas = ExtResource( 8 ) -region = Rect2( 132, 2, 24, 24 ) - -[node name="table" type="Control"] -anchor_right = 1.0 -anchor_bottom = 1.0 -script = ExtResource( 3 ) -__meta__ = { -"_edit_horizontal_guides_": [ 584.0, 64.0, 70.0 ], -"_edit_use_anchors_": false, -"_edit_vertical_guides_": [ 1000.47 ] -} - -[node name="tabs" type="TabContainer" parent="."] -anchor_right = 1.0 -anchor_bottom = 1.0 -theme_override_fonts/font = ExtResource( 7 ) -__meta__ = { -"_edit_use_anchors_": false -} - -[node name="structure" type="TabBar" parent="tabs"] -visible = false -anchor_right = 1.0 -anchor_bottom = 1.0 -offset_left = 4.0 -offset_top = 40.0 -offset_right = -4.0 -offset_bottom = -4.0 -tab_close_display_policy = 2 - -[node name="header" type="HBoxContainer" parent="tabs/structure"] -offset_right = 892.0 -offset_bottom = 24.0 -__meta__ = { -"_edit_use_anchors_": false -} - -[node name="new_property_btn" type="TextureButton" parent="tabs/structure/header"] -offset_right = 24.0 -offset_bottom = 24.0 -texture_normal = SubResource( 1 ) -texture_pressed = SubResource( 2 ) -texture_hover = SubResource( 3 ) - -[node name="props_lbl" type="Label" parent="tabs/structure/header"] -offset_left = 28.0 -offset_top = 1.0 -offset_right = 114.0 -offset_bottom = 23.0 -theme_override_fonts/font = ExtResource( 7 ) -text = "Properties:" -__meta__ = { -"_edit_use_anchors_": false -} - -[node name="scroll" type="ScrollContainer" parent="tabs/structure"] -anchor_right = 1.0 -anchor_bottom = 1.0 -offset_top = 30.0 -__meta__ = { -"_edit_use_anchors_": false -} - -[node name="properties" type="VBoxContainer" parent="tabs/structure/scroll"] -__meta__ = { -"_edit_use_anchors_": false -} - -[node name="data" type="TabBar" parent="tabs"] -anchor_right = 1.0 -anchor_bottom = 1.0 -offset_left = 4.0 -offset_top = 40.0 -offset_right = -4.0 -offset_bottom = -4.0 -tab_close_display_policy = 2 - -[node name="add_data_btn" type="TextureButton" parent="tabs/data"] -offset_right = 24.0 -offset_bottom = 24.0 -disabled = true -texture_normal = SubResource( 4 ) -texture_pressed = SubResource( 5 ) -texture_hover = SubResource( 6 ) -texture_disabled = SubResource( 7 ) -__meta__ = { -"_edit_use_anchors_": false -} - -[node name="scroll" type="ScrollContainer" parent="tabs/data"] -anchor_right = 1.0 -anchor_bottom = 1.0 -offset_top = 38.0 -__meta__ = { -"_edit_use_anchors_": false -} - -[node name="data_holder" type="VBoxContainer" parent="tabs/data/scroll"] -offset_bottom = 36.0 -__meta__ = { -"_edit_use_anchors_": false -} - -[node name="data_header" type="HBoxContainer" parent="tabs/data/scroll/data_holder"] -offset_bottom = 32.0 -custom_minimum_size = Vector2( 0, 32 ) -clip_contents = true - -[node name="data_container" type="VBoxContainer" parent="tabs/data/scroll/data_holder"] -offset_top = 36.0 -offset_bottom = 36.0 - -[node name="error_dlg" parent="." instance=ExtResource( 1 )] -visible = false - -[node name="load_res_path_dlg" parent="." instance=ExtResource( 2 )] -filters = PackedStringArray( "*.res, *.tres ; Godot resource file types", "*.scn, *.tscn, *escn ; Godot scene file types", "*.gd, *.cs, *.h, *.c, *.hpp, *.cpp ; Code file types", "*.shader ; Shader file types", "*.mat ; Material file types", "*.dae, *.gltf, *.obj, *.fbx ; Mesh file types", "*.anim ; Animation file types", "*.ttf, *.otf ; Font file types", "*.png, *.jpg, *.jpeg, *.tiff, *.tga, *.bmp, *.webp, *.gif, *.hdr ; Images file types", "*.snd, *.wav, *.ogg, *.mp3 ; Sound file types", "*.ogg, *.mpg, *.mpeg, *.avi, *.mov, *.mp4, *.webm ; Video file types", "*.txt, *.csv, *.json, *.xml, *.cfg, *.ini ; Text file types", "*.doc, *.docx, *.xls, *.xlsx, *.odt, *.ods, *.pdf ; Doc file types", "*.dat, *.raw ; Binary data file types" ) - -[node name="data_dlg" parent="." instance=ExtResource( 4 )] - -[node name="edit_string_dlg" parent="." instance=ExtResource( 5 )] - -[node name="delete_prop_dlg" parent="." instance=ExtResource( 6 )] -visible = false diff --git a/addons/godot_db_manager/table_item.gd b/addons/godot_db_manager/table_item.gd deleted file mode 100644 index 42f505f..0000000 --- a/addons/godot_db_manager/table_item.gd +++ /dev/null @@ -1,67 +0,0 @@ -@tool -""" -class GDDBTableItem -""" - -class_name GDDBTableItem - -extends Control - -signal select_item -signal edit_table -signal delete_table - -var m_table_id = gddb_constants.c_invalid_id -var m_table_name = "" - -var m_is_selected = false - -# Called when the node enters the scene tree for the first time. -func _ready(): - $select_btn.connect("pressed", Callable(self, "on_select_btn_pressed")) - $edit_table_btn.connect("pressed", Callable(self, "on_edit_table_btn_pressed")) - $delete_table_btn.connect("pressed", Callable(self, "on_delete_table_btn_pressed")) - $select.hide() - -# sets the table id -func set_table_id(id : int) -> void: - m_table_id = id - -# returns the table id -func get_table_id() -> int: - return m_table_id - -func set_selected(select : bool) -> void : - m_is_selected = select - if(m_is_selected): - $select.show() - else: - $select.hide() - -func is_selected(): - return m_is_selected - -# sets the table name -func set_table_name(name : String) -> void: - # print("GDDBTableItem::set_table_name(" + name + ")") - m_table_name = name - $table_name.set_text(m_table_name) - -# returns the table name -func get_table_name() -> String: - return m_table_name - -# called when the user presses the edit_table button -func on_edit_table_btn_pressed(): - # print("GDDBTableItem::on_edit_table_btn_pressed") - emit_signal("edit_table", m_table_id, m_table_name) - -# called when the user presses the delete_table button -func on_delete_table_btn_pressed(): - # print("GDDBTableItem::on_delete_table_btn_pressed") - emit_signal("delete_table", m_table_id) - -# called when select_btn is pressed -func on_select_btn_pressed() -> void: - # print("GDDBTableItem::on_select_btn_pressed()") - emit_signal("select_item", m_table_id) diff --git a/addons/godot_db_manager/table_item.tscn b/addons/godot_db_manager/table_item.tscn deleted file mode 100644 index bb0e4e3..0000000 --- a/addons/godot_db_manager/table_item.tscn +++ /dev/null @@ -1,109 +0,0 @@ -[gd_scene load_steps=11 format=2] - -[ext_resource path="res://addons/godot_db_manager/assets/fnt/roboto_18.tres" type="FontFile" id=1] -[ext_resource path="res://addons/godot_db_manager/assets/tex/gui.png" type="Texture2D" id=2] -[ext_resource path="res://addons/godot_db_manager/table_item.gd" type="Script" id=3] -[ext_resource path="res://addons/godot_db_manager/assets/tex/debug.png" type="Texture2D" id=4] - -[sub_resource type="AtlasTexture" id=1] -atlas = ExtResource( 2 ) -region = Rect2( 28, 2, 24, 24 ) - -[sub_resource type="AtlasTexture" id=2] -atlas = ExtResource( 2 ) -region = Rect2( 28, 54, 24, 24 ) - -[sub_resource type="AtlasTexture" id=3] -atlas = ExtResource( 2 ) -region = Rect2( 28, 28, 24, 24 ) - -[sub_resource type="AtlasTexture" id=4] -atlas = ExtResource( 2 ) -region = Rect2( 54, 2, 24, 24 ) - -[sub_resource type="AtlasTexture" id=5] -atlas = ExtResource( 2 ) -region = Rect2( 54, 54, 24, 24 ) - -[sub_resource type="AtlasTexture" id=6] -atlas = ExtResource( 2 ) -region = Rect2( 54, 28, 24, 24 ) - -[node name="table_item" type="Control"] -anchor_right = 1.0 -anchor_bottom = 0.021 -offset_bottom = 0.399998 -custom_minimum_size = Vector2( 180, 34 ) -script = ExtResource( 3 ) -__meta__ = { -"_edit_horizontal_guides_": [ 34.0 ], -"_edit_use_anchors_": false, -"_edit_vertical_guides_": [ 170.0 ] -} - -[node name="dbg" type="NinePatchRect" parent="."] -visible = false -anchor_right = 1.0 -anchor_bottom = 1.0 -texture = ExtResource( 4 ) -region_rect = Rect2( 38, 38, 10, 10 ) -__meta__ = { -"_edit_use_anchors_": false -} - -[node name="select" type="NinePatchRect" parent="."] -visible = false -anchor_right = 1.0 -anchor_bottom = 1.0 -texture = ExtResource( 2 ) -region_rect = Rect2( 80, 6, 24, 4 ) -__meta__ = { -"_edit_use_anchors_": false -} - -[node name="table_name" type="Label" parent="."] -anchor_right = 1.0 -anchor_bottom = 1.0 -offset_right = -6.10352e-05 -offset_bottom = -4.0 -custom_minimum_size = Vector2( 100, 0 ) -theme_override_fonts/font = ExtResource( 1 ) -text = "Table_999" -valign = 1 -__meta__ = { -"_edit_use_anchors_": false -} - -[node name="select_btn" type="TextureButton" parent="."] -anchor_right = 1.0 -anchor_bottom = 1.0 -__meta__ = { -"_edit_use_anchors_": false -} - -[node name="edit_table_btn" type="TextureButton" parent="."] -anchor_left = 1.0 -anchor_right = 1.0 -anchor_bottom = 1.0 -offset_left = -56.0 -offset_top = 5.0 -offset_right = -32.0 -offset_bottom = -5.0 -texture_normal = SubResource( 1 ) -texture_pressed = SubResource( 2 ) -texture_hover = SubResource( 3 ) -__meta__ = { -"_edit_use_anchors_": false -} - -[node name="delete_table_btn" type="TextureButton" parent="."] -anchor_left = 1.0 -anchor_right = 1.0 -anchor_bottom = 1.0 -offset_left = -28.0 -offset_top = 5.0 -offset_right = -4.0 -offset_bottom = -5.0 -texture_normal = SubResource( 4 ) -texture_pressed = SubResource( 5 ) -texture_hover = SubResource( 6 ) diff --git a/addons/godot_db_manager/table_property.gd b/addons/godot_db_manager/table_property.gd deleted file mode 100644 index 4cb7c74..0000000 --- a/addons/godot_db_manager/table_property.gd +++ /dev/null @@ -1,195 +0,0 @@ -@tool -""" -class GDDBTableProperty -""" - -class_name GDDBTableProperty - -extends Control - -signal delete_property -signal edit_property -signal enable_autoincrement - -var m_prop_id : int = -1 -var m_prop_type : int = 0 -var m_prop_name : String = "" - -var m_parent_table = null - -# Called when the node enters the scene tree for the first time. -func _ready() -> void: - $prop_name.connect("text_changed", Callable(self, "on_name_changed")) - - $prop_type.clear() - for idx in range(0, gddb_types.e_prop_types_count): - $prop_type.add_item(gddb_globals.get_data_name(idx), gddb_types.e_prop_type_bool + idx) - $prop_type.select(0) - - $prop_type.get_popup().connect("about_to_popup", Callable(self, "on_about_to_show")) - $prop_type.connect("item_selected", Callable(self, "on_type_changed")) - - $delete_button.connect("pressed", Callable(self, "on_delete_button_pressed")) - - $autoincrement_btn.hide() - $autoincrement_btn.connect("toggled", Callable(self, "on_set_autoincrement")) - -# setup property -func setup(prop_id : int, prop_type : int, prop_name : String) -> void: - """ - if(prop_type < gddb_types.e_prop_types_count): - print("GDDBTableProperty::setup(" + str(prop_id) + ", " + gddb_globals.get_data_name(prop_type) + ", " + prop_name + ")") - else: - var db = m_parent_table.get_parent_database() - var table = db.get_table_by_id(prop_type - gddb_types.e_prop_types_count) - print("GDDBTableProperty::setup(" + str(prop_id) + ", " + table.get_table_name() + ", " + prop_name + ")") - #""" - set_prop_id(prop_id) - set_prop_type(prop_type) - set_prop_name(prop_name) - -# sets parent table -func set_parent_table(table): - #print("GDDBTableProperty::set_parent_table(" + str(table) + ")") - m_parent_table = table - var db = m_parent_table.get_parent_database() - for idx in range(0, db.get_tables_count()): - var tbl = db.get_table_at(idx) - if(tbl == m_parent_table): - continue - $prop_type.add_item(tbl.get_table_name(), gddb_types.e_prop_types_count + tbl.get_table_id()) - -# sets proprty id -func set_prop_id(prop_id : int) -> void: - # print("GDDBTableProperty::set_prop_id(" + str(prop_id) + ")") - m_prop_id = prop_id - -# returns property id -func get_prop_id() -> int: - return m_prop_id - -# sets property type -func set_prop_type(prop_type : int) -> void: - """ - print("GDDBTableProperty::set_prop_type(" + str(prop_type) + ")") - if(prop_type < gddb_types.e_prop_types_count): - print("GDDBTableProperty::set_prop_type(" + gddb_globals.get_data_name(prop_type) + ")") - else: - var db = m_parent_table.get_parent_database() - var table = db.get_table_by_id(prop_type - gddb_types.e_prop_types_count) - print("GDDBTableProperty::set_prop_type(" + table.get_table_name() + ")") - #""" - #if(prop_type >= gddb_types.e_prop_types_count): - # print("GDDBTableProperty::set_prop_type(" + str(prop_type) + ")") - m_prop_type = prop_type - select_current_prop() - - if(m_prop_type == gddb_types.e_prop_type_int): - $autoincrement_btn.show() - var prop = m_parent_table.get_prop_by_id(m_prop_id) - if(prop.has_autoincrement()): - $autoincrement_btn.set_pressed(true) - else: - $autoincrement_btn.hide() - -# selects current property -func select_current_prop() -> void: - if(m_prop_type < gddb_types.e_prop_types_count): - $prop_type.select(m_prop_type) - -# links property type to other tables -func link(): - # print("GDDBTableProperty::link()") - refill_list() - if(m_prop_type >= gddb_types.e_prop_types_count): - """ - print("m_prop_id : " + str(m_prop_id)) - print("m_prop_type : " + str(m_prop_type)) - print("m_prop_name : " + m_prop_name) - """ - set_selection_by_id(m_prop_type) - else: - $prop_type.select(m_prop_type) - -# returns property type -func get_prop_type() -> int: - return m_prop_type - -# sets property name -func set_prop_name(prop_name : String) -> void: - # print("GDDBTableProperty::set_prop_name(" + prop_name + ")") - m_prop_name = prop_name - $prop_name.set_text(m_prop_name) - -# returns property name -func get_prop_name() -> String: - return m_prop_name - -# called everytime the name of the property is changed -func on_name_changed(new_text : String) -> void: - m_prop_name = new_text - emit_signal("edit_property", m_prop_id, m_prop_type, m_prop_name) - -# called when the popup from option button is about to be shown -func on_about_to_show(): - var selected_id = $prop_type.get_selected_id() - # print("GDDBTableProperty::on_about_to_show() - " + str(selected_id)) - - refill_list() - set_selection_by_id(selected_id) - -# refills the list -func refill_list() -> void : - $prop_type.clear() - for idx in range(0, gddb_types.e_prop_types_count): - $prop_type.add_item(gddb_globals.get_data_name(idx), gddb_types.e_prop_type_bool + idx) - - if(null != m_parent_table): - var db = m_parent_table.get_parent_database() - for idx in range(0, db.get_tables_count()): - var table = db.get_table_at(idx) - if(table == m_parent_table): - continue - """ - print("GDDBTableProperty::refill_list - Add:") - print("table id: " + str(table.get_table_id())) - print("table name: " + table.get_table_name()) - #""" - # print("GDDBTableProperty::prop_type.add_item(" + table.get_table_name() + ", " + str(gddb_types.e_prop_types_count + table.get_table_id()) + ")" ) - $prop_type.add_item(table.get_table_name(), gddb_types.e_prop_types_count + table.get_table_id()) - # $prop_type.select(selected_idx) - -# sets selection -func set_selection_by_id(selected_id : int) -> void : - # print("GDDBTableProperty::set_selection_by_id(" + str(selected_id) + ")") - for idx in range(0, $prop_type.get_item_count()): - if($prop_type.get_item_id(idx) == selected_id): - $prop_type.select(idx) - break - -func on_set_autoincrement(enable : bool) -> void: - # print("GDDBTableProperty::on_set_autoincrement(" + str(enable) + ") - " + str(m_prop_id)) - emit_signal("enable_autoincrement", m_prop_id, enable) - -# called everytime the type of the property is changed -func on_type_changed(option_idx : int) -> void: - var option_id = $prop_type.get_item_id(option_idx) - """ - print("GDDBTableProperty::on_type_changed(" + str(option_idx) + ")") - print("option_id = " + str(option_id)) - if(option_id >= gddb_types.e_prop_types_count): - print("GDDBTableProperty::on_type_changed(" + str(option_id) + ")") - else: - print("GDDBTableProperty::on_type_changed(" + gddb_globals.get_data_name(option_id) + ")") - #""" - m_prop_type = option_id - $autoincrement_btn.set_pressed(false) - if(m_prop_type == gddb_types.e_prop_type_int): - $autoincrement_btn.show() - else: - $autoincrement_btn.hide() - emit_signal("edit_property", m_prop_id, m_prop_type, m_prop_name) - -# called when the delete property button is pressed -func on_delete_button_pressed() -> void: - emit_signal("delete_property", m_prop_id) diff --git a/addons/godot_db_manager/table_property.tscn b/addons/godot_db_manager/table_property.tscn deleted file mode 100644 index e210e3c..0000000 --- a/addons/godot_db_manager/table_property.tscn +++ /dev/null @@ -1,89 +0,0 @@ -[gd_scene load_steps=7 format=2] - -[ext_resource path="res://addons/godot_db_manager/assets/tex/gui.png" type="Texture2D" id=1] -[ext_resource path="res://addons/godot_db_manager/table_property.gd" type="Script" id=2] -[ext_resource path="res://addons/godot_db_manager/assets/fnt/roboto_18.tres" type="FontFile" id=3] - -[sub_resource type="AtlasTexture" id=1] -atlas = ExtResource( 1 ) -region = Rect2( 54, 2, 24, 24 ) - -[sub_resource type="AtlasTexture" id=2] -atlas = ExtResource( 1 ) -region = Rect2( 54, 54, 24, 24 ) - -[sub_resource type="AtlasTexture" id=3] -atlas = ExtResource( 1 ) -region = Rect2( 54, 28, 24, 24 ) - -[node name="table_property" type="Control"] -anchor_right = 1.0 -offset_bottom = 15.36 -custom_minimum_size = Vector2( 900, 32 ) -script = ExtResource( 2 ) -__meta__ = { -"_edit_horizontal_guides_": [ 32.0 ], -"_edit_use_anchors_": true -} - -[node name="delete_button" type="TextureButton" parent="."] -offset_left = 3.0 -offset_top = 4.0 -offset_right = 27.0 -offset_bottom = 28.0 -texture_normal = SubResource( 1 ) -texture_pressed = SubResource( 2 ) -texture_hover = SubResource( 3 ) -__meta__ = { -"_edit_use_anchors_": false -} - -[node name="prop_name_label" type="Label" parent="."] -offset_left = 28.0 -offset_top = 2.0 -offset_right = 97.0 -offset_bottom = 24.0 -theme_override_fonts/font = ExtResource( 3 ) -text = " Name:" -__meta__ = { -"_edit_use_anchors_": false -} - -[node name="prop_name" type="LineEdit" parent="."] -offset_left = 109.0 -offset_right = 409.0 -offset_bottom = 32.0 -custom_minimum_size = Vector2( 300, 0 ) -theme_override_fonts/font = ExtResource( 3 ) -__meta__ = { -"_edit_use_anchors_": false -} - -[node name="type_label" type="Label" parent="."] -offset_left = 413.0 -offset_top = 2.0 -offset_right = 473.0 -offset_bottom = 24.0 -theme_override_fonts/font = ExtResource( 3 ) -text = " Type:" - -[node name="prop_type" type="OptionButton" parent="."] -offset_left = 485.0 -offset_right = 735.0 -offset_bottom = 32.0 -custom_minimum_size = Vector2( 250, 0 ) -theme_override_fonts/font = ExtResource( 3 ) -text = "Bool" -items = [ "Bool", null, false, 0, null, "Integer", null, false, 1, null, "Float", null, false, 2, null, "String", null, false, 3, null, "Resource", null, false, 4, null ] -selected = 0 - -[node name="autoincrement_btn" type="CheckBox" parent="."] -visible = false -offset_left = 740.0 -offset_right = 891.0 -offset_bottom = 32.0 -theme_override_fonts/font = ExtResource( 3 ) -text = "Auto increment" -__meta__ = { -"_edit_use_anchors_": false -} diff --git a/addons/godot_db_manager/tables_header.gd b/addons/godot_db_manager/tables_header.gd deleted file mode 100644 index 9c94b77..0000000 --- a/addons/godot_db_manager/tables_header.gd +++ /dev/null @@ -1,17 +0,0 @@ -@tool -""" -class GDDBTablesHeader -""" - -class_name GDDBTablesHeader - -extends Control - -signal add_table - -# Called when the node enters the scene tree for the first time. -func _ready(): - $add_table_btn.connect("pressed", Callable(self, "on_add_table_btn_pressed")) - -func on_add_table_btn_pressed(): - emit_signal("add_table") diff --git a/addons/godot_db_manager/tables_header.tscn b/addons/godot_db_manager/tables_header.tscn deleted file mode 100644 index d98e8fd..0000000 --- a/addons/godot_db_manager/tables_header.tscn +++ /dev/null @@ -1,65 +0,0 @@ -[gd_scene load_steps=7 format=2] - -[ext_resource path="res://addons/godot_db_manager/assets/fnt/roboto_20.tres" type="FontFile" id=1] -[ext_resource path="res://addons/godot_db_manager/tables_header.gd" type="Script" id=2] -[ext_resource path="res://addons/godot_db_manager/assets/tex/gui.png" type="Texture2D" id=3] - -[sub_resource type="AtlasTexture" id=1] -atlas = ExtResource( 3 ) -region = Rect2( 2, 2, 24, 24 ) - -[sub_resource type="AtlasTexture" id=2] -atlas = ExtResource( 3 ) -region = Rect2( 2, 54, 24, 24 ) - -[sub_resource type="AtlasTexture" id=3] -atlas = ExtResource( 3 ) -region = Rect2( 2, 28, 24, 24 ) - -[node name="tables_header" type="Control"] -anchor_right = 0.188889 -anchor_bottom = 0.019 -offset_right = 6.10352e-05 -offset_bottom = -0.4 -custom_minimum_size = Vector2( 170, 30 ) -script = ExtResource( 2 ) -__meta__ = { -"_edit_use_anchors_": true, -"_edit_vertical_guides_": [ 170.0 ] -} - -[node name="add_table_btn" type="TextureButton" parent="."] -offset_left = 2.0 -offset_top = 2.0 -offset_right = 26.0 -offset_bottom = 26.0 -texture_normal = SubResource( 1 ) -texture_pressed = SubResource( 2 ) -texture_hover = SubResource( 3 ) -__meta__ = { -"_edit_use_anchors_": false -} - -[node name="tables_lbl" type="Label" parent="."] -anchor_left = 0.165 -anchor_right = 1.0 -anchor_bottom = 0.8 -offset_left = -0.0500259 -offset_right = -0.000152588 -theme_override_fonts/font = ExtResource( 1 ) -text = "Tables " -align = 1 -__meta__ = { -"_edit_use_anchors_": false -} - -[node name="line" type="NinePatchRect" parent="."] -anchor_right = 1.0 -anchor_bottom = 1.0 -offset_top = 29.0 -offset_right = -0.000152588 -texture = ExtResource( 3 ) -region_rect = Rect2( 80, 2, 24, 2 ) -__meta__ = { -"_edit_use_anchors_": false -} diff --git a/addons/godot_db_manager/tables_list.gd b/addons/godot_db_manager/tables_list.gd deleted file mode 100644 index e0e1c67..0000000 --- a/addons/godot_db_manager/tables_list.gd +++ /dev/null @@ -1,129 +0,0 @@ -@tool -""" -class GDDBTablesList -""" - -class_name GDDBTablesList - -extends Control - -signal resize_tables_list - -signal add_table -signal edit_table_name -signal delete_table -signal select_table - -var m_tables = [] - -var m_mouse_pos_pressed : Vector2 = Vector2() -var m_mouse_pressed : bool = false - -# Called when the node enters the scene tree for the first time. -func _ready() -> void: - m_mouse_pos_pressed = Vector2() - m_mouse_pressed = false - - $tables_header.connect("add_table", Callable(self, "on_add_table")) - -# called when the node gets an input -func _input(event : InputEvent) -> void : - if(!gddb_globals.is_interface_active()): - return - - var evLocal = $resize_ctrl.make_input_local(event) - - if event is InputEventMouseButton : - if(event.button_index == MOUSE_BUTTON_LEFT): - if(event.pressed): - var rect = Rect2(Vector2(0, 0), $resize_ctrl.get_size()) - var inside = rect.has_point(evLocal.position) - if(inside): - m_mouse_pressed = true - m_mouse_pos_pressed = evLocal.position - else: - m_mouse_pressed = false - - elif event is InputEventMouseMotion : - if(m_mouse_pressed): - var diff_x = evLocal.position.x - m_mouse_pos_pressed.x - emit_signal("resize_tables_list", diff_x) - -# custom resizing the tables list -func resize_content(size : Vector2) -> void : - set_size(size) - - # I have no idea why I need to do this; this should be done automatically - var content_size = $tables_container/tables.get_size() - content_size.x = size.x - $tables_container/tables.set_custom_minimum_size(content_size) - -# Called when the user presses the "add_table" button from the tables_list/header -func on_add_table() -> void : - # print("GDDBTablesList::on_add_table()") - emit_signal("add_table") - -# creates a table -func create_table(db_table : Object, select_table : bool = true) -> void: - # print("GDDBTablesList::create_table(" + str(db_table) + ")") - var table = load(gddb_constants.c_addon_main_path + "table_item.tscn").instantiate() - var table_id = db_table.get_table_id() - table.set_table_id(table_id) - table.set_table_name(db_table.get_table_name()) - table.connect("select_item", Callable(self, "on_select_item")) - table.connect("edit_table", Callable(self, "on_edit_table_name")) - table.connect("delete_table", Callable(self, "on_delete_table")) - m_tables.push_back(table) - $tables_container/tables.add_child(table) - if(select_table): - select_item_by_id(table_id) - -# Called when the user presses the "edit_table" button from the tables_list/table -func on_edit_table_name(table_id : int, table_name : String) -> void: - # print("GDDBTablesList::on_edit_table_name(" + str(table_id) + ", " + table_name + ")") - emit_signal("edit_table_name", table_id, table_name) - -# Called when the user presses the "delete_table" button from the tables_list/table -func on_delete_table(table_id : int) -> void: - # print("GDDBTablesList::on_delete_table(" + str(table_id) + ")") - emit_signal("delete_table", table_id) - -# edits the table name -func edit_table_name(table_id: int, table_name : String) -> void: - for idx in range(0, m_tables.size()): - if(m_tables[idx].get_table_id() == table_id): - m_tables[idx].set_table_name(table_name) - break - -# deletes a table from the list -func delete_table(table_id : int) -> void: - for idx in range(0, m_tables.size()): - if(m_tables[idx].get_table_id() == table_id): - $tables_container/tables.remove_child(m_tables[idx]) - m_tables.remove(idx) - break - -# called when the user presses an item -func on_select_item(table_id : int) -> void: - # print("GDDBTablesList::on_select_item(" + str(table_id) + ")") - select_item_by_id(table_id) - emit_signal("select_table", table_id) - -# select an item by index -func select_item_at(table_idx : int) -> void: - for idx in range(0, m_tables.size()): - m_tables[idx].set_selected(idx == table_idx) - -# select an item by id -func select_item_by_id(table_id : int) -> void: - # print("GDDBTablesList::select_item_by_id(" + str(table_id) + ")") - for idx in range(0, m_tables.size()): - m_tables[idx].set_selected(m_tables[idx].get_table_id() == table_id) - -# returns the selected element -func get_selected_item(): - for idx in range(0, m_tables.size()): - if(m_tables[idx].is_selected()): - return m_tables[idx] - print("ERROR: GDDBTablesList::get_selected_item() - there is no selected element") - return null diff --git a/addons/godot_db_manager/tables_list.tscn b/addons/godot_db_manager/tables_list.tscn deleted file mode 100644 index 72a63de..0000000 --- a/addons/godot_db_manager/tables_list.tscn +++ /dev/null @@ -1,60 +0,0 @@ -[gd_scene load_steps=5 format=2] - -[ext_resource path="res://addons/godot_db_manager/tables_list.gd" type="Script" id=1] -[ext_resource path="res://addons/godot_db_manager/tables_header.tscn" type="PackedScene" id=2] -[ext_resource path="res://addons/godot_db_manager/debug/dbg.tscn" type="PackedScene" id=3] -[ext_resource path="res://addons/godot_db_manager/assets/tex/gui.png" type="Texture2D" id=4] - -[node name="tables_list" type="Control"] -anchor_right = 0.2 -anchor_bottom = 1.0 -offset_right = -9.99991 -custom_minimum_size = Vector2( 180, 225 ) -script = ExtResource( 1 ) -__meta__ = { -"_edit_horizontal_guides_": [ 29.9686, 584.0 ], -"_edit_use_anchors_": false, -"_edit_vertical_guides_": [ 180.0 ] -} - -[node name="tables_header" parent="." instance=ExtResource( 2 )] -anchor_right = 1.0 -anchor_bottom = 0.0 -offset_right = -9.15527e-05 -offset_bottom = 30.0 -custom_minimum_size = Vector2( 180, 30 ) - -[node name="tables_container" type="ScrollContainer" parent="."] -anchor_right = 1.0 -anchor_bottom = 1.0 -offset_top = 32.0 -custom_minimum_size = Vector2( 180, 0 ) -__meta__ = { -"_edit_use_anchors_": false -} - -[node name="tables" type="VBoxContainer" parent="tables_container"] -clip_contents = true -__meta__ = { -"_edit_use_anchors_": false -} - -[node name="resize_ctrl" type="NinePatchRect" parent="."] -anchor_left = 1.0 -anchor_right = 1.0 -anchor_bottom = 1.0 -offset_left = -3.0 -mouse_filter = 0 -mouse_default_cursor_shape = 15 -texture = ExtResource( 4 ) -region_rect = Rect2( 80, 12, 24, 24 ) -patch_margin_left = 1 -patch_margin_top = 1 -patch_margin_right = 1 -patch_margin_bottom = 1 -__meta__ = { -"_edit_use_anchors_": false -} - -[node name="dbg" parent="." instance=ExtResource( 3 )] -visible = false diff --git a/addons/lod/lod_cpu_particles.gd b/addons/lod/lod_cpu_particles.gd index 86e6189..fe031f1 100644 --- a/addons/lod/lod_cpu_particles.gd +++ b/addons/lod/lod_cpu_particles.gd @@ -1,7 +1,8 @@ # Copyright © 2020 Hugo Locurcio and contributors - MIT License # See `LICENSE.md` included in the source distribution for details. +@icon("res://addons/lod/lod_cpu_particles.svg") +class_name LODCPUParticles extends CPUParticles3D -class_name LODCPUParticles, "lod_cpu_particles.svg" # If `false`, LOD won't update anymore. This can be used for performance comparison # purposes. diff --git a/addons/lod/lod_omni_light.gd b/addons/lod/lod_omni_light.gd index 57db015..956677d 100644 --- a/addons/lod/lod_omni_light.gd +++ b/addons/lod/lod_omni_light.gd @@ -1,7 +1,8 @@ # Copyright © 2020 Hugo Locurcio and contributors - MIT License # See `LICENSE.md` included in the source distribution for details. +@icon("res://addons/lod/lod_omni_light.svg") +class_name LODOmniLight extends OmniLight3D -class_name LODOmniLight, "lod_omni_light.svg" # If `false`, LOD won't update anymore. This can be used for performance comparison # purposes. diff --git a/addons/lod/lod_particles.gd b/addons/lod/lod_particles.gd index 02bb3fe..78067ac 100644 --- a/addons/lod/lod_particles.gd +++ b/addons/lod/lod_particles.gd @@ -1,7 +1,8 @@ # Copyright © 2020 Hugo Locurcio and contributors - MIT License # See `LICENSE.md` included in the source distribution for details. -extends Particles -class_name LODParticles, "lod_particles.svg" +@icon("res://addons/lod/lod_particles.svg") +class_name LODParticles +extends GPUParticles3D # If `false`, LOD won't update anymore. This can be used for performance comparison # purposes. diff --git a/addons/lod/lod_spatial.gd b/addons/lod/lod_spatial.gd index 86eca73..3edb136 100644 --- a/addons/lod/lod_spatial.gd +++ b/addons/lod/lod_spatial.gd @@ -1,7 +1,8 @@ # Copyright © 2020 Hugo Locurcio and contributors - MIT License # See `LICENSE.md` included in the source distribution for details. +@icon("res://addons/lod/lod_spatial.svg") +class_name LODSpatial extends Node3D -class_name LODSpatial, "lod_spatial.svg" # If `false`, LOD won't update anymore. This can be used for performance comparison # purposes. diff --git a/addons/lod/lod_spot_light.gd b/addons/lod/lod_spot_light.gd index 99308db..982a5a7 100644 --- a/addons/lod/lod_spot_light.gd +++ b/addons/lod/lod_spot_light.gd @@ -1,7 +1,8 @@ # Copyright © 2020 Hugo Locurcio and contributors - MIT License # See `LICENSE.md` included in the source distribution for details. +@icon("res://addons/lod/lod_spot_light.svg") +class_name LODSpotLight extends SpotLight3D -class_name LODSpotLight, "lod_spot_light.svg" # If `false`, LOD won't update anymore. This can be used for performance comparison # purposes. diff --git a/project.godot b/project.godot index 456fb6a..37fb66a 100644 --- a/project.godot +++ b/project.godot @@ -1,4 +1,3 @@ -; Project was converted by built-in tool to Godot 4 ; Engine configuration file. ; It's best edited using the editor UI and not directly, ; since the parameters that go here are not all obvious. @@ -9,196 +8,6 @@ config_version=5 -_global_script_classes=[ { -"base": "Node", -"class": "GDDBConstants", -"language": "GDScript", -"path": "res://addons/godot_db_manager/core/GDDBConstants.gd" -}, { -"base": "Object", -"class": "GDDBData", -"language": "GDScript", -"path": "res://addons/godot_db_manager/core/db_data.gd" -}, { -"base": "Label", -"class": "GDDBDataLabel", -"language": "GDScript", -"path": "res://addons/godot_db_manager/data_label.gd" -}, { -"base": "PopupPanel", -"class": "GDDBDataPanel", -"language": "GDScript", -"path": "res://addons/godot_db_manager/dlgs/data_dlg.gd" -}, { -"base": "WindowDialog", -"class": "GDDBDeletePropDlg", -"language": "GDScript", -"path": "res://addons/godot_db_manager/dlgs/delete_prop_dlg.gd" -}, { -"base": "WindowDialog", -"class": "GDDBDeleteTableDlg", -"language": "GDScript", -"path": "res://addons/godot_db_manager/dlgs/delete_table_dlg.gd" -}, { -"base": "WindowDialog", -"class": "GDDBEditStringDlg", -"language": "GDScript", -"path": "res://addons/godot_db_manager/dlgs/edit_string_dlg.gd" -}, { -"base": "Tabs", -"class": "GDDBEditor", -"language": "GDScript", -"path": "res://addons/godot_db_manager/db_editor.gd" -}, { -"base": "Node", -"class": "GDDBGlobals", -"language": "GDScript", -"path": "res://addons/godot_db_manager/core/GDDBGlobals.gd" -}, { -"base": "Control", -"class": "GDDBInterface", -"language": "GDScript", -"path": "res://addons/godot_db_manager/db_interface.gd" -}, { -"base": "FileDialog", -"class": "GDDBLoadResourcePathDlg", -"language": "GDScript", -"path": "res://addons/godot_db_manager/dlgs/load_res_path_dlg.gd" -}, { -"base": "Object", -"class": "GDDBMan", -"language": "GDScript", -"path": "res://addons/godot_db_manager/core/db_man.gd" -}, { -"base": "EditorPlugin", -"class": "GDDBManager", -"language": "GDScript", -"path": "res://addons/godot_db_manager/db_manager.gd" -}, { -"base": "Control", -"class": "GDDBMenu", -"language": "GDScript", -"path": "res://addons/godot_db_manager/menu.gd" -}, { -"base": "WindowDialog", -"class": "GDDBNewDBDlg", -"language": "GDScript", -"path": "res://addons/godot_db_manager/dlgs/new_db_dlg.gd" -}, { -"base": "WindowDialog", -"class": "GDDBNewTableDlg", -"language": "GDScript", -"path": "res://addons/godot_db_manager/dlgs/new_table_dlg.gd" -}, { -"base": "Object", -"class": "GDDBProperty", -"language": "GDScript", -"path": "res://addons/godot_db_manager/core/db_prop.gd" -}, { -"base": "Object", -"class": "GDDBTable", -"language": "GDScript", -"path": "res://addons/godot_db_manager/core/db_table.gd" -}, { -"base": "Control", -"class": "GDDBTableCell", -"language": "GDScript", -"path": "res://addons/godot_db_manager/table_cell.gd" -}, { -"base": "Control", -"class": "GDDBTableEditor", -"language": "GDScript", -"path": "res://addons/godot_db_manager/table_editor.gd" -}, { -"base": "Control", -"class": "GDDBTableItem", -"language": "GDScript", -"path": "res://addons/godot_db_manager/table_item.gd" -}, { -"base": "Control", -"class": "GDDBTableProperty", -"language": "GDScript", -"path": "res://addons/godot_db_manager/table_property.gd" -}, { -"base": "Control", -"class": "GDDBTablesHeader", -"language": "GDScript", -"path": "res://addons/godot_db_manager/tables_header.gd" -}, { -"base": "Control", -"class": "GDDBTablesList", -"language": "GDScript", -"path": "res://addons/godot_db_manager/tables_list.gd" -}, { -"base": "Node", -"class": "GDDBTypes", -"language": "GDScript", -"path": "res://addons/godot_db_manager/core/GDDBTypes.gd" -}, { -"base": "Object", -"class": "GDDatabase", -"language": "GDScript", -"path": "res://addons/godot_db_manager/core/database.gd" -}, { -"base": "CPUParticles", -"class": "LODCPUParticles", -"language": "GDScript", -"path": "res://addons/lod/lod_cpu_particles.gd" -}, { -"base": "OmniLight", -"class": "LODOmniLight", -"language": "GDScript", -"path": "res://addons/lod/lod_omni_light.gd" -}, { -"base": "Particles", -"class": "LODParticles", -"language": "GDScript", -"path": "res://addons/lod/lod_particles.gd" -}, { -"base": "Spatial", -"class": "LODSpatial", -"language": "GDScript", -"path": "res://addons/lod/lod_spatial.gd" -}, { -"base": "SpotLight", -"class": "LODSpotLight", -"language": "GDScript", -"path": "res://addons/lod/lod_spot_light.gd" -} ] -_global_script_class_icons={ -"GDDBConstants": "", -"GDDBData": "", -"GDDBDataLabel": "", -"GDDBDataPanel": "", -"GDDBDeletePropDlg": "", -"GDDBDeleteTableDlg": "", -"GDDBEditStringDlg": "", -"GDDBEditor": "", -"GDDBGlobals": "", -"GDDBInterface": "", -"GDDBLoadResourcePathDlg": "", -"GDDBMan": "", -"GDDBManager": "", -"GDDBMenu": "", -"GDDBNewDBDlg": "", -"GDDBNewTableDlg": "", -"GDDBProperty": "", -"GDDBTable": "", -"GDDBTableCell": "", -"GDDBTableEditor": "", -"GDDBTableItem": "", -"GDDBTableProperty": "", -"GDDBTablesHeader": "", -"GDDBTablesList": "", -"GDDBTypes": "", -"GDDatabase": "", -"LODCPUParticles": "res://addons/lod/lod_cpu_particles.svg", -"LODOmniLight": "res://addons/lod/lod_omni_light.svg", -"LODParticles": "res://addons/lod/lod_particles.svg", -"LODSpatial": "res://addons/lod/lod_spatial.svg", -"LODSpotLight": "res://addons/lod/lod_spot_light.svg" -} - [application] config/name="Puzzle Quest" @@ -215,9 +24,6 @@ config/quit_on_go_back=false Loading="*res://scenes/UI/loading/Loading.tscn" Global="*res://scripts/Global.gd" -gddb_constants="*res://addons/godot_db_manager/core/GDDBConstants.gd" -gddb_types="*res://addons/godot_db_manager/core/GDDBTypes.gd" -gddb_globals="*res://addons/godot_db_manager/core/GDDBGlobals.gd" Setting="*res://scripts/Setting.gd" Event="*res://scripts/Event.gd" GlobalAnimation="*res://scripts/Animation.gd" @@ -234,7 +40,7 @@ window/stretch/aspect="expand" [editor_plugins] -enabled=PackedStringArray( "res://addons/godot_db_manager/plugin.cfg", "res://addons/lod/plugin.cfg" ) +enabled=PackedStringArray("res://addons/lod/plugin.cfg") [importer_defaults] @@ -252,27 +58,13 @@ scene={ ui_end={ "deadzone": 0.5, -"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"command":false,"pressed":false,"keycode":4194318,"physical_keycode":0,"unicode":0,"echo":false,"script":null) -, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"command":false,"pressed":false,"keycode":4194305,"physical_keycode":0,"unicode":0,"echo":false,"script":null) - ] +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194318,"physical_keycode":0,"unicode":0,"echo":false,"script":null), Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194305,"physical_keycode":0,"unicode":0,"echo":false,"script":null)] } [locale] -translations=PackedStringArray( "res://locales/fr.po", "res://locales/en.po" ) -locale_filter=[ 1, [ "en_GB", "fr_FR" ] ] - -[physics] - -common/enable_pause_aware_picking=true +translations=PackedStringArray("res://locales/fr.po", "res://locales/en.po") [rendering] -quality/driver/fallback_to_gles2=true -threads/thread_model=2 -vram_compression/import_etc=true -shading/overrides/force_vertex_shading.mobile=false -quality/lightmapping/use_bicubic_sampling.mobile=true -quality/depth/hdr.mobile=true environment/defaults/default_environment="res://default_env.tres" -quality/dynamic_fonts/use_oversampling=false diff --git a/scripts/Database.gd b/scripts/Database.gd index 8276832..1087807 100644 --- a/scripts/Database.gd +++ b/scripts/Database.gd @@ -1,47 +1,148 @@ extends Node -func initialize(): - var database_manager = load(gddb_constants.c_addon_main_path + "core/db_man.gd").new() - var db_id = database_manager.load_database(save_database_in()) - - check_invalid_type(db_id) - check_invalid_version(db_id) - - return database_manager.get_db_by_id(db_id) +# Lightweight replacement for the godot_db_manager plugin (incompatible with +# Godot 4). Keeps the ahog.json format used by the rest of the project and +# exposes the same surface API the M* model classes expect: +# +# Global.database.get_table_by_name(name) -> Table +# Global.database.save_db() +# table.get_data_at_row_idx(row_id) -> [Cell, Cell, ...] +# table.edit_data(prop_id, row_id, value) +# table.m_rows_count +# table.get_data_by_prop_name_and_data(prop_name, value) -> Array +# table.get_dictionary_by_prop_name_and_data(prop_name, value) -> Array[Dict] +# cell.get_data() -> String -func check_invalid_type(db_id): - if db_id == gddb_types.e_db_invalid_file: - print("[Database#check_invalid_type] Error invalid database file !") - OS.set_exit_code(1) +class Cell: + var _value + func _init(value): + _value = value + func get_data(): + return _value -func check_invalid_version(db_id): - if db_id == gddb_types.e_db_invalid_ver: - print("[Database#check_invalid_version] Error invalid database version !") - OS.set_exit_code(1) +class Table: + var name: String + var props: Array + var data: Array + var m_rows_count: int -func save_database_in(): + func _init(table_dict: Dictionary): + name = table_dict["table_name"] + props = table_dict["props"] + data = table_dict["data"] + m_rows_count = data.size() / max(1, props.size()) + + func _col_count() -> int: + return props.size() + + func _find_prop_idx(prop_name: String) -> int: + for i in range(props.size()): + if props[i]["name"] == prop_name: + return i + return -1 + + func get_data_at_row_idx(row_idx: int) -> Array: + var start = row_idx * _col_count() + var cells = [] + for i in range(_col_count()): + cells.append(Cell.new(data[start + i])) + return cells + + func edit_data(prop_id: int, row_id: int, value) -> void: + data[row_id * _col_count() + prop_id] = str(value) + + func get_data_by_prop_name_and_data(prop_name: String, value) -> Array: + var prop_idx = _find_prop_idx(prop_name) + var matches = [] + if prop_idx == -1: + return matches + for row in range(m_rows_count): + if data[row * _col_count() + prop_idx] == str(value): + matches.append(row) + return matches + + func get_dictionary_by_prop_name_and_data(prop_name: String, value) -> Array: + var prop_idx = _find_prop_idx(prop_name) + var results = [] + if prop_idx == -1: + return results + for row in range(m_rows_count): + if data[row * _col_count() + prop_idx] == str(value): + var d = {} + for i in range(_col_count()): + d[props[i]["name"]] = data[row * _col_count() + i] + results.append(d) + return results + + func to_dict() -> Dictionary: + return { + "table_name": name, + "props": props, + "data": data, + } + +class DB: + var version: String + var db_name: String + var tables: Dictionary = {} + var _path: String + + func _init(path: String): + _path = path + var f = FileAccess.open(path, FileAccess.READ) + if f == null: + push_error("[Database] Cannot open " + path) + return + var raw = f.get_as_text() + f.close() + var doc = JSON.parse_string(raw) + if doc == null: + push_error("[Database] Invalid JSON in " + path) + return + version = doc.get("GDDB_ver", "") + db_name = doc.get("db_name", "") + for t in doc.get("tables", []): + var table = Table.new(t) + tables[table.name] = table + + func get_table_by_name(name: String): + return tables.get(name) + + func save_db() -> void: + var doc = { + "GDDB_ver": version, + "db_name": db_name, + "tables": [], + } + for tname in tables: + doc["tables"].append(tables[tname].to_dict()) + var f = FileAccess.open(_path, FileAccess.WRITE) + if f == null: + push_error("[Database] Cannot write " + _path) + return + f.store_string(JSON.stringify(doc)) + f.close() + +func initialize() -> DB: + return DB.new(save_database_in()) + +func save_database_in() -> String: if OS.get_name() == "Android": copy_database() return android_path() - else: - return normal_path() + return normal_path() -func android_path(): +func android_path() -> String: return "user://database.json" -func normal_path(): +func normal_path() -> String: return "res://db/ahog.json" -func copy_database(): - var database_copy = File.new() - - if !database_copy.file_exists(android_path()): - var database_file = File.new() - - database_file.open(normal_path(), File.READ) - database_copy.open(android_path(), File.WRITE) - database_copy.store_string(database_file.get_as_text()) - database_copy.close() - - database_file.close() - +func copy_database() -> void: + if FileAccess.file_exists(android_path()): + return + var src = FileAccess.open(normal_path(), FileAccess.READ) + var dst = FileAccess.open(android_path(), FileAccess.WRITE) + dst.store_string(src.get_as_text()) + dst.close() + src.close() diff --git a/scripts/Global.gd b/scripts/Global.gd index ee82991..f1948cd 100644 --- a/scripts/Global.gd +++ b/scripts/Global.gd @@ -1,18 +1,17 @@ extends Control -const TICKS_TIME_MAX = 100 # msec - @onready var current_scene = null @onready var current_scene_int = null -@onready var loader = null @onready var wait_frames = 1 @onready var database = null @onready var loaded = false @onready var animation = Loading.get_node("AnimLoading") +var _loading_path: String = "" + func _ready(): database = load("res://scripts/Database.gd").new().initialize() - + _initialize_current_scene() _initialize_loading_scene() @@ -21,60 +20,59 @@ func _initialize_loading_scene(): animation.connect("animation_finished", Callable(Event, "_loading_is_finished")) func goto_scene(path): - print("[global#goto_scene] : load scene "+String(path)) - loader = ResourceLoader.load_threaded_request(path) - if loader == null: + print("[global#goto_scene] : load scene " + str(path)) + var err = ResourceLoader.load_threaded_request(path) + if err != OK: print("Error loading ....") return + _loading_path = path Loading.show() animation.play("BorderAnim") - + set_process(true) current_scene.queue_free() wait_frames = 1 - Loading.get_node("LoadingBare/VBoxContainer/HBoxContainer/ProgressBar").set_max(loader.get_stage_count()) + Loading.get_node("LoadingBare/VBoxContainer/HBoxContainer/ProgressBar").set_max(1.0) func _process(_delta): - if loader == null: + if _loading_path == "": set_process(false) return - + if wait_frames > 0: wait_frames -= 1 - - var tick = Time.get_ticks_msec() - - # Use "TICKS_TIME_MAX" to control for how long we block this thread - while Time.get_ticks_msec() < tick + TICKS_TIME_MAX: - if loaded: - var err = loader.poll() - - if err == ERR_FILE_EOF: # Finished loading. - _set_new_scene() - break - elif err == OK: - _update_progress() - else: - loader = null - break + + if not loaded: + return + + var progress := [] + var status = ResourceLoader.load_threaded_get_status(_loading_path, progress) + match status: + ResourceLoader.THREAD_LOAD_IN_PROGRESS: + _update_progress(progress[0] if progress.size() > 0 else 0.0) + ResourceLoader.THREAD_LOAD_LOADED: + _set_new_scene() + ResourceLoader.THREAD_LOAD_FAILED, ResourceLoader.THREAD_LOAD_INVALID_RESOURCE: + print("Error loading ....") + _loading_path = "" ## PRIVATE func _initialize_current_scene(): print("[global#_initialize_current_scene]") var root = get_tree().get_root() current_scene = root.get_child(root.get_child_count() - 1) - + if current_scene.name != "Main": get_node("/root/Loading").hide() -func _update_progress(): +func _update_progress(value: float): Loading.visible = true - Loading.get_node("LoadingBare/VBoxContainer/HBoxContainer/ProgressBar").set_value(loader.get_stage()) + Loading.get_node("LoadingBare/VBoxContainer/HBoxContainer/ProgressBar").set_value(value) func _set_new_scene(): - var resource = loader.get_resource() - loader = null + var resource = ResourceLoader.load_threaded_get(_loading_path) + _loading_path = "" current_scene = resource.instantiate() get_node("/root").add_child(current_scene) Loading.hide()