From 6fc1c3a4d1cf0c865f7dfdb1221ef07a5d25f305 Mon Sep 17 00:00:00 2001 From: Juan Linietsky Date: Sat, 27 Feb 2016 23:10:44 -0300 Subject: [PATCH] Completed the support for plugins! It is not possible to add plugins. Not all APIs are provided yet, please request whathever you are missing. Some example plugins are provided in demos/plugins. Just copy them to a folder in your project named addons/ and then enable them from the project settings. Have fun! --- core/bind/core_bind.cpp | 6 + core/bind/core_bind.h | 1 + core/error_macros.h | 5 + core/global_constants.cpp | 1 + core/register_core_types.cpp | 3 +- demos/plugins/custom_dock/custom_dock.scn | Bin 0 -> 1494 bytes demos/plugins/custom_dock/dock_plugin.gd | 23 + demos/plugins/custom_dock/plugin.cfg | 14 + .../custom_import_plugin/import_plugin.gd | 81 ++ .../custom_import_plugin/material_dialog.gd | 67 ++ .../custom_import_plugin/material_dialog.tscn | 111 ++ .../custom_import_plugin/material_import.gd | 22 + demos/plugins/custom_import_plugin/plugin.cfg | 14 + demos/plugins/custom_import_plugin/test.mtxt | 1 + demos/plugins/custom_node/heart.gd | 12 + demos/plugins/custom_node/heart.png | Bin 0 -> 12584 bytes demos/plugins/custom_node/heart_icon.png | Bin 0 -> 809 bytes demos/plugins/custom_node/heart_plugin.gd | 18 + demos/plugins/custom_node/plugin.cfg | 14 + demos/plugins/readme.txt | 13 + doc/base/classes.xml | 956 +++++++++++++++--- modules/gdscript/gd_script.cpp | 5 + tools/doc/doc_data.cpp | 8 +- tools/editor/create_dialog.cpp | 29 +- tools/editor/editor_data.cpp | 12 +- tools/editor/editor_data.h | 3 + tools/editor/editor_help.cpp | 80 +- tools/editor/editor_import_export.cpp | 31 +- tools/editor/editor_import_export.h | 4 + tools/editor/editor_node.cpp | 184 +++- tools/editor/editor_node.h | 38 +- tools/editor/editor_plugin.cpp | 79 +- tools/editor/editor_plugin.h | 30 +- tools/editor/editor_plugin_settings.cpp | 188 ++++ tools/editor/editor_plugin_settings.h | 35 + tools/editor/editor_settings.cpp | 217 ---- tools/editor/editor_settings.h | 12 - .../io_plugins/editor_font_import_plugin.cpp | 2 +- .../io_plugins/editor_scene_import_plugin.cpp | 11 +- .../io_plugins/editor_scene_import_plugin.h | 2 +- tools/editor/plugins/path_editor_plugin.cpp | 12 +- tools/editor/plugins/path_editor_plugin.h | 6 +- .../editor/plugins/spatial_editor_plugin.cpp | 29 +- tools/editor/plugins/spatial_editor_plugin.h | 4 - tools/editor/project_settings.cpp | 12 + tools/editor/project_settings.h | 10 + tools/editor/settings_config_dialog.cpp | 201 ---- tools/editor/settings_config_dialog.h | 14 - tools/editor/spatial_editor_gizmos.cpp | 63 +- tools/editor/spatial_editor_gizmos.h | 93 +- 50 files changed, 2070 insertions(+), 706 deletions(-) create mode 100644 demos/plugins/custom_dock/custom_dock.scn create mode 100644 demos/plugins/custom_dock/dock_plugin.gd create mode 100644 demos/plugins/custom_dock/plugin.cfg create mode 100644 demos/plugins/custom_import_plugin/import_plugin.gd create mode 100644 demos/plugins/custom_import_plugin/material_dialog.gd create mode 100644 demos/plugins/custom_import_plugin/material_dialog.tscn create mode 100644 demos/plugins/custom_import_plugin/material_import.gd create mode 100644 demos/plugins/custom_import_plugin/plugin.cfg create mode 100644 demos/plugins/custom_import_plugin/test.mtxt create mode 100644 demos/plugins/custom_node/heart.gd create mode 100644 demos/plugins/custom_node/heart.png create mode 100644 demos/plugins/custom_node/heart_icon.png create mode 100644 demos/plugins/custom_node/heart_plugin.gd create mode 100644 demos/plugins/custom_node/plugin.cfg create mode 100644 demos/plugins/readme.txt create mode 100644 tools/editor/editor_plugin_settings.cpp create mode 100644 tools/editor/editor_plugin_settings.h diff --git a/core/bind/core_bind.cpp b/core/bind/core_bind.cpp index 229640ba11..aff047177c 100644 --- a/core/bind/core_bind.cpp +++ b/core/bind/core_bind.cpp @@ -55,11 +55,17 @@ bool _ResourceLoader::has(const String &p_path) { return ResourceCache::has(local_path); }; +Ref _ResourceLoader::load_import_metadata(const String& p_path) { + + return ResourceLoader::load_import_metadata(p_path); +} + void _ResourceLoader::_bind_methods() { ObjectTypeDB::bind_method(_MD("load_interactive:ResourceInteractiveLoader","path","type_hint"),&_ResourceLoader::load_interactive,DEFVAL("")); ObjectTypeDB::bind_method(_MD("load:Resource","path","type_hint", "p_no_cache"),&_ResourceLoader::load,DEFVAL(""), DEFVAL(false)); + ObjectTypeDB::bind_method(_MD("load_import_metadata:ResourceImportMetadata","path"),&_ResourceLoader::load_import_metadata); ObjectTypeDB::bind_method(_MD("get_recognized_extensions_for_type","type"),&_ResourceLoader::get_recognized_extensions_for_type); ObjectTypeDB::bind_method(_MD("set_abort_on_missing_resources","abort"),&_ResourceLoader::set_abort_on_missing_resources); ObjectTypeDB::bind_method(_MD("get_dependencies","path"),&_ResourceLoader::get_dependencies); diff --git a/core/bind/core_bind.h b/core/bind/core_bind.h index 4a9bb2a961..2c43390d3c 100644 --- a/core/bind/core_bind.h +++ b/core/bind/core_bind.h @@ -26,6 +26,7 @@ public: void set_abort_on_missing_resources(bool p_abort); StringArray get_dependencies(const String& p_path); bool has(const String& p_path); + Ref load_import_metadata(const String& p_path); _ResourceLoader(); }; diff --git a/core/error_macros.h b/core/error_macros.h index cafbf0c16e..47b1de5df3 100644 --- a/core/error_macros.h +++ b/core/error_macros.h @@ -223,5 +223,10 @@ extern bool _err_error_exists; } \ +#define WARN_PRINTS(m_string) \ + { \ + _err_print_error(FUNCTION_STR,__FILE__,__LINE__,String(m_string).utf8().get_data(),ERR_HANDLER_WARNING); \ + _err_error_exists=false;\ + } \ #endif diff --git a/core/global_constants.cpp b/core/global_constants.cpp index a183255b06..130fca1b2a 100644 --- a/core/global_constants.cpp +++ b/core/global_constants.cpp @@ -432,6 +432,7 @@ static _GlobalConstant _global_constants[]={ BIND_GLOBAL_CONSTANT( ERR_FILE_EOF ), BIND_GLOBAL_CONSTANT( ERR_CANT_OPEN ), ///< Can't open a resource/socket/file BIND_GLOBAL_CONSTANT( ERR_CANT_CREATE ), + BIND_GLOBAL_CONSTANT( ERR_PARSE_ERROR ), BIND_GLOBAL_CONSTANT( ERROR_QUERY_FAILED ), BIND_GLOBAL_CONSTANT( ERR_ALREADY_IN_USE ), BIND_GLOBAL_CONSTANT( ERR_LOCKED ), ///< resource is locked diff --git a/core/register_core_types.cpp b/core/register_core_types.cpp index d977ea3e18..54431cf381 100644 --- a/core/register_core_types.cpp +++ b/core/register_core_types.cpp @@ -51,6 +51,7 @@ #include "packed_data_container.h" #include "func_ref.h" #include "input_map.h" +#include "undo_redo.h" #ifdef XML_ENABLED static ResourceFormatSaverXML *resource_saver_xml=NULL; @@ -128,7 +129,7 @@ void register_core_types() { // ObjectTypeDB::register_type(); ObjectTypeDB::register_type(); ObjectTypeDB::register_type(); - + ObjectTypeDB::register_type(); ObjectTypeDB::register_type(); ObjectTypeDB::register_virtual_type(); diff --git a/demos/plugins/custom_dock/custom_dock.scn b/demos/plugins/custom_dock/custom_dock.scn new file mode 100644 index 0000000000000000000000000000000000000000..0e32ece2649b83c82e32785c41216a881747c99f GIT binary patch literal 1494 zcmWku3rrMO6uq;vyTc!J5h|ejfGDj*RuN+&wcMFqRQ&O^RY{y3mSK0;*%`Vs3uv2k zl~~ZCrnQZ={!mk!+K-K?U^FSCY5Y{Hv8f@Zq>M2!O-zgFC#J-dK3;O(d%5?#ch1Yb zc}>lJKg^6AfG4Q{-?;!7fFuKsq?-YTu#iF6%myhGmD`&`vL?g%6p~|C0pogN6NwU&oQar3^zPJ*On87_m zIgU2nBnuhvkP=Ua9!eNKC9LUjS&ZokLxx#47A@_s7Fp^5PvN2gRgCm?M#$;0x#o#{Y>w@0vN1jzQIUc<0Y*9>A z4vxD_xz~go@&?l-7dLX6DRwJ{(ngB-2&iO<+YpVY($!Vm3bV-qHYW9o$vIXTKhM2E zYLivqh?fx(CuC$hc>gS++hZHvMrIgoNR#AB_3&?tG>%rbZuxao{$QpJeMJ>a>yjNG0&-( za?FsN{5{af$DG(DnGpl#lO6>A1s*I8gFzY@TI5_|0`sww3+b9BhV(?SL4ySdRzYy1 z%@kZ)hn%}xiYt<4COe>zRN=2I01X679E?PE-vH8fGr5>a)?fgp2_&Y{OS+RRfe(ka!R6p&K8T>!El(sZmhaUWhKD-+Xv5~GvFh-4(+Eh22WCW7fiea0p!4)_I z&T188tmjQ!5m2z)e+=s>)Binn9>T&vE7sU-6xUF$$L6Be4gVAL((ClVWz43;zyKvi z@gxOcnRNxvP`b?)T2Yuos`zReEvH2-7fw^zzo?!E%jjIjQ$M5l9M5AvKBj>yl-AE@ zQ~C_*A)lno=nKoO=hmp59i_A=pKp7k{$VSMXJ%ZD@$2!Z&5c=6zKkXU71Tq>8nao$ zny~W?zR&g;76h(Qg9dNVQm@uJ$;! z1Sn!n?cToobT~>GU0afKeGbtYTnbhxgC!RgUtk zzUq?|BTrW?-`aU$>S6a+`$r#cn3^6q^2w9U-rVe`D<>!VA8%JK3?CdFFWLp4UOIRC zT*YYVvbVfFpD*w_n7jX;>Zv)iaj^1wV7znJGtau&%MYwOKNZb#=Dhz?Rn~#dsrrlV xFScu0sdxyg?77a2Z-3<82I1_Y@rGAi0b*xCUG1j8*FtbNKvQ#*zlr|n{{tSf;!ywq literal 0 HcmV?d00001 diff --git a/demos/plugins/custom_dock/dock_plugin.gd b/demos/plugins/custom_dock/dock_plugin.gd new file mode 100644 index 0000000000..ce8a3bcd09 --- /dev/null +++ b/demos/plugins/custom_dock/dock_plugin.gd @@ -0,0 +1,23 @@ +tool +extends EditorPlugin + +var dock = null + +func _enter_tree(): + # When this plugin node enters tree, add the custom type + + dock = preload("res://addons/custom_dock/custom_dock.scn").instance() + + add_control_to_dock( DOCK_SLOT_LEFT_UL, dock ) + +func _exit_tree(): + + # Remove from docks (must be called so layout is updated and saved) + remove_control_from_docks(dock) + # Remove the node + dock.free() + + + + + \ No newline at end of file diff --git a/demos/plugins/custom_dock/plugin.cfg b/demos/plugins/custom_dock/plugin.cfg new file mode 100644 index 0000000000..e295384c25 --- /dev/null +++ b/demos/plugins/custom_dock/plugin.cfg @@ -0,0 +1,14 @@ +[plugin] + +name="Custom Dock" +description="Adds a new Customizable Dock" +author="Juan Linietsky" +version="1.0" +script="dock_plugin.gd" + + + + + + + diff --git a/demos/plugins/custom_import_plugin/import_plugin.gd b/demos/plugins/custom_import_plugin/import_plugin.gd new file mode 100644 index 0000000000..2cf8a0302f --- /dev/null +++ b/demos/plugins/custom_import_plugin/import_plugin.gd @@ -0,0 +1,81 @@ +tool + +extends EditorImportPlugin + + +# Simple plugin that imports a text file with extension .mtxt +# which contains 3 integers in format R,G,B (0-255) +# (see example .mtxt in this folder) +# Imported file is converted to a material + +var dialog = null + +func get_name(): + return "silly_material" + +func get_visible_name(): + return "Silly Material" + +func import_dialog(path): + var md = null + if (path!=""): + md = ResourceLoader.load_import_metadata(path) + dialog.configure(self,path,md) + dialog.popup_centered() + +func import(path,metadata): + + assert(metadata.get_source_count() == 1) + + var source = metadata.get_source_path(0) + var use_red_anyway = metadata.get_option("use_red_anyway") + + var f = File.new() + var err = f.open(source,File.READ) + if (err!=OK): + return ERR_CANT_OPEN + + var l = f.get_line() + + f.close() + + var channels = l.split(",") + if (channels.size()!=3): + return ERR_PARSE_ERROR + + var color = Color8(int(channels[0]),int(channels[1]),int(channels[2])) + + var material + + if (ResourceLoader.has(path)): + # Material is in use, update it + material = ResourceLoader.load(path) + else: + # Material not in use, create + material = FixedMaterial.new() + + if (use_red_anyway): + color=Color8(255,0,0) + + material.set_parameter(FixedMaterial.PARAM_DIFFUSE,color) + + # Make sure import metadata links to this plugin + + metadata.set_editor("silly_material") + + # Update the import metadata + + material.set_import_metadata(metadata) + + + # Save + err = ResourceSaver.save(path,material) + + return err + + +func config(base_control): + + dialog = preload("res://addons/custom_import_plugin/material_dialog.tscn").instance() + base_control.add_child(dialog) + diff --git a/demos/plugins/custom_import_plugin/material_dialog.gd b/demos/plugins/custom_import_plugin/material_dialog.gd new file mode 100644 index 0000000000..1022743254 --- /dev/null +++ b/demos/plugins/custom_import_plugin/material_dialog.gd @@ -0,0 +1,67 @@ +tool +extends ConfirmationDialog + +var src_fs +var dst_fs +var import_plugin + +func configure(p_import_plugin,path,metadata): + import_plugin=p_import_plugin + if (metadata): + # metadata from previous import exists, fill in fields + assert( metadata.get_source_count() > 0 ) + # Always expand the source paths + var src_path = import_plugin.expand_source_path( metadata.get_source_path(0) ) + get_node("src_file").set_text(src_path) + get_node("dst_file").set_text(path) + # Fill in from metadata options + get_node("use_red_anyway").set_pressed( metadata.get_option("use_red_anyway") ) + + +func _ready(): + + src_fs = FileDialog.new() + src_fs.set_mode(FileDialog.MODE_OPEN_FILE) + src_fs.set_access(FileDialog.ACCESS_FILESYSTEM) #access all filesystem, not only res:// + src_fs.add_filter("*.mtxt") + src_fs.connect("file_selected",self,"_on_src_selected") + + add_child(src_fs) + + dst_fs = EditorFileDialog.new() + dst_fs.set_mode(EditorFileDialog.MODE_SAVE_FILE) + dst_fs.add_filter("*.mtl") # Use binary extension always, text can't save metadata + dst_fs.connect("file_selected",self,"_on_dst_selected") + + add_child(dst_fs) + + set_hide_on_ok(true) + get_ok().set_text("Import!") + + +func _on_src_browse_pressed(): + src_fs.popup_centered_ratio() + +func _on_dst_browse_pressed(): + dst_fs.popup_centered_ratio() + +func _on_src_selected(path): + get_node("src_file").set_text(path) + +func _on_dst_selected(path): + get_node("dst_file").set_text(path) + +func _on_MaterialImport_confirmed(): + # Create an import metadata + var imd = ResourceImportMetadata.new() + # Add the source files, always validate the source path + imd.add_source( import_plugin.validate_source_path( get_node("src_file").get_text() )) + # Add the options + imd.set_option( "use_red_anyway", get_node("use_red_anyway").is_pressed() ) + # Perform regular import + var err = import_plugin.import( get_node("dst_file").get_text(), imd ) + # Warn if error + if (err!=OK): + get_node("error").set_text("Error Importing!") + get_node("error").popup_centered_minsize() + diff --git a/demos/plugins/custom_import_plugin/material_dialog.tscn b/demos/plugins/custom_import_plugin/material_dialog.tscn new file mode 100644 index 0000000000..9ad6f492fd --- /dev/null +++ b/demos/plugins/custom_import_plugin/material_dialog.tscn @@ -0,0 +1,111 @@ +[gd_scene load_steps=2 format=1] + +[ext_resource path="res://addons/custom_import_plugin/material_dialog.gd" type="Script" id=1] + +[node name="MaterialImport" type="ConfirmationDialog"] + +margin/right = 276.0 +margin/bottom = 154.0 +focus/ignore_mouse = false +focus/stop_mouse = true +size_flags/horizontal = 2 +size_flags/vertical = 2 +popup/exclusive = false +window/title = "Silly Material Import" +dialog/hide_on_ok = true +script/script = ExtResource( 1 ) +__meta__ = { "__editor_plugin_screen__":"Script" } + +[node name="src_file" type="LineEdit" parent="."] + +margin/left = 19.0 +margin/top = 6.0 +margin/right = 190.0 +margin/bottom = 29.0 +focus/ignore_mouse = false +focus/stop_mouse = true +size_flags/horizontal = 2 +size_flags/vertical = 2 +text = "" +max_length = 0 +editable = true +secret = false + +[node name="src_browse" type="Button" parent="."] + +margin/left = 195.0 +margin/top = 7.0 +margin/right = 249.0 +margin/bottom = 29.0 +focus/ignore_mouse = false +focus/stop_mouse = true +size_flags/horizontal = 2 +size_flags/vertical = 2 +toggle_mode = false +text = "browse" +flat = false + +[node name="dst_browse" type="Button" parent="."] + +margin/left = 195.0 +margin/top = 47.0 +margin/right = 249.0 +margin/bottom = 69.0 +focus/ignore_mouse = false +focus/stop_mouse = true +size_flags/horizontal = 2 +size_flags/vertical = 2 +toggle_mode = false +text = "browse" +flat = false + +[node name="dst_file" type="LineEdit" parent="."] + +margin/left = 19.0 +margin/top = 46.0 +margin/right = 190.0 +margin/bottom = 69.0 +focus/ignore_mouse = false +focus/stop_mouse = true +size_flags/horizontal = 2 +size_flags/vertical = 2 +text = "" +max_length = 0 +editable = true +secret = false + +[node name="use_red_anyway" type="CheckBox" parent="."] + +margin/left = 20.0 +margin/top = 84.0 +margin/right = 144.0 +margin/bottom = 106.0 +focus/ignore_mouse = false +focus/stop_mouse = true +size_flags/horizontal = 2 +size_flags/vertical = 2 +toggle_mode = true +text = "Use Red Anyway" +flat = false +align = 0 + +[node name="error" type="AcceptDialog" parent="."] + +visibility/visible = false +margin/right = 40.0 +margin/bottom = 40.0 +focus/ignore_mouse = false +focus/stop_mouse = true +size_flags/horizontal = 2 +size_flags/vertical = 2 +popup/exclusive = false +window/title = "Alert!" +dialog/hide_on_ok = true + +[connection signal="confirmed" from="." to="." method="_on_MaterialImport_confirmed"] + +[connection signal="pressed" from="src_browse" to="." method="_on_src_browse_pressed"] + +[connection signal="pressed" from="dst_browse" to="." method="_on_dst_browse_pressed"] + + diff --git a/demos/plugins/custom_import_plugin/material_import.gd b/demos/plugins/custom_import_plugin/material_import.gd new file mode 100644 index 0000000000..f9859251af --- /dev/null +++ b/demos/plugins/custom_import_plugin/material_import.gd @@ -0,0 +1,22 @@ +tool +extends EditorPlugin + +var import_plugin + +func _enter_tree(): + + import_plugin = preload("res://addons/custom_import_plugin/import_plugin.gd").new() + + # pass the GUI base control, so the dialog has a parent node + import_plugin.config( get_base_control() ) + + add_import_plugin( import_plugin) + +func _exit_tree(): + + remove_import_plugin( import_plugin ) + + + + + \ No newline at end of file diff --git a/demos/plugins/custom_import_plugin/plugin.cfg b/demos/plugins/custom_import_plugin/plugin.cfg new file mode 100644 index 0000000000..a002ad680d --- /dev/null +++ b/demos/plugins/custom_import_plugin/plugin.cfg @@ -0,0 +1,14 @@ +[plugin] + +name="Silly Material Importer" +description="Imports a 3D Material from an external text file" +author="Juan Linietsky" +version="1.0" +script="material_import.gd" + + + + + + + diff --git a/demos/plugins/custom_import_plugin/test.mtxt b/demos/plugins/custom_import_plugin/test.mtxt new file mode 100644 index 0000000000..546ea2af20 --- /dev/null +++ b/demos/plugins/custom_import_plugin/test.mtxt @@ -0,0 +1 @@ +0,0,255 diff --git a/demos/plugins/custom_node/heart.gd b/demos/plugins/custom_node/heart.gd new file mode 100644 index 0000000000..d53c92d800 --- /dev/null +++ b/demos/plugins/custom_node/heart.gd @@ -0,0 +1,12 @@ +tool +extends Node2D + + +var heart = preload("res://addons/custom_node/heart.png") + +func _draw(): + draw_texture(heart,-heart.get_size()/2) + +func _get_item_rect(): + #override + return Rect2(-heart.get_size()/2,heart.get_size()) diff --git a/demos/plugins/custom_node/heart.png b/demos/plugins/custom_node/heart.png new file mode 100644 index 0000000000000000000000000000000000000000..1dfd14a456e46be311f69bc29f8b12a02e60b965 GIT binary patch literal 12584 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 KMNUMnLSTZNZ)Ac1 literal 0 HcmV?d00001 diff --git a/demos/plugins/custom_node/heart_icon.png b/demos/plugins/custom_node/heart_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..2eb819aa24d49c85bd675b385abdfbb422b5df97 GIT binary patch literal 809 zcmV+^1J?YBP)e zSad^gZEa<4bO1wgWnpw>WFU8GbZ8()Nlj2!fese{00NOoL_t(I%YBpEi%nq|#((d( zj{7h(teI_4(s0En)5MVEctufMa4&|;AK=2pa3?AE+;Jr(OeBZQ+ykw&6i(2hAU z!``#kT6?Y6#hN`1;i;$I-k$gCd7tn57!cux>k5D1P6@kF-);L@H*mvoWIzO?s!mH! zk8`tFy!!mW0e#umr^DMfy|0h6t09i3+5c$6y0MWXhxJ8oZ~ayfT9&O!f zS4KzUj-yAzXnfq&BoV8rDGfioG1ZYPeL&7%vHMJZ*~+v&{mqCm>Xu5;`*Y_!%XK}u z4i|EZ7w?NEC%vRr!^wlcz`|=S;1sE&sTEf&2`9c&8%u~QV0j)+5QJg5d`$*KJ_Bta z;5ZHBy9E#?EsYA+bHM ze6Vxi6v1tVTq&d5ob~K{Lt}8}&r2;=FPR+`J?XaKAgCBc42T8B2IJ1s&Uzx56HO9r zUbV#D8FI?pUn)*+0h`4jX$(mO^_k3e_$6ap$$Ciy@s!fOXTN-XFtYhxJI>q-eB34d zNyLf#LM2xMuN9ox5aTM;CSaxvh1GkRxO?UF#!-%u<@ys(*KoGuc5esifYJlyri_j$7R z7bk1!9b7It)FC>wOmwhAbhw-TIqe$$k7wJEhn>=MI}p7ch(7F;ZF5<5|E`sP9Wu0B n-0COhp=fRqU$?x~_;0!&x>f`NsA%>300000NkvXXu0mjf*4k#0 literal 0 HcmV?d00001 diff --git a/demos/plugins/custom_node/heart_plugin.gd b/demos/plugins/custom_node/heart_plugin.gd new file mode 100644 index 0000000000..01a6177c9b --- /dev/null +++ b/demos/plugins/custom_node/heart_plugin.gd @@ -0,0 +1,18 @@ +tool +extends EditorPlugin + + +func _enter_tree(): + # When this plugin node enters tree, add the custom type + + add_custom_type("Heart","Node2D",preload("res://addons/custom_node/heart.gd"),preload("res://addons/custom_node/heart_icon.png")) + +func _exit_tree(): + # When the plugin node exits the tree, remove the custom type + + remove_custom_type("Heart") + + + + + \ No newline at end of file diff --git a/demos/plugins/custom_node/plugin.cfg b/demos/plugins/custom_node/plugin.cfg new file mode 100644 index 0000000000..ebb4b56499 --- /dev/null +++ b/demos/plugins/custom_node/plugin.cfg @@ -0,0 +1,14 @@ +[plugin] + +name="Heart" +description="Adds a new Heart node in 2D" +author="Juan Linietsky" +version="1.0" +script="heart_plugin.gd" + + + + + + + diff --git a/demos/plugins/readme.txt b/demos/plugins/readme.txt new file mode 100644 index 0000000000..963850dcbb --- /dev/null +++ b/demos/plugins/readme.txt @@ -0,0 +1,13 @@ + +To install these, copy each of these folders to a folder: + +addons/ + +inside your projects, example: + +addons/custom_node + +To distribute and install from UI, make a zip that contains the folder, +example: + +zip -r custom_node.zip custom_node/* \ No newline at end of file diff --git a/doc/base/classes.xml b/doc/base/classes.xml index bfe6bfc3ae..5915805b72 100644 --- a/doc/base/classes.xml +++ b/doc/base/classes.xml @@ -1,5 +1,5 @@ - + Built-in GDScript functions. @@ -1476,6 +1476,8 @@ + + @@ -2227,7 +2229,7 @@ - + @@ -2258,7 +2260,7 @@ - + Set the value of an existing key. @@ -6118,7 +6120,7 @@ - + Used for editing, returns an opaque value representing the transform state. @@ -6608,7 +6610,7 @@ - + @@ -8059,7 +8061,7 @@ - + @@ -8069,7 +8071,7 @@ - + @@ -8208,7 +8210,7 @@ - + @@ -8216,7 +8218,7 @@ - + @@ -8707,7 +8709,7 @@ - + @@ -9765,6 +9767,47 @@ Returns an empty String "" at the end of the list. + + + Editor plugin to control the export process. + + + This plugin is added into EditorImportExport and allows to modify + the behavior of the export process for individual files. + + + + + + + + + + + This function is called for each file exported and + depending from the return value one of many things + might happen. + + 1) If returned value is null, the file is exported + as is. + + 2) If the returned value is a RawAray (array of + bytes), the content of that array becomes the new + file being exported. + + 3) If the file must also change it's name when + exported, then a [Dictionary] must be returned with + two fields: 'name' with the new filename and 'data' + with a [RawArray] containing the raw contents of the + file. Even if the name is changed, the run-time will + redirect the old file to the new file automatically + when accessed. + + + + + + @@ -9915,8 +9958,14 @@ Returns an empty String "" at the end of the list. + Import plugin for editor + Import plugins make it easy to handle importing of external assets + into a project. + + They way they work is not that obvious though, so please make sure + to read the documentation, tutorials and examples. @@ -9924,19 +9973,35 @@ Returns an empty String "" at the end of the list. + + + Generally, files that are imported stay the same + when exported. The only exception is in some cases + when the file must be re-imported for different + platforms (ie. texture compression). + + If you want to customize the export process, it's + recommended to use [EditorExportPlugin.custom_export] + instead. + Get the name of the import plugin, which will be + used to identify content imported by this plugin. + + Try to use lowecase and underscores if possible. + Visible name for this plugin, which will be shown on + the import menu. @@ -9944,15 +10009,70 @@ Returns an empty String "" at the end of the list. - + + Perform an import of an external resources into the + project. This function is both called on import + (from the dialog) or re-import + (manual or automatic when external source files + changed). + + An import process generally works like this: + + 1) Check the metadata for source files and options. + Metadata is either generated in the import dialog or + taken from an existing resource upon reimport. + + 2) Perform the import process into a new resource. + Some times the resource being re-imported may be already loaded + and in use, so checking for this by using + [ResourceLoader.has] is recommended. Otherwise + create a new resource. + + 3) Set the metadata from the argument into the existing or new + resource being created using + [Resource.set_import_metadata]. + + 4) Save the resource into 'path' (function argument) + This function is called when either the user chooses + to import a resource of this type (Import menu), or + when the user chooses to re-import the resource + (from filesystem). In the later case, the path for + the existing file is supplied in the argument. + + If the path is supplied, it is recommended to read + the import metadata with + [ResourceLoader.load_import_metadata] and fill in + the fields with the values contained there. + + The dialog can be shown in any way (just use a + ConfirmationDialog and pop it up). Upon + confirmation, fill up a ResourceImportMetadata and + call the [EditorImportPlugin.import] function with + this information. + + + + + + + + + + + + + + + + @@ -9961,22 +10081,52 @@ Returns an empty String "" at the end of the list. + Used by the editor to extend it's functionality. + Plugins are used by the editor to extend functionality. The most + common types of plugins are those which edit a given node or + resource type, import plugins and export plugins. + This method is called when the editor is about to + save the project, switch to another tab, etc. It + asks the plugin to apply any pending state changes + to ensure consistency. + + This is used, for example, in shader editors to let + the plugin know that it must apply the shader code + being written by the user to the object. + Clear all the state and reset the object being + edited to zero. This ensures your plugin does not + keep editing a currently existing node, or a node + fromt the wrong scene. + + + + + + + + + This is used for plugins that create gizmos used by + the spatial editor. Just check that the node passed + in the "for_spatial" argument matches your plugin. + This function is used for plugins that edit specific + object types (nodes or resources). It requests the + editor to edit the given object. @@ -9985,6 +10135,13 @@ Returns an empty String "" at the end of the list. + This is a low level function for plugins that edit a given + object type derived from CanvasItem to capture the input in the 2D editor + viewport. The function is only being called if your + object is being edited. + + Return true if you want to capture the input, + otherwise false. @@ -9995,24 +10152,45 @@ Returns an empty String "" at the end of the list. + This is a low level function for plugins that edit a + given objet type derived from Spatial to capture the + input of the viewport. The function is only being + called if your object is being edited. + + By using the [InputEvent] and the [Camera] arguments + it's pretty easy to do raycasts into space using + Camera functions. + + Return true if you want to capture the input, + otherwise false. + This is for editors that edit script based objects. + You can return a list of breakpoints in the format + (script:line), for example: res://path_to_script.gd:25 + Get the name of the editor plugin. For main scren + plugins this is what will appear in the selector + (which by default is 2D, 3D, Script). + Get the state of your plugin editor. This is used + when saving the scene (so state is kept when opening + it again) and for switching tabs (so state can be + restored when the tab returns). @@ -10021,38 +10199,102 @@ Returns an empty String "" at the end of the list. + Implement this function if your plugin edits a + specific type of object (Resource or Node). If you + return true, then you will get the functions + [EditorPlugin.edit] and [EditorPlugin.make_visible] + called when the editor requests them. + Return true if this is a main screen editor plugin + (it goes in the main screen selector together with + 2D, 3D, Script). + This function will be called when the editor is + requested to become visible. It is used for plugins + that edit a specific object type. + + Remember that you have to manage the visibility of + all your editor controls manually. + Restore the state saved by [EditorPlugin.get_state]. - - - - - - - + - + + Add a custom control to a container (see + CONTAINER_* enum). There are many locations where + custom controls can be added in the editor UI. + + Please remember that you have to manage the + visibility of your custom controls yourself (and likely + hide it after adding it). + + If your plugin is being removed, also make sure to + remove your custom controls too. + + + + + + + + + Add a control to the bottom dock (together with + Output, Debug, Animation, etc). + + Please remember that you have to manage the + visibility of your custom controls yourself (and likely + hide it after adding it). + + If your plugin is being removed, also make sure to + remove your custom controls too. + + + + + + + + + Add the control to a specific dock slot (see DOCK_* + enum for options). + + If the dock is repositioned and as long as the + plugin is active, the editor will save the dock + position on further sessions. + + If your plugin is being removed, also make sure to + remove your control by calling [method + remove_control_from_docks]. + + + + + + + + Remove the control from the dock. Don't forget to + call this if you added one, so the editor can save + the layout and remove it cleanly. @@ -10065,12 +10307,102 @@ Returns an empty String "" at the end of the list. + Add a custom type, which will appear in the list of + nodes or resources. An icon can be optionally + passed. + + When given node or resource is selected, the base + type will be instanced (ie, "Spatial", "Control", + "Resource"), then the script will be loaded and set + to this object. + + You can use the [EditorPlugin.handles] to check if + your custom object is being edited by checking the + script or using 'extends' keyword. + + During run-time, this will be a simple object with a + script so this function does not need to be called + then. + Remove a custom type added by + [EditorPlugin.add_custom_type] + + + + + + + Add an import plugin. These plugins manage importing + external content (from outside the project) into + formats the engine can understand. + + On exit, don't forget to remove the plugin by + calling [method remove_import_plugin] + + + + + + + Remove the import plugin, don't forget to call this + on exit. + + + + + + + Add an export plugin. Plugins of this kind can + change files being exported. On exit don't forget to + call [method remove_export_plugin]. + + + + + + + Remove the export plugin, don't forget to call this + on exit. + + + + + + + Get a base control where it's safe to place dialogs. + Many plugins open dialogs and they need a control as + a base to make sure they use the editor icons and + theme. + + + + + + + Get the undo/redo object. Most actions in the editor + can be undoable, so use this object to make sure + this happens when it's worth it. + + + + + + + Get the object that handles the selection of nodes + in the Scene Tree editor. + + + + + + + Get the general settings for the editor (the same + window that appears in the Settings menu). @@ -10087,18 +10419,45 @@ Returns an empty String "" at the end of the list. + + + + + + + + + + + + + + + + + + + Base script for post-processing scenes being imported. + These scripts can modify scenes after being imported by the 3D Scene + import option of the Import menu. + This function is called upon import with the + imported scene. + + Just do any changes desired to the scene and return + it. If null is returned, import will fail and throw + an error to the user. @@ -10107,8 +10466,10 @@ Returns an empty String "" at the end of the list. + Simple script to perform changes in the currently edited scene. + This script can be run from the Scene -> Run Script menu option. @@ -10131,6 +10492,300 @@ Returns an empty String "" at the end of the list. + + + Manages the SceneTree selection in the editor. + + + This object manages the SceneTree selection in the editor. + + + + + Clear the selection. + + + + + + + Add a node to the selection. + + + + + + + Remove a node from the selection. + + + + + + + Get the list of selectes nodes. + + + + + + + Emitted when the selection changes. + + + + + + + + + Object that holds the project-independent editor settings. + + + Object that holds the project-independent editor settings. These + settings are generally visible in the Editor Settings menu. + + Accessing the settings is done by using the regular [Object] API, + such as. + + settings.set(prop,value) + + settings.get(prop) + + list_of_settings = settings.get_property_list() + + + + + + + Erase a given setting (pass full property path). + + + + + + + Get the global settings path for the engine. Inside + this path you can find some standard paths such as: + + settings/tmp - used for temporary storage of files + + settings/templates - where export templates are + located + + + + + + + Get the specific project settings path. Projects all + have an unique sub-directory inside the settings + path where project specific settings are saved. + + + + + + + Set the list of favorite directories for this + project. + + + + + + + Get the list of favorite directories for this + project. + + + + + + + Set the list of recently visited folders in the file + dialog for this project. + + + + + + + Get the list of recently visited folders in the file + dialog for this project. + + + + + + + + + + + + + + + Custom gizmo for editing Spatial objects. + + + Custom gizmo that is used for providing custom visualization and + editing (handles) for 3D Spatial objects. These are created by + [method EditorPlugin.create_spatial_gizmo]. + + + + + + + + + + + Commit a handle being edited (handles must have been + prevously added by [method add_handles]). + + If the cancel parameter is true, an option to + restore the edited value to the original is + provided. + + + + + + + + + Get the name of an edited handle (handles must have + been previously added by [method add_handles]). + + Handles can be named for reference to the user when editing. + + + + + + + + + Get actual value of a handle. This value can be + anything and used for eventually undoing the motion + when calling [method commit_handle] + + + + + This function is called when the Spatial this gizmo + refers to changes (the [method Spatial.update_gizmo] + is called). + + + + + + + + + + + This function is used when the user drags a gizmo + handle (previously added with [method add_handles]) + in screen coordinates. + + The [Camera] is also provided + so screen coordinates can be converted to raycasts. + + + + + + + + + + + Add lines to the gizmo (as sets of 2 points), with a + given material. The lines are used for visualizing + the gizmo. + + Call this function during [method redraw]. + + + + + + + + + + + Add a mesh to the gizmo, this is used for + visualization. + + Call this function during [method redraw]. + + + + + + + + + + + + + Add collision triangles to the gizmo for picking. A + [TriangleMesh] can be generated from a regular + [Mesh] too. + + Call this function during [method redraw]. + + + + + + + + + Add an unscaled billboard for visualization. + + Call this function during [method redraw]. + + + + + + + + + + + Add a list of handles (points) which can be used to + deform the object being edited. + + There are virtual functions which will be called + upon editing of these handles. + + Call this function during [method redraw]. + + + + + + + Call this function once and upon creation of the + gizmo, otherwise no other function will work. + + The argument is the node being edited by the gizmo. + + + + + + @@ -10152,7 +10807,7 @@ Returns an empty String "" at the end of the list. - + @@ -10182,7 +10837,7 @@ Returns an empty String "" at the end of the list. - + @@ -10782,7 +11437,7 @@ Returns an empty String "" at the end of the list. - + @@ -10988,7 +11643,7 @@ Returns an empty String "" at the end of the list. - + Set a parameter, parameters are defined in the PARAM_* enum. The type of each parameter may change, so it's best to check the enum. @@ -11349,25 +12004,25 @@ Returns an empty String "" at the end of the list. - + - + - + - + - + - + - + - + - + - + @@ -11397,7 +12052,7 @@ Returns an empty String "" at the end of the list. - + @@ -15139,7 +15794,7 @@ Example: (content-length:12), (Content-Type:application/json; charset=UTF-8) - + @@ -16744,7 +17399,7 @@ Example: (content-length:12), (Content-Type:application/json; charset=UTF-8) - + @@ -17722,7 +18377,7 @@ Example: (content-length:12), (Content-Type:application/json; charset=UTF-8) - + @@ -17770,7 +18425,7 @@ Example: (content-length:12), (Content-Type:application/json; charset=UTF-8) - + @@ -17804,7 +18459,7 @@ Example: (content-length:12), (Content-Type:application/json; charset=UTF-8) - + @@ -20219,7 +20874,7 @@ Example: (content-length:12), (Content-Type:application/json; charset=UTF-8) - + Set a property. Return true if the property was found. @@ -20248,7 +20903,7 @@ Example: (content-length:12), (Content-Type:application/json; charset=UTF-8) - + Set property into the object. @@ -20307,7 +20962,7 @@ Example: (content-length:12), (Content-Type:application/json; charset=UTF-8) - + Set a metadata into the object. Metadata is serialized. Metadata can be [i]anything[/i]. @@ -20356,15 +21011,15 @@ Example: (content-length:12), (Content-Type:application/json; charset=UTF-8) - + - + - + - + - + Emit a signal. Arguments are passed in an array. @@ -20373,25 +21028,25 @@ Example: (content-length:12), (Content-Type:application/json; charset=UTF-8) - + - + - + - + - + - + - + - + - + - + Call a function in the object, result is returned. @@ -20400,15 +21055,15 @@ Example: (content-length:12), (Content-Type:application/json; charset=UTF-8) - + - + - + - + - + Create and store a function in the object. The call will take place on idle time. @@ -20699,7 +21354,7 @@ Example: (content-length:12), (Content-Type:application/json; charset=UTF-8) - + @@ -20898,7 +21553,7 @@ Example: (content-length:12), (Content-Type:application/json; charset=UTF-8) - + @@ -22795,7 +23450,7 @@ This method controls whether the position between two cached points is interpola - + @@ -22995,7 +23650,7 @@ This method controls whether the position between two cached points is interpola - + @@ -23127,7 +23782,7 @@ This method controls whether the position between two cached points is interpola - + @@ -23291,7 +23946,7 @@ This method controls whether the position between two cached points is interpola - + @@ -23409,7 +24064,7 @@ This method controls whether the position between two cached points is interpola - + @@ -24328,7 +24983,7 @@ This method controls whether the position between two cached points is interpola - + @@ -24512,7 +25167,7 @@ This method controls whether the position between two cached points is interpola - + @@ -24754,7 +25409,7 @@ This method controls whether the position between two cached points is interpola - + @@ -24856,7 +25511,7 @@ This method controls whether the position between two cached points is interpola - + @@ -26149,7 +26804,7 @@ This method controls whether the position between two cached points is interpola - + @@ -26536,7 +27191,7 @@ This method controls whether the position between two cached points is interpola - + @@ -27766,7 +28421,7 @@ This method controls whether the position between two cached points is interpola - + @@ -27864,6 +28519,14 @@ This method controls whether the position between two cached points is interpola + + + + + + + + @@ -28063,7 +28726,7 @@ This method controls whether the position between two cached points is interpola - + @@ -29909,6 +30572,22 @@ This method controls whether the position between two cached points is interpola + + + + + + + + + + + + + + + + @@ -30032,7 +30711,7 @@ This method controls whether the position between two cached points is interpola - + @@ -30168,15 +30847,15 @@ This method controls whether the position between two cached points is interpola - + - + - + - + - + @@ -30628,7 +31307,7 @@ This method controls whether the position between two cached points is interpola - + @@ -31180,7 +31859,7 @@ This method controls whether the position between two cached points is interpola - + @@ -32212,6 +32891,16 @@ This method controls whether the position between two cached points is interpola + + + + + + + + + + @@ -36082,7 +36771,7 @@ This method controls whether the position between two cached points is interpola - + @@ -37704,7 +38393,7 @@ This method controls whether the position between two cached points is interpola - + @@ -38106,9 +38795,9 @@ This method controls whether the position between two cached points is interpola - + - + @@ -38128,9 +38817,9 @@ This method controls whether the position between two cached points is interpola - + - + @@ -38152,15 +38841,15 @@ This method controls whether the position between two cached points is interpola - + - + - + - + - + @@ -38174,15 +38863,15 @@ This method controls whether the position between two cached points is interpola - + - + - + - + - + @@ -38194,7 +38883,7 @@ This method controls whether the position between two cached points is interpola - + @@ -38218,7 +38907,7 @@ This method controls whether the position between two cached points is interpola - + @@ -38246,7 +38935,7 @@ This method controls whether the position between two cached points is interpola - + @@ -38270,7 +38959,7 @@ This method controls whether the position between two cached points is interpola - + @@ -38353,8 +39042,14 @@ This method controls whether the position between two cached points is interpola + Helper to manage UndoRedo in the editor or custom tools. + Helper to maange UndoRedo in the editor or custom tools. It works by + storing calls to functions in both 'do' an 'undo' lists. + + Common behavior is to create an action, then add do/undo calls to + functions or property changes, then commiting the action. @@ -38363,10 +39058,16 @@ This method controls whether the position between two cached points is interpola + Create a new action. After this is called, do all + your calls to [method add_do_method], + [method add_undo_method], [method add_do_property] + and [method add_undo_property]. + Commit the action. All 'do' methods/properties are + called/set when this function is called. @@ -38374,17 +39075,19 @@ This method controls whether the position between two cached points is interpola - + - + - + - + - + + Add a call to a method in a given object with custom + arguments. @@ -38392,17 +39095,20 @@ This method controls whether the position between two cached points is interpola - + - + - + - + - + + Add a call to an undo method in a given object with + custom arguments. Undo calls are used to revert 'do' + calls. @@ -38413,6 +39119,7 @@ This method controls whether the position between two cached points is interpola + Set a property with a custom value. @@ -38423,34 +39130,51 @@ This method controls whether the position between two cached points is interpola + Undo setting of a property with a custom value. + Add a 'do' reference that will be erased if the 'do' + history is lost. This is useful mostly for new nodes + created for the 'do' call. Do not use for resources. + Add an 'undo' reference that will be erased if the + 'undo' history is lost. This is useful mostly for + nodes rmoved with the 'do' call (not the 'undo' + call!). + Clear the undo/redo history and associated + references. + Get the name of the current action. + Get the version, each time a new action is commited, + the version number of the UndoRedo is increased + automatically. + + This is useful mostly to check if something changed + from a saved version. @@ -40175,7 +40899,7 @@ This method controls whether the position between two cached points is interpola - + diff --git a/modules/gdscript/gd_script.cpp b/modules/gdscript/gd_script.cpp index 1b2ed670ad..c1ee148ef3 100644 --- a/modules/gdscript/gd_script.cpp +++ b/modules/gdscript/gd_script.cpp @@ -1481,6 +1481,11 @@ Variant GDScript::_new(const Variant** p_args,int p_argcount,Variant::CallError& /* STEP 1, CREATE */ + if (!valid) { + r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD; + return Variant(); + } + r_error.error=Variant::CallError::CALL_OK; REF ref; Object *owner=NULL; diff --git a/tools/doc/doc_data.cpp b/tools/doc/doc_data.cpp index 3836fa710b..2d0d7617c2 100644 --- a/tools/doc/doc_data.cpp +++ b/tools/doc/doc_data.cpp @@ -190,7 +190,11 @@ void DocData::generate(bool p_basic_types) { #ifdef DEBUG_METHODS_ENABLED if (m && m->get_return_type()!=StringName()) method.return_type=m->get_return_type(); - else if (arginfo.type!=Variant::NIL) // { + else if (method.name.find(":")!=-1) { + method.return_type=method.name.get_slice(":",1); + method.name=method.name.get_slice(":",0); + + } else if (arginfo.type!=Variant::NIL) // { #endif method.return_type=(arginfo.hint==PROPERTY_HINT_RESOURCE_TYPE)?arginfo.hint_string:Variant::get_type_name(arginfo.type); // } @@ -210,7 +214,7 @@ void DocData::generate(bool p_basic_types) { } else if (arginfo.hint==PROPERTY_HINT_RESOURCE_TYPE) { type_name=arginfo.hint_string; } else if (arginfo.type==Variant::NIL) - type_name="var"; + type_name="Variant"; else type_name=Variant::get_type_name(arginfo.type); diff --git a/tools/editor/create_dialog.cpp b/tools/editor/create_dialog.cpp index 0f39d72308..23b8cad9ce 100644 --- a/tools/editor/create_dialog.cpp +++ b/tools/editor/create_dialog.cpp @@ -171,6 +171,7 @@ void CreateDialog::_update_search() { if (EditorNode::get_editor_data().get_custom_types().has(type)) { //there are custom types based on this... cool. + //print_line("there are custom types"); const Vector &ct = EditorNode::get_editor_data().get_custom_types()[type]; @@ -259,7 +260,33 @@ Object *CreateDialog::instance_selected() { TreeItem *selected = search_options->get_selected(); if (selected) { - return ObjectTypeDB::instance(selected->get_text(0)); + String custom = selected->get_metadata(0); + if (custom!=String()) { + if (EditorNode::get_editor_data().get_custom_types().has(custom)) { + + for(int i=0;iget_text(0)) { + Ref icon = EditorNode::get_editor_data().get_custom_types()[custom][i].icon; + Ref