Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add node active feature and corresponding lifecycle methods #94144

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions doc/classes/EditorSettings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,9 @@
<member name="docks/scene_tree/center_node_on_reparent" type="bool" setter="" getter="">
If [code]true[/code], new node created when reparenting node(s) will be positioned at the average position of the selected node(s).
</member>
<member name="docks/scene_tree/show_node_active_checkbox" type="bool" setter="" getter="">
If [code]true[/code], a checkbox button will appear next to each node in the scene tree which can be used to toggle the [code]node_active[/code] property.
</member>
<member name="docks/scene_tree/start_create_dialog_fully_expanded" type="bool" setter="" getter="">
If [code]true[/code], the Create dialog (Create New Node/Create New Resource) will start with all its sections expanded. Otherwise, sections will be collapsed until the user starts searching (which will automatically expand sections as needed).
</member>
Expand Down
48 changes: 48 additions & 0 deletions doc/classes/Node.xml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,24 @@
[b]Note:[/b] This method is only called if the node is present in the scene tree (i.e. if it's not an orphan).
</description>
</method>
<method name="_node_active" qualifiers="virtual">
<return type="void" />
<description>
Called when the node has become "active in tree", i.e. when [member node_active] has become [code]true[/code], or after the node enters the [SceneTree] if it is active.
In the first case, this method is also called if it was previously inactive in tree and has become active as a result of an ancestor becoming active. See [method is_node_active_in_tree].
In the second case, this method is run after [method _ready] has finished. You can use this method for conditional initialization.
Corresponds to the [constant NOTIFICATION_NODE_ACTIVE] notification in [method Object._notification].
</description>
</method>
<method name="_node_inactive" qualifiers="virtual">
<return type="void" />
<description>
Called when the node has become "inactive in tree", i.e. when [member node_active] has become [code]false[/code], or before the node exits the [SceneTree] if it is active.
In the first case, this method is also called if it was previously active in tree and has become inactive due to an ancestor becoming inactive. See [method is_node_active_in_tree].
You can use this method for lifecycle cleanup, since it is run when this node or an ancestor node was set to inactive, or if this node is about to be removed from the scene.
Corresponds to the [constant NOTIFICATION_NODE_INACTIVE] notification in [method Object._notification].
</description>
</method>
<method name="_physics_process" qualifiers="virtual">
<return type="void" />
<param index="0" name="delta" type="float" />
Expand Down Expand Up @@ -620,6 +638,12 @@
Returns [code]true[/code] if the local system is the multiplayer authority of this node.
</description>
</method>
<method name="is_node_active_in_tree" qualifiers="const">
<return type="bool" />
<description>
Returns [code]true[/code] if this node and all of its ancestors have [member node_active] set to [code]true[/code].
</description>
</method>
<method name="is_node_ready" qualifiers="const">
<return type="bool" />
<description>
Expand Down Expand Up @@ -1005,6 +1029,11 @@
The name of the node. This name must be unique among the siblings (other child nodes from the same parent). When set to an existing sibling's name, the node is automatically renamed.
[b]Note:[/b] When changing the name, the following characters will be replaced with an underscore: ([code].[/code] [code]:[/code] [code]@[/code] [code]/[/code] [code]"[/code] [code]%[/code]). In particular, the [code]@[/code] character is reserved for auto-generated names. See also [method String.validate_node_name].
</member>
<member name="node_active" type="bool" setter="set_node_active" getter="is_node_active_self" default="true">
Whether the node should be "active" in the scene. Inactive nodes will not process scripts, will not be visible in the viewport, and will not act as a valid [Camera2D] or [Camera3D].
If an ancestor of this node is not active, this node will behave as though it is inactive, even if [member node_active] is [code]true[/code]. See [method is_node_active_in_tree].
When [member node_active] changes, [method _node_active] or [method _node_inactive] will be run on this node and on descendant nodes that have had their "active in tree" status changed as a result.
</member>
<member name="owner" type="Node" setter="set_owner" getter="get_owner">
The owner of this node. The owner must be an ancestor of this node. When packing the owner node in a [PackedScene], all the nodes it owns are also saved with it.
[b]Note:[/b] In the editor, nodes not owned by the scene root are usually not displayed in the Scene dock, and will [b]not[/b] be saved. To prevent this, remember to set the owner after calling [method add_child]. See also (see [member unique_name_in_owner])
Expand Down Expand Up @@ -1068,6 +1097,16 @@
Emitted when the node's editor description field changed.
</description>
</signal>
<signal name="node_active_changed">
<description>
Emitted when [member node_active] has changed on this node.
</description>
</signal>
<signal name="node_active_in_tree_changed">
<description>
Emitted when this node's "active in tree" status has changed, i.e. when [member node_active] has changed on this node or any ancestor nodes.
</description>
</signal>
<signal name="ready">
<description>
Emitted when the node is considered ready, after [method _ready] is called.
Expand Down Expand Up @@ -1171,6 +1210,15 @@
<constant name="NOTIFICATION_ENABLED" value="29">
Notification received when the node is enabled again after being disabled. See [constant PROCESS_MODE_DISABLED].
</constant>
<constant name="NOTIFICATION_NODE_ACTIVE_CHANGED" value="90">
Notification received when [member node_active] has changed for this node.
</constant>
<constant name="NOTIFICATION_NODE_ACTIVE" value="91">
Notification received when the node has become active in tree. See [method _node_active].
</constant>
<constant name="NOTIFICATION_NODE_INACTIVE" value="92">
Notification received when the node has become inactive in tree. See [method _node_inactive].
</constant>
<constant name="NOTIFICATION_RESET_PHYSICS_INTERPOLATION" value="2001">
Notification received when [method reset_physics_interpolation] is called on the node or its ancestors.
</constant>
Expand Down
1 change: 1 addition & 0 deletions editor/editor_settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -621,6 +621,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
_initial_set("docks/scene_tree/start_create_dialog_fully_expanded", false);
_initial_set("docks/scene_tree/auto_expand_to_selected", true);
_initial_set("docks/scene_tree/center_node_on_reparent", false);
_initial_set("docks/scene_tree/show_node_active_checkbox", false);

// FileSystem
EDITOR_SETTING(Variant::INT, PROPERTY_HINT_RANGE, "docks/filesystem/thumbnail_size", 64, "32,128,16")
Expand Down
124 changes: 124 additions & 0 deletions editor/gui/scene_tree_editor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,20 @@ void SceneTreeEditor::_cell_button_pressed(Object *p_item, int p_column, int p_i
}
}
undo_redo->commit_action();
} else if (p_id == BUTTON_ACTIVE) {
undo_redo->create_action(TTR("Toggle Node Active"));
_toggle_active(n);
List<Node *> selection = editor_selection->get_selected_node_list();
if (selection.size() > 1 && selection.find(n) != nullptr) {
for (Node *nv : selection) {
ERR_FAIL_NULL(nv);
if (nv == n) {
continue;
}
_toggle_active(nv);
}
}
undo_redo->commit_action();
} else if (p_id == BUTTON_LOCK) {
undo_redo->create_action(TTR("Unlock Node"));
undo_redo->add_do_method(n, "remove_meta", "_edit_lock_");
Expand Down Expand Up @@ -217,6 +231,20 @@ void SceneTreeEditor::_toggle_visible(Node *p_node) {
}
}

void SceneTreeEditor::_toggle_active(Node *p_node) {
if (p_node->has_method("is_node_active_self") && p_node->has_method("set_node_active")) {
bool currentlyActive = bool(p_node->call("is_node_active_self"));
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
if (currentlyActive) {
undo_redo->add_do_method(p_node, "set_node_active", false);
undo_redo->add_undo_method(p_node, "set_node_active", true);
} else {
undo_redo->add_do_method(p_node, "set_node_active", true);
undo_redo->add_undo_method(p_node, "set_node_active", false);
}
}
}

void SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent) {
if (!p_node) {
return;
Expand Down Expand Up @@ -445,6 +473,16 @@ void SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent) {
_update_visibility_color(p_node, item);
}

if (p_node->has_method("is_node_active_self") && p_node->has_method("set_node_active")) {
bool show_node_active_checkbox = EDITOR_GET("docks/scene_tree/show_node_active_checkbox").operator bool();
_set_node_active_checkbox_enabled(item, p_node, show_node_active_checkbox);

const Callable active_in_tree_changed = callable_mp(this, &SceneTreeEditor::_node_active_in_tree_changed);
if (!p_node->is_connected(SceneStringName(node_active_in_tree_changed), active_in_tree_changed)) {
p_node->connect(SceneStringName(node_active_in_tree_changed), active_in_tree_changed.bind(p_node));
}
}

if (p_node->is_class("AnimationMixer")) {
bool is_pinned = AnimationPlayerEditor::get_singleton()->get_editing_node() == p_node && AnimationPlayerEditor::get_singleton()->is_pinned();

Expand Down Expand Up @@ -588,11 +626,83 @@ void SceneTreeEditor::_update_visibility_color(Node *p_node, TreeItem *p_item) {
}
}

void SceneTreeEditor::_set_node_active_checkbox_enabled(TreeItem *p_item, Node *p_node, bool p_enabled) {
int idx = p_item->get_button_by_id(0, BUTTON_ACTIVE);
if (p_enabled) {
if (idx == -1) {
// Only add checkbox if it does not exist.
bool is_active = p_node->call("is_node_active_self");
if (is_active) {
p_item->add_button(0, get_editor_theme_icon(SNAME("GuiChecked")), BUTTON_ACTIVE, false, TTR("Toggle Node Active"));
} else {
p_item->add_button(0, get_editor_theme_icon(SNAME("GuiUnchecked")), BUTTON_ACTIVE, false, TTR("Toggle Node Active"));
}
}
} else {
if (idx != -1) {
// Only erase checkbox if it exists.
p_item->erase_button(0, idx);
}
}
}

void SceneTreeEditor::_node_active_in_tree_changed(Node *p_node) {
if (!p_node || (p_node != get_scene_node() && !p_node->get_owner())) {
return;
}

TreeItem *item = _find(tree->get_root(), p_node->get_path());

if (!item) {
return;
}

bool show_node_active_checkbox = EDITOR_GET("docks/scene_tree/show_node_active_checkbox").operator bool();
if (!show_node_active_checkbox) {
return;
}

int idx = item->get_button_by_id(0, BUTTON_ACTIVE);
ERR_FAIL_COND(idx == -1);

bool node_active = false;

if (p_node->has_method("is_node_active_self")) {
node_active = p_node->call("is_node_active_self");
if (p_node->is_class("CanvasItem") || p_node->is_class("CanvasLayer") || p_node->is_class("Window")) {
CanvasItemEditor::get_singleton()->get_viewport_control()->queue_redraw();
}
}

if (node_active) {
item->set_button(0, idx, get_editor_theme_icon(SNAME("GuiChecked")));
} else {
item->set_button(0, idx, get_editor_theme_icon(SNAME("GuiUnchecked")));
}

if (p_node->call("can_process")) {
_clear_item_custom_color(item);
} else {
_set_item_custom_color(item, get_theme_color(SNAME("font_disabled_color"), EditorStringName(Editor)));
}

if (p_node->has_method("is_visible") && p_node->has_method("set_visible") && p_node->has_signal(SceneStringName(visibility_changed))) {
_node_visibility_changed(p_node);
}
}

void SceneTreeEditor::_set_item_custom_color(TreeItem *p_item, Color p_color) {
p_item->set_custom_color(0, p_color);
p_item->set_meta(SNAME("custom_color"), p_color);
}

void SceneTreeEditor::_clear_item_custom_color(TreeItem *p_item) {
if (p_item->has_meta(SNAME("custom_color"))) {
p_item->clear_custom_color(0);
p_item->remove_meta(SNAME("custom_color"));
}
}

void SceneTreeEditor::_node_script_changed(Node *p_node) {
if (tree_dirty) {
return;
Expand All @@ -617,6 +727,12 @@ void SceneTreeEditor::_node_removed(Node *p_node) {
}
}

if (p_node->has_signal(SceneStringName(node_active_in_tree_changed))) {
if (p_node->is_connected(SceneStringName(node_active_in_tree_changed), callable_mp(this, &SceneTreeEditor::_node_active_in_tree_changed))) {
p_node->disconnect(SceneStringName(node_active_in_tree_changed), callable_mp(this, &SceneTreeEditor::_node_active_in_tree_changed));
}
}

if (p_node == selected) {
selected = nullptr;
}
Expand Down Expand Up @@ -967,6 +1083,12 @@ void SceneTreeEditor::_notification(int p_what) {
_update_tree();
} break;

case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
if (EditorSettings::get_singleton()->check_changed_settings_in_group("docks/scene_tree")) {
_update_tree();
}
} break;

case NOTIFICATION_VISIBILITY_CHANGED: {
if (is_visible()) {
TreeItem *item = nullptr;
Expand Down Expand Up @@ -1842,6 +1964,8 @@ SceneTreeDialog::SceneTreeDialog() {
// Disable the OK button when no node is selected.
get_ok_button()->set_disabled(!tree->get_selected());
tree->connect("node_selected", callable_mp(this, &SceneTreeDialog::_selected_changed));

EDITOR_DEF("docks/scene_tree/show_node_active_checkbox", true);
}

SceneTreeDialog::~SceneTreeDialog() {
Expand Down
5 changes: 5 additions & 0 deletions editor/gui/scene_tree_editor.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ class SceneTreeEditor : public Control {
BUTTON_GROUPS = 7,
BUTTON_PIN = 8,
BUTTON_UNIQUE = 9,
BUTTON_ACTIVE = 10,
};

Tree *tree = nullptr;
Expand Down Expand Up @@ -121,12 +122,16 @@ class SceneTreeEditor : public Control {

void _cell_button_pressed(Object *p_item, int p_column, int p_id, MouseButton p_button);
void _toggle_visible(Node *p_node);
void _toggle_active(Node *p_node);
void _cell_multi_selected(Object *p_object, int p_cell, bool p_selected);
void _update_selection(TreeItem *item);
void _node_script_changed(Node *p_node);
void _set_node_active_checkbox_enabled(TreeItem *p_item, Node *p_node, bool p_enabled);
void _node_active_in_tree_changed(Node *p_node);
void _node_visibility_changed(Node *p_node);
void _update_visibility_color(Node *p_node, TreeItem *p_item);
void _set_item_custom_color(TreeItem *p_item, Color p_color);
void _clear_item_custom_color(TreeItem *p_item);
void _update_node_tooltip(Node *p_node, TreeItem *p_item);
void _queue_update_node_tooltip(Node *p_node, TreeItem *p_item);
void _tree_scroll_to_item(ObjectID p_item_id);
Expand Down
18 changes: 15 additions & 3 deletions scene/2d/camera_2d.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,16 @@ void Camera2D::_notification(int p_what) {
callable_mp(this, &Camera2D::_reset_just_exited).call_deferred();
} break;

case NOTIFICATION_NODE_ACTIVE_CHANGED: {
bool enabled_and_active = is_enabled();

if (enabled_and_active && !viewport->get_camera_2d()) {
make_current();
} else if (!enabled_and_active && is_current()) {
clear_current();
}
} break;

#ifdef TOOLS_ENABLED
case NOTIFICATION_DRAW: {
if (!is_inside_tree() || !_is_editing_in_editor()) {
Expand Down Expand Up @@ -497,15 +507,17 @@ void Camera2D::set_enabled(bool p_enabled) {
return;
}

if (enabled && !viewport->get_camera_2d()) {
bool enabled_and_active = is_enabled();

if (enabled_and_active && !viewport->get_camera_2d()) {
make_current();
} else if (!enabled && is_current()) {
} else if (!enabled_and_active && is_current()) {
clear_current();
}
}

bool Camera2D::is_enabled() const {
return enabled;
return enabled && is_node_active_in_tree();
}

Camera2D::Camera2DProcessCallback Camera2D::get_process_callback() const {
Expand Down
Loading
Loading