Add object_created_in_editor signal - #122058
Conversation
9412219 to
7578e03
Compare
| Notification received right after the scene with the node is saved in the editor. This notification is only sent in the Godot editor and will not occur in exported projects. | ||
| </constant> | ||
| <constant name="NOTIFICATION_EDITOR_CREATED" value="9005"> | ||
| Notification received right after the node has been created in the editor. This notification is only sent when a node is created using the Godot editor UI and will not occur when nodes are created by scripts or instantiating packed scenes. |
There was a problem hiding this comment.
Provide a few examples. Sorry for not doing so myself as of writing.
| Notification received right after the node has been created in the editor. This notification is only sent when a node is created using the Godot editor UI and will not occur when nodes are created by scripts or instantiating packed scenes. | |
| Notification received right after the node has been created using the editor's interface, such as via _ . This notification is not sent when the node is created by scripts or when instantiating packed scenes. |
Or similar. I am not sure about the wording I suggested, either. I just know a few examples are a given.
7578e03 to
6358cce
Compare
| Notification received right after the scene with the node is saved in the editor. This notification is only sent in the Godot editor and will not occur in exported projects. | ||
| </constant> | ||
| <constant name="NOTIFICATION_EDITOR_CREATED" value="9005"> | ||
| Notification received right after the node has been created using the editor's interface, such as via Add Child Node or as a root of a new scene. This notification is not sent when the node is created by scripts or when instantiating packed scenes. |
There was a problem hiding this comment.
I'd also mention this case (which surprised me a bit at first, but it makes sense):
| Notification received right after the node has been created using the editor's interface, such as via Add Child Node or as a root of a new scene. This notification is not sent when the node is created by scripts or when instantiating packed scenes. | |
| Notification received right after the node has been created using the editor's interface, such as via Add Child Node or as a root of a new scene. This notification is not sent when the node is created by scripts, when instantiating packed scenes or when duplicating the node in the editor. |
There was a problem hiding this comment.
I commented, but the comment is gone 🤦♂️
I don't think it's surprising when duplicating a node. It'd be more surprising if it was otherwise, because duplicating nodes could lead to losing some properties.
Calinou
left a comment
There was a problem hiding this comment.
Tested locally, it works as expected. Code looks good to me.
|
Could reasoning and possible approaches be expanded? The linked issue is very specific about scripts overriding engine defaults, but it's being suggested as a new approach instead of changing engine node default values to avoid breaking compatibility with previous versions. Something I don't see being explained in the issue, here, in the salvaged PR or in any of the PRs/issues that are being referenced to this (the linked chat message also doesn't open for me, so can't check that). If it's also only applied when created by the editor, doesn't that mean two versions of every single instance to be maintained? That seems a nightmare? |
Well, that's just one possible usage. #119020 does change properties, but they are not intended to be defaults; they are meant to make the node more usable in the editor out of the box. The customized "defaults" should be irrelevant outside the editor. For changes that work around compatibility, the code should be explicitly marked with Lines 8418 to 8420 in ac7ef92
There is a risk if it's overused I guess. I think the editor defaults should be mentioned in the docs. However this PR only introduces the notification, the details on its usage in the engine are yet to be decided in the subsequent PRs. There are just numerous cases where it can be useful. |
|
This leads to adding blocks of editor only code in each Node that uses it. Users can add nodes to a scene in the editor from code, and if they want to use editor defaults they may try to send the notification manually. The proposal mentions wanting the notification from code, and this would be a workaround for that. However, I wanted to mention an alternative approach to consider, since I haven't really seen it brought up. void set_editor_defaults(Node *p_node) {
Label *label = Object::cast_to<Label>(p_node);
if (label) {
label->set_text("Label");
}
Decal *decal = Object::cast_to<Decal>(p_node);
//etc
}There can be a signal emitted on EditorNode from This way editor-only code isn't added to each node. Users wouldn't have to make their Nodes tool scripts to have editor defaults, it can be in a separate tool script. Should we have this for Resources too? Also cc @godotengine/xr since the proposal is mainly about XR. |
|
Admittedly we have a very chaotic setup for editor-only code which would be nice to re-evaluate.
|
That sounds like sensible approach. A signal could be even connected in plugins, so e.g. particle plugin could be responsible for setting particle defaults, so we don't have a single super method for handling everything. One downside is that many callbacks might get connected to such signal, each checking whether the provided node is of expected type, which might eventually make instantiating nodes slower. Although it does not cause problems with Another thing is that the order of callbacks for signal can't be reasonably guaranteed, unlike for a notification. Multiple callbacks customizing one node can easily mess up each other's settings. |
6358cce to
8a5a75e
Compare
|
Changed notification to a signal and fixed some cases where object wasn't initialized. |
| // This is a situation where listing a subclass before a parent class makes sense (e.g. "NoiseTexture2D,Texture2D"). | ||
| Object *prop = ClassDB::instantiate(String(pi.class_name).get_slicec(',', 0)); | ||
| p_object->set(pi.name, prop); | ||
| instantiate_object_properties(prop); |
There was a problem hiding this comment.
This is notable change. Objects instantiated here are technically created by the editor, so this allows to initialize them recursively.
8a5a75e to
7074172
Compare
NOTIFICATION_EDITOR_CREATEDobject_created_in_editor signal
|
With this new approach, is it still possible to implement #122066 in a way that doesn't break encapsulation? #ifdef TOOLS_ENABLED
void Decal::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_READY: {
EditorInterface::get_singleton()->connect("object_created_in_editor", Callable(this, "_object_created_in_editor"));
} break;
}
}
void Decal::_object_created_in_editor(Object *p_object) {
print_line("test");
// Set improved defaults for newly created Decal nodes in the editor.
// This does not affect Decals created by code, or Decals created in older versions of Godot.
Decal* d = Object::cast_to<Decal>(p_object);
if (d) {
d->set_size(Vector3(2.0, 1.0, 2.0));
d->set_upper_fade(0.0);
d->set_lower_fade(0.0);
}
}
#endifFor this to compile, I need to |
|
It's not possible in the class itself. You'd have to add DecalEditorPlugin, which handles the signal. It's more involved, but avoids adding editor-specific code to scene classes.
You should use |
7074172 to
1a53167
Compare
Thanks for the advice, I updated #122066 accordingly 🙂 |
|
Y'know, now that I think about it, both the signal and the notification don't have to be mutually exclusive features. Because, yes, with a signal we do not break encapsulation and allow addons to inject their own code, but one could argue some users would rather not be bothered to make their own editor plugin for a few tweaks on object creation. Either way we would have to choose one or the other within Godot's codebase. |
|
You don't really need a plugin to use this signal in your scripts, you only have to avoid using EditorInterface directly. @tool
class_name TestClass extends Node
func _init() -> void:
if Engine.has_singleton(&"EditorInterface"):
Engine.get_singleton(&"EditorInterface").object_created_in_editor.connect(initialize_editor_stuff, CONNECT_ONE_SHOT)
func initialize_editor_stuff(object: Object):
if object == self:
print("Test")This works and does not break scripts at runtime. It's a bit more involved than notification though. |
1a53167 to
89e05cb
Compare
kitbdev
left a comment
There was a problem hiding this comment.
Tested the signal and method, looks good.
Creating Scripts and Shaders don't emit the signal, but that seems fine.
- For the main use case of godotengine/godot-proposals#7593, having godotengine/godot-proposals#338 might be a cleaner solution.
But for the purposes of changing defaults without breaking compatibility or letting users set defaults without inheriting the class, this is a good approach. And it allows a workaround for not being able to override inherited defaults.
89e05cb to
cb0fa6b
Compare
Why is that if they are still Resources? That special treatment for Scripts is exactly why some things is impossible to do in the editor. |
Co-authored-by: Malcolm Nixon <Malcolm.nixon@gmail.com>
cb0fa6b to
d2aeb69
Compare
|
Fixed Scripts and Shaders. It was an oversight. |
What problem(s) does this PR solve?
Closes (maybe) godotengine/godot-proposals#7593
Helps #119020
Helps https://chat.godotengine.org/channel/editor?msg=5NLvSXn9Tse6xdcEp
Additional information
This PR salvages #81217 and it's mostly the same, except the new notification is sent in
instantiate_object_properties(), so nodes receive it in all cases whenPROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECTis handled. It also handles a few missed cases: when a script is dropped to make node, when scene root is created from favorites and when new scene is created using non-custom node.See #81217 for rationale on this solution, especially my last comment.
EDIT:
Changed to a signal in EditorInterface instead.