Godot version:
3.0.5-stable_x11.64, Xubuntu
Issue description:
If you create a plugin to make a custom control, like a customized LineEdit, you can add it into your game choosing it from the classes.
However, you cannot add your custom control via GDScript, in fact the parser won't recognize its name. The workaround for this is to just instantiate a base control and add a script to it, however this has some disadvantages:
There should be a way to properly instantiate a custom control via GDScript. Or maybe there is and I can't find it.
Example project:
customized_control.zip
In master you can use script classes for this. I don't think it's worth it adding more features to add_custom_type() at this point.
@akien-mga: Could you please elaborate on how to do that?
I was also looking for this. Or a method like instance_custom_type or get_custom_type which would return the base class/type (?) so you could call .new() on it to create a new instance. So, just like you can call Node.new(), you could do get_custom_type("MyCustomType").new().
The problem is that custom types are only an editor feature. Since the plugins aren't active when the game is running, there's no custom type registered at that point.
One solution would be to register those types in the project settings so they could be loaded in-game. But since named script classes already do this, it's redundant to add that now for plugins.
Okay, thanks a lot for the clarification. Actually I was exploring into this just for "cosmetic" and editor-only reasons: making a dock plugin for quickly creating some basic as well as custom nodes into the scene. (So, in my case, the only set back is that the custom nodes won't show their dedicated icon in the scene tree.) Anyway, thanks, vnen! (And sorry for misreading who commented.)
If anyone's interested found a decent enough work-around - at least for creating custom nodes in editor. I think this also works for the original issue:
var custom_node = load(dummy_scene_path).instance().get_node("MyCustomNode").duplicate()
parent.add_child(custom_node)
custom_node.owner = get_tree().get_edited_scene_root()
If you know how to make an instance local in script (not context menu), then you can instead save your node as the scene root and just instance it without duplicating - otherwise it will be a PackedScene instead.
Edit: Can also save the node as root. In this case set custom_node.filename = "" to detach it.
var custom_node = load(dummy_scene_path).instance()
custom_node.filename = ""
@takaturre
I think this also works for the original issue:
Not really.
All the points in the bulleted list in my OP would still be valid.
Your solution is no different than just normally instancing the node and "manually" attaching the script, which is considered in point # 1. However it is useful if your custom node have children nodes. In that case you can keep the children "folded" (no children displayed in the tree), if your method does work (didn't tried it yet).
Yes, exactly, that is the "solution", and it works perfectly for my case. It is not meant to be a perfect/correct solution, more like a hacky work around useful for some cases - for editor use, not so much in-game use.
Anyway, sorry, if it didn't work for you. However, all the points in the (original) list are met, except no. 4 (which is imo another topic in itself):
node.filename = "" (and so that clapperboard will disappear in the editor).get_class() command returns the same as if I add the node from the + button and search it from the list. It gives the base class from which the custom node extends.... I thought a custom node is always one node. So the children thing is really unrelated, right? However, using a scene, this method of course supports that if you want to add children inside a custom node and fetch the whole family instead of just one node. (Edit: If you do this, rememeber to set the owner for all kids recursively - except the ones belonging to a nested instanced scene. Here is a pic for that func. )
Btw.. I noticed that you could also instance your custom node without first making a base class node. Thought it would't work but it actually did, at least for some cases - however, the icon didn't appear (which led me here originally). So:
var child = load("res://addons/my_node/my_node.gd").new()
add_child(child)
child.owner = get_tree().get_edited_scene_root()
... To be honest, I'm a bit confused about the "core issue" here. If you really want this IN GAME, why do you need the icon and such..? Why do you need to use get_class()? There are likely other methods you can use. (For example, get_script().get_path() or preload a script and check is MyClass, etc.) What are the core practical reasons..?
@takaturre I tried and it works indeed. Thanks for sharing!
The hack keeps the icon indeed, and it's not considered an instanced scene.
No, I didn't care about in-game use, but you may want to use _get_class()_ in the plugin code, but as you correctly found out, it gives the base class even when you manually select the node from the class list, so it's another Godot limitation; not a big deal though.
Glad to hear it worked for you! Yeah, I agree get_class() is a bit confusing, because a custom node is essentially a custom class.
I've just found this in 3.06 too, so here is my workaround:
# Instance it
var layer = preload("hterrain_detail_layer.gd").new()
# Give it the icon
layer.set_meta("_editor_icon", preload("icons/detail_layer_node.svg"))
# Give it a proper name (otherwise it just says "Spatial")
layer.name = "HTerrainDetailLayer"
_terrain.add_child(layer)
# Make it editable
layer.owner = get_tree().edited_scene_root
Thanks a lot for sharing! Should make things much easier in some contexts. (I will check this later for myself.)
If I'm not mistaken, don't 3.1's script classes fix all of these issues?
extends Control
class_name MyControl, "res://my_control.svg"
# main.gd
extends Node
func _ready():
add_child(MyControl.new()) # you have runtime access to the typename
get_class() returning the base name rather than the class name is its own Issue now (#21789).
Thanks for the info!
I tested your method, willnationsdev, but when adding a custom class by an editor script, it didn't load up the icon. However, as Zylann pointed out, it's very easy to set by: node.set_meta("_editor_icon", preload("icons/node.svg"))
.. If I remember correctly, it always worked that you can find your custom class (with the custom icon) from the CreateDialog - issue was about adding with GDScript. (If it didn't, certainly works now.)
Most helpful comment
I've just found this in 3.06 too, so here is my workaround: