Skip to content

Commit

Permalink
Add the ability to expose nodes for direct access in instantiated scenes
Browse files Browse the repository at this point in the history
  • Loading branch information
yahkr committed Jan 6, 2025
1 parent 1aaf20b commit 5418e0f
Show file tree
Hide file tree
Showing 15 changed files with 346 additions and 11 deletions.
3 changes: 3 additions & 0 deletions doc/classes/EditorSettings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,9 @@
<member name="docks/scene_tree/ask_before_deleting_related_animation_tracks" type="bool" setter="" getter="">
If [code]true[/code], when a node is deleted with animation tracks referencing it, a confirmation dialog appears before the tracks are deleted. The dialog will appear even when using the "Delete (No Confirm)" shortcut.
</member>
<member name="docks/scene_tree/ask_before_revoking_node_exposure" type="bool" setter="" getter="">
If [code]true[/code], displays a confirmation dialog after left-clicking the "exposed" icon next to a node name in the Scene tree dock. When clicked, this icon revokes the node's scene exposure, which can impact the behavior of scripts that rely on this exposed node due to identifiers not being found anymore.
</member>
<member name="docks/scene_tree/ask_before_revoking_unique_name" type="bool" setter="" getter="">
If [code]true[/code], displays a confirmation dialog after left-clicking the "percent" icon next to a node name in the Scene tree dock. When clicked, this icon revokes the node's scene-unique name, which can impact the behavior of scripts that rely on this scene-unique name due to identifiers not being found anymore.
</member>
Expand Down
12 changes: 10 additions & 2 deletions editor/editor_data.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -724,9 +724,17 @@ bool EditorData::check_and_update_scene(int p_idx) {
for (const Node *E : edited_scene.write[p_idx].selection) {
NodePath p = edited_scene[p_idx].root->get_path_to(E);
Node *new_node = new_scene->get_node(p);
if (new_node) {
new_selection.push_back(new_node);
// Node can't be found, skip it.
if (!new_node) {
continue;
}

// Node is no longer exposed, skip it.
if (E->has_meta(META_EXPOSED_IN_OWNER) && !new_node->has_meta(META_EXPOSED_IN_OWNER)) {
continue;
}

new_selection.push_back(new_node);
}

new_scene->set_scene_file_path(edited_scene[p_idx].root->get_scene_file_path());
Expand Down
1 change: 1 addition & 0 deletions editor/editor_settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
_initial_set("interface/editors/show_scene_tree_root_selection", true);
_initial_set("interface/editors/derive_script_globals_by_name", true);
_initial_set("docks/scene_tree/ask_before_revoking_unique_name", true);
_initial_set("docks/scene_tree/ask_before_revoking_node_exposure", true);

// Inspector
EDITOR_SETTING_BASIC(Variant::INT, PROPERTY_HINT_RANGE, "interface/inspector/max_array_dictionary_items_per_page", 20, "10,100,1")
Expand Down
81 changes: 80 additions & 1 deletion editor/gui/scene_tree_editor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,18 @@ void SceneTreeEditor::_cell_button_pressed(Object *p_item, int p_column, int p_i
} else {
_revoke_unique_name();
}
} else if (p_id == BUTTON_EXPOSED) {
bool ask_before_revoking_node_exposure = EDITOR_GET("docks/scene_tree/ask_before_revoking_node_exposure");
revoke_node = n;
if (ask_before_revoking_node_exposure) {
String msg = vformat(TTR("Unexpose node \"%s\"?"), n->get_name());
ask_before_revoke_node_exposure_checkbox->set_pressed(false);
revoke_node_exposure_dialog_label->set_text(msg);
revoke_node_exposure->reset_size();
revoke_node_exposure->popup_centered();
} else {
_toggle_node_exposure();
}
}
}

Expand All @@ -231,6 +243,39 @@ void SceneTreeEditor::_revoke_unique_name() {
undo_redo->commit_action();
}

void SceneTreeEditor::_update_ask_before_revoking_node_exposure() {
if (ask_before_revoke_node_exposure_checkbox->is_pressed()) {
EditorSettings::get_singleton()->set("docks/scene_tree/ask_before_revoking_node_exposure", false);
ask_before_revoke_node_exposure_checkbox->set_pressed(false);
}
_toggle_node_exposure();
}

void SceneTreeEditor::_toggle_node_exposure() {
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
bool enabled = revoke_node->has_meta(META_MARKED_FOR_EXPOSURE);
undo_redo->create_action(enabled ? TTR("Unexpose Node In Scene") : TTR("Expose Node In Scene"));
if (revoke_node->get_owner() == get_tree()->get_edited_scene_root()) {
if (enabled) {
undo_redo->add_do_method(revoke_node, "remove_meta", META_EXPOSED_IN_OWNER);
undo_redo->add_undo_method(revoke_node, "set_meta", META_EXPOSED_IN_OWNER, true);
} else {
undo_redo->add_do_method(revoke_node, "set_meta", META_EXPOSED_IN_OWNER, true);
undo_redo->add_undo_method(revoke_node, "remove_meta", META_EXPOSED_IN_OWNER);
}
}
if (enabled) {
undo_redo->add_do_method(revoke_node, "remove_meta", META_MARKED_FOR_EXPOSURE);
undo_redo->add_undo_method(revoke_node, "set_meta", META_MARKED_FOR_EXPOSURE, true);
} else {
undo_redo->add_do_method(revoke_node, "set_meta", META_MARKED_FOR_EXPOSURE, true);
undo_redo->add_undo_method(revoke_node, "remove_meta", META_MARKED_FOR_EXPOSURE);
}
undo_redo->add_do_method(this, "_update_tree");
undo_redo->add_undo_method(this, "_update_tree");
undo_redo->commit_action();
}

void SceneTreeEditor::_toggle_visible(Node *p_node) {
if (p_node->has_method("is_visible") && p_node->has_method("set_visible")) {
bool v = bool(p_node->call("is_visible"));
Expand Down Expand Up @@ -274,8 +319,16 @@ void SceneTreeEditor::_update_node_subtree(Node *p_node, TreeItem *p_parent, boo
bool part_of_subscene = false;

if (!display_foreign && p_node->get_owner() != get_scene_node() && p_node != get_scene_node()) {
if ((show_enabled_subscene || can_open_instance) && p_node->get_owner() && (get_scene_node()->is_editable_instance(p_node->get_owner()))) {
if ((show_enabled_subscene || can_open_instance) && p_node->get_owner() && (get_scene_node()->is_editable_instance(p_node->get_owner()) || (p_node->has_meta(META_EXPOSED_IN_INSTANCE) && p_node->has_meta(META_EXPOSED_IN_OWNER)))) {
part_of_subscene = true;
//allow
} else if (p_node->has_exposed_nodes()) {
for (int i = 0; i < p_node->get_child_count(); i++) {
if (!get_scene_node()->is_editable_instance(p_node->get_child(i)->get_owner()) || p_node->get_child(i)->has_meta(META_EXPOSED_IN_INSTANCE)) {
_update_node_subtree(p_node->get_child(i), p_parent);
}
}
return;
// Allow.
} else {
// Stale node, remove recursively.
Expand Down Expand Up @@ -319,6 +372,10 @@ void SceneTreeEditor::_update_node_subtree(Node *p_node, TreeItem *p_parent, boo
}
} else {
index = p_node->get_index(false);
// Shift the exposed nodes based on how many siblings already exist to maintain exposed node order.
if (!p_node->get_owner()->is_editable_instance(p_node) && p_node->has_meta(META_EXPOSED_IN_OWNER)) {
index = p_parent->get_child_count() + index;
}
item = tree->create_item(p_parent, index);
}

Expand Down Expand Up @@ -498,6 +555,16 @@ void SceneTreeEditor::_update_node(Node *p_node, TreeItem *p_item, bool p_part_o
p_item->add_button(0, get_editor_theme_icon(warning_icon), BUTTON_WARNING, false, TTR("Node configuration warning:") + all_warnings);
}

if (p_node->has_meta(META_EXPOSED_IN_OWNER) && p_node->get_owner() == EditorNode::get_singleton()->get_edited_scene()) {
p_item->add_button(0, get_editor_theme_icon(SNAME("SceneExposedNode")), BUTTON_EXPOSED, false, TTR("This node will be exposed in the editor when this scene is instantiated.") + "\n" + TTR("Click to disable this."));
} else {
if (p_node->has_meta(META_MARKED_FOR_EXPOSURE)) {
p_item->add_button(0, get_editor_theme_icon(SNAME("SceneExposedNode")), BUTTON_EXPOSED, false, TTR("This node has been exposed in the underlying scene.") + "\n" + TTR("This node will be exposed in the editor when this scene is instantiated.") + "\n" + TTR("Click to disable this."));
} else if (p_node->has_meta(META_EXPOSED_IN_INSTANCE)) {
p_item->add_button(0, get_editor_theme_icon(SNAME("SceneExposedNodeInstanced")), BUTTON_EXPOSED, p_node->get_owner() != EditorNode::get_singleton()->get_edited_scene(), TTR("This node has been exposed in the underlying scene."));
}
}

if (p_node->is_unique_name_in_owner()) {
const bool disabled = p_node->get_owner() != EditorNode::get_singleton()->get_edited_scene();
String button_text = vformat(TTR("This node can be accessed from anywhere within the scene it belongs to by using the '%s' prefix in the node path."), UNIQUE_NODE_PREFIX);
Expand Down Expand Up @@ -2003,6 +2070,18 @@ SceneTreeEditor::SceneTreeEditor(bool p_label, bool p_can_rename, bool p_can_ope
ask_before_revoke_checkbox->set_tooltip_text(TTR("This dialog can also be enabled/disabled in the Editor Settings: Docks > Scene Tree > Ask Before Revoking Unique Name."));
vb->add_child(ask_before_revoke_checkbox);

revoke_node_exposure = memnew(ConfirmationDialog);
revoke_node_exposure->set_ok_button_text(TTR("Revoke"));
add_child(revoke_node_exposure);
revoke_node_exposure->connect(SceneStringName(confirmed), callable_mp(this, &SceneTreeEditor::_update_ask_before_revoking_node_exposure));
VBoxContainer *nevb = memnew(VBoxContainer);
revoke_node_exposure->add_child(nevb);
revoke_node_exposure_dialog_label = memnew(Label);
nevb->add_child(revoke_node_exposure_dialog_label);
ask_before_revoke_node_exposure_checkbox = memnew(CheckBox(TTR("Don't Ask Again")));
ask_before_revoke_node_exposure_checkbox->set_tooltip_text(TTR("This dialog can also be enabled/disabled in the Editor Settings: Docks > Scene Tree > Ask Before Revoking Node Exposure."));
nevb->add_child(ask_before_revoke_node_exposure_checkbox);

script_types = memnew(List<StringName>);
ClassDB::get_inheriters_from_class("Script", script_types);
}
Expand Down
7 changes: 7 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_EXPOSED = 10,
};

struct CachedNode {
Expand Down Expand Up @@ -125,6 +126,10 @@ class SceneTreeEditor : public Control {
CheckBox *ask_before_revoke_checkbox = nullptr;
Node *revoke_node = nullptr;

ConfirmationDialog *revoke_node_exposure = nullptr;
Label *revoke_node_exposure_dialog_label = nullptr;
CheckBox *ask_before_revoke_node_exposure_checkbox = nullptr;

bool auto_expand_selected = true;
bool connect_to_script_mode = false;
bool connecting_signal = false;
Expand Down Expand Up @@ -218,6 +223,8 @@ class SceneTreeEditor : public Control {

void _update_ask_before_revoking_unique_name();
void _revoke_unique_name();
void _update_ask_before_revoking_node_exposure();
void _toggle_node_exposure();

public:
// Public for use with callable_mp.
Expand Down
1 change: 1 addition & 0 deletions editor/icons/SceneExposedNode.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions editor/icons/SceneExposedNodeInstanced.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 9 additions & 1 deletion editor/import/3d/resource_importer_scene.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1409,6 +1409,10 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, HashMap<
}
}

if (!isroot && node_settings.has("import/exposed")) {
p_node->set_meta(META_EXPOSED_IN_OWNER, bool(node_settings["import/exposed"]));
}

if (Object::cast_to<ImporterMeshInstance3D>(p_node)) {
ObjectID node_id = p_node->get_instance_id();
for (int i = 0; i < post_importer_plugins.size(); i++) {
Expand Down Expand Up @@ -1988,9 +1992,11 @@ void ResourceImporterScene::get_internal_import_options(InternalImportCategory p
switch (p_category) {
case INTERNAL_IMPORT_CATEGORY_NODE: {
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "import/skip_import", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "import/exposed", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));
} break;
case INTERNAL_IMPORT_CATEGORY_MESH_3D_NODE: {
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "import/skip_import", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "import/exposed", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "generate/physics", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "generate/navmesh", PROPERTY_HINT_ENUM, "Disabled,Mesh + NavMesh,NavMesh Only"), 0));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "physics/body_type", PROPERTY_HINT_ENUM, "Static,Dynamic,Area"), 0));
Expand Down Expand Up @@ -2067,6 +2073,7 @@ void ResourceImporterScene::get_internal_import_options(InternalImportCategory p
} break;
case INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE: {
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "import/skip_import", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "import/exposed", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "optimizer/enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), true));
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "optimizer/max_velocity_error", PROPERTY_HINT_RANGE, "0,1,0.01"), 0.01));
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "optimizer/max_angular_error", PROPERTY_HINT_RANGE, "0,1,0.01"), 0.01));
Expand All @@ -2079,6 +2086,7 @@ void ResourceImporterScene::get_internal_import_options(InternalImportCategory p
} break;
case INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE: {
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "import/skip_import", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "import/exposed", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "rest_pose/load_pose", PROPERTY_HINT_ENUM, "Default Pose,Use AnimationPlayer,Load External Animation", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 0));
r_options->push_back(ImportOption(PropertyInfo(Variant::OBJECT, "rest_pose/external_animation_library", PROPERTY_HINT_RESOURCE_TYPE, "Animation,AnimationLibrary", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), Variant()));
r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "rest_pose/selected_animation", PROPERTY_HINT_ENUM, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), ""));
Expand Down Expand Up @@ -2619,7 +2627,7 @@ Node *ResourceImporterScene::_generate_meshes(Node *p_node, const Dictionary &p_
mesh_node->set_gi_mode(GeometryInstance3D::GI_MODE_STATIC);
} break;
}

mesh_node->set_meta(META_EXPOSED_IN_OWNER, src_mesh_node->has_meta(META_EXPOSED_IN_OWNER));
mesh_node->set_layer_mask(src_mesh_node->get_layer_mask());
mesh_node->set_cast_shadows_setting(src_mesh_node->get_cast_shadows_setting());
mesh_node->set_visible(src_mesh_node->is_visible());
Expand Down
Loading

0 comments on commit 5418e0f

Please sign in to comment.