From 30e925faea07e6aef389da9125ee4159d0d89d88 Mon Sep 17 00:00:00 2001 From: Chris Cranford Date: Wed, 31 Jul 2024 18:29:40 -0400 Subject: [PATCH] GH-628 Synchronize signal slot disconnect UI updates --- .../component_panels/component_panel.cpp | 59 ++++++++++++ src/editor/component_panels/component_panel.h | 10 ++ .../component_panels/functions_panel.cpp | 70 +++++++------- src/editor/component_panels/functions_panel.h | 12 ++- src/editor/component_panels/graphs_panel.cpp | 96 ++++++++++--------- src/editor/component_panels/graphs_panel.h | 14 ++- 6 files changed, 174 insertions(+), 87 deletions(-) diff --git a/src/editor/component_panels/component_panel.cpp b/src/editor/component_panels/component_panel.cpp index 0251e0e7..8fcf59f5 100644 --- a/src/editor/component_panels/component_panel.cpp +++ b/src/editor/component_panels/component_panel.cpp @@ -153,6 +153,65 @@ Variant OrchestratorScriptComponentPanel::_tree_drag_data(const Vector2& p_posit return data; } +void OrchestratorScriptComponentPanel::_iterate_tree_item(TreeItem* p_item, const Callable& p_callable) +{ + p_callable.call(p_item); + + TreeItem* item = p_item->get_first_child(); + while (item) + { + _iterate_tree_item(item, p_callable); + item = item->get_next(); + } +} + +void OrchestratorScriptComponentPanel::_iterate_tree_items(const Callable& p_callback) +{ + if (TreeItem* root = _tree->get_root()) + _iterate_tree_item(root, p_callback); +} + +void OrchestratorScriptComponentPanel::_disconnect_slot(TreeItem* p_item) +{ + const Ref script = _orchestration->get_self(); + const Vector nodes = SceneUtils::find_all_nodes_for_script_in_edited_scene(script); + + const String method_name = _get_tree_item_name(p_item); + + for (Node* node : nodes) + { + TypedArray connections = node->get_incoming_connections(); + for (int i = 0; i < connections.size(); i++) + { + const Dictionary& dict = connections[i]; + const Callable& callable = dict["callable"]; + if (callable.get_method() != method_name) + continue; + + const Signal& signal = dict["signal"]; + + if (Node* source = Object::cast_to(ObjectDB::get_instance(signal.get_object_id()))) + { + source->disconnect(signal.get_name(), callable); + + // When disconnecting a slot from within Orchestrator, the SceneTreeDock and ConnectionsDock + // editor windows need to be updated, so they redraw the new state of the connections. + if (Node* editor_node = get_tree()->get_root()->get_child(0)) + { + if (Node* signals_node = editor_node->find_child("Signals", true, false)) + signals_node->call("update_tree"); + + if (Node* scene_editor = editor_node->find_child("*SceneTreeEditor*", true, false)) + scene_editor->call("update_tree"); + } + + update(); + return; + } + } + } +} + TreeItem* OrchestratorScriptComponentPanel::_create_item(TreeItem* p_parent, const String& p_text, const String& p_item_name, const String& p_icon_name) { TreeItem* item = p_parent->create_child(); diff --git a/src/editor/component_panels/component_panel.h b/src/editor/component_panels/component_panel.h index cdb92b28..234ce15c 100644 --- a/src/editor/component_panels/component_panel.h +++ b/src/editor/component_panels/component_panel.h @@ -44,6 +44,8 @@ class OrchestratorScriptComponentPanel : public VBoxContainer GDCLASS(OrchestratorScriptComponentPanel, VBoxContainer); static void _bind_methods(); + void _iterate_tree_item(TreeItem* p_item, const Callable& p_callable); + protected: String _title; //! Title Orchestration* _orchestration; //! The owning orchestration @@ -69,6 +71,14 @@ class OrchestratorScriptComponentPanel : public VBoxContainer Variant _tree_drag_data(const Vector2& p_position); //~ End Signal handlers + /// Iterates all tree items, calling the callable + /// @param p_callback the callback to call for all tree items + void _iterate_tree_items(const Callable& p_callback); + + /// Disconnect a slot + /// @param p_item the tree item + void _disconnect_slot(TreeItem* p_item); + /// Creates an item in the tree. /// @param p_parent the parent /// @param p_text the display text diff --git a/src/editor/component_panels/functions_panel.cpp b/src/editor/component_panels/functions_panel.cpp index c5cb8729..2aad27b9 100644 --- a/src/editor/component_panels/functions_panel.cpp +++ b/src/editor/component_panels/functions_panel.cpp @@ -38,33 +38,38 @@ void OrchestratorScriptFunctionsComponentPanel::_show_function_graph(TreeItem* p _tree->deselect_all(); } -void OrchestratorScriptFunctionsComponentPanel::_disconnect_slot(TreeItem* p_item) +void OrchestratorScriptFunctionsComponentPanel::_update_slots() { - const Ref script = _orchestration->get_self(); - const Vector nodes = SceneUtils::find_all_nodes_for_script_in_edited_scene(script); + if (_orchestration->get_type() != OrchestrationType::OT_Script) + return; - const String method_name = _get_tree_item_name(p_item); + const Ref script = _orchestration->get_self(); + const Vector script_nodes = SceneUtils::find_all_nodes_for_script_in_edited_scene(script); + const String base_type = script->get_instance_base_type(); - for (Node* node : nodes) - { - TypedArray connections = node->get_incoming_connections(); - for (int i = 0; i < connections.size(); i++) + _iterate_tree_items(callable_mp_lambda(this, [&](TreeItem* item) { + if (item->has_meta("__name")) { - const Dictionary& dict = connections[i]; - const Callable& callable = dict["callable"]; - if (callable.get_method() != method_name) - continue; - - const Signal& signal = dict["signal"]; - - if (Node *source = Object::cast_to(ObjectDB::get_instance(signal.get_object_id()))) + Ref graph = _orchestration->get_graph(item->get_meta("__name")); + if (graph.is_valid() && graph->get_flags().has_flag(OScriptGraph::GraphFlags::GF_FUNCTION)) { - source->disconnect(signal.get_name(), callable); - update(); - return; + const String function_name = item->get_meta("__name"); + if (SceneUtils::has_any_signals_connected_to_function(function_name, base_type, script_nodes)) + { + if (item->get_button_count(0) == 0) + { + item->add_button(0, SceneUtils::get_editor_icon("Slot")); + item->set_meta("__slot", true); + } + } + else if (item->get_button_count(0) > 0) + { + item->erase_button(0, 0); + item->remove_meta("__slot"); + } } } - } + })); } PackedStringArray OrchestratorScriptFunctionsComponentPanel::_get_existing_names() const @@ -217,16 +222,6 @@ void OrchestratorScriptFunctionsComponentPanel::update() { _clear_tree(); - Vector script_nodes; - String base_type; - - if (_orchestration->get_type() == OrchestrationType::OT_Script) - { - const Ref script = _orchestration->get_self(); - script_nodes = SceneUtils::find_all_nodes_for_script_in_edited_scene(script); - base_type = script->get_instance_base_type(); - } - OrchestratorSettings* settings = OrchestratorSettings::get_singleton(); bool use_friendly_names = settings->get_setting("ui/components_panel/show_function_friendly_names", true); @@ -239,12 +234,7 @@ void OrchestratorScriptFunctionsComponentPanel::update() if (use_friendly_names) friendly_name = graph->get_graph_name().capitalize(); - TreeItem* item = _create_item(_tree->get_root(), friendly_name, graph->get_graph_name(), "MemberMethod"); - if (SceneUtils::has_any_signals_connected_to_function(graph->get_graph_name(), base_type, script_nodes)) - { - item->add_button(0, SceneUtils::get_editor_icon("Slot")); - item->set_meta("__slot", true); - } + _create_item(_tree->get_root(), friendly_name, graph->get_graph_name(), "MemberMethod"); } if (_tree->get_root()->get_child_count() == 0) @@ -255,6 +245,8 @@ void OrchestratorScriptFunctionsComponentPanel::update() return; } + _update_slots(); + OrchestratorScriptComponentPanel::update(); } @@ -267,6 +259,12 @@ void OrchestratorScriptFunctionsComponentPanel::_notification(int p_what) if (p_what == NOTIFICATION_READY) { + _slot_update_timer = memnew(Timer); + _slot_update_timer->set_wait_time(1); + _slot_update_timer->set_autostart(true); + _slot_update_timer->connect("timeout", callable_mp(this, &OrchestratorScriptFunctionsComponentPanel::_update_slots)); + add_child(_slot_update_timer); + HBoxContainer* container = _get_panel_hbox(); _override_button = memnew(Button); diff --git a/src/editor/component_panels/functions_panel.h b/src/editor/component_panels/functions_panel.h index 79a87d77..32b407aa 100644 --- a/src/editor/component_panels/functions_panel.h +++ b/src/editor/component_panels/functions_panel.h @@ -19,6 +19,8 @@ #include "editor/component_panels/component_panel.h" +#include + class OrchestratorScriptFunctionsComponentPanel : public OrchestratorScriptComponentPanel { GDCLASS(OrchestratorScriptFunctionsComponentPanel, OrchestratorScriptComponentPanel); @@ -35,6 +37,8 @@ class OrchestratorScriptFunctionsComponentPanel : public OrchestratorScriptCompo Button* _override_button{ nullptr }; Callable _new_function_callback; + Timer* _slot_update_timer{ nullptr }; + protected: //~ Begin OrchestratorScriptComponentPanel Interface String _get_unique_name_prefix() const override { return "NewFunction"; } @@ -55,9 +59,11 @@ class OrchestratorScriptFunctionsComponentPanel : public OrchestratorScriptCompo //~ Begin Signal handlers void _show_function_graph(TreeItem* p_item); - void _disconnect_slot(TreeItem* p_item); //~ End Signal handlers + /// Updates the slot icons on tree items + void _update_slots(); + /// Default constructor OrchestratorScriptFunctionsComponentPanel() = default; @@ -66,9 +72,9 @@ class OrchestratorScriptFunctionsComponentPanel : public OrchestratorScriptCompo void update() override; //~ End OrchestratorScriptViewSection Interface - /// Godot's notification callback - /// @param p_what the notification type + //~ Begin Wrapped Interface void _notification(int p_what); + //~ End Wrapped Interface /// Construct the function component panel /// @param p_orchestration the orchestration diff --git a/src/editor/component_panels/graphs_panel.cpp b/src/editor/component_panels/graphs_panel.cpp index bbd66a87..c7ca8df6 100644 --- a/src/editor/component_panels/graphs_panel.cpp +++ b/src/editor/component_panels/graphs_panel.cpp @@ -16,6 +16,7 @@ // #include "editor/component_panels/graphs_panel.h" +#include "common/callable_lambda.h" #include "common/scene_utils.h" #include "common/settings.h" #include "editor/script_connections.h" @@ -53,35 +54,6 @@ void OrchestratorScriptGraphsComponentPanel::_remove_graph_function(TreeItem* p_ update(); } -void OrchestratorScriptGraphsComponentPanel::_disconnect_slot(TreeItem* p_item) -{ - const Ref script = _orchestration->get_self(); - const Vector nodes = SceneUtils::find_all_nodes_for_script_in_edited_scene(script); - - const String method_name = _get_tree_item_name(p_item); - - for (Node* node : nodes) - { - TypedArray connections = node->get_incoming_connections(); - for (int i = 0; i < connections.size(); i++) - { - const Dictionary& dict = connections[i]; - const Callable& callable = dict["callable"]; - if (callable.get_method() != method_name) - continue; - - const Signal& signal = dict["signal"]; - - if (Node* source = Object::cast_to(ObjectDB::get_instance(signal.get_object_id()))) - { - source->disconnect(signal.get_name(), callable); - update(); - return; - } - } - } -} - PackedStringArray OrchestratorScriptGraphsComponentPanel::_get_existing_names() const { PackedStringArray result; @@ -216,6 +188,36 @@ void OrchestratorScriptGraphsComponentPanel::_handle_button_clicked(TreeItem* p_ dialog->popup_connections(_get_tree_item_name(p_item), nodes); } +void OrchestratorScriptGraphsComponentPanel::_update_slots() +{ + if (_orchestration->get_type() != OrchestrationType::OT_Script) + return; + + const Ref script = _orchestration->get_self(); + const Vector script_nodes = SceneUtils::find_all_nodes_for_script_in_edited_scene(script); + const String base_type = script->get_instance_base_type(); + + _iterate_tree_items(callable_mp_lambda(this, [&](TreeItem* item) { + if (item->has_meta("__name")) + { + const String function_name = item->get_meta("__name"); + if (SceneUtils::has_any_signals_connected_to_function(function_name, base_type, script_nodes)) + { + if (item->get_button_count(0) == 0) + { + item->add_button(0, SceneUtils::get_editor_icon("Slot")); + item->set_meta("__slot", true); + } + } + else if (item->get_button_count(0) > 0) + { + item->erase_button(0, 0); + item->remove_meta("__slot"); + } + } + })); +} + void OrchestratorScriptGraphsComponentPanel::update() { _clear_tree(); @@ -229,16 +231,6 @@ void OrchestratorScriptGraphsComponentPanel::update() return; } - Vector script_nodes; - String base_type; - - if (_orchestration->get_type() == OrchestrationType::OT_Script) - { - const Ref script = _orchestration->get_self(); - script_nodes = SceneUtils::find_all_nodes_for_script_in_edited_scene(script); - base_type = script->get_instance_base_type(); - } - OrchestratorSettings* settings = OrchestratorSettings::get_singleton(); bool use_friendly_names = settings->get_setting("ui/components_panel/show_graph_friendly_names", true); @@ -263,19 +255,33 @@ void OrchestratorScriptGraphsComponentPanel::update() if (use_friendly_names) friendly_name = vformat("%s Event", function_name.capitalize()); - TreeItem* func = _create_item(item, friendly_name, function_name, "PlayStart"); - if (SceneUtils::has_any_signals_connected_to_function(function_name, base_type, script_nodes)) - { - func->add_button(0, SceneUtils::get_editor_icon("Slot")); - func->set_meta("__slot", true); - } + _create_item(item, friendly_name, function_name, "PlayStart"); } } } + _update_slots(); + OrchestratorScriptComponentPanel::update(); } +void OrchestratorScriptGraphsComponentPanel::_notification(int p_what) +{ + #if GODOT_VERSION < 0x040300 + // Godot does not dispatch to parent (shrugs) + OrchestratorScriptComponentPanel::_notification(p_what); + #endif + + if (p_what == NOTIFICATION_READY) + { + _slot_update_timer = memnew(Timer); + _slot_update_timer->set_wait_time(1); + _slot_update_timer->set_autostart(true); + _slot_update_timer->connect("timeout", callable_mp(this, &OrchestratorScriptGraphsComponentPanel::_update_slots)); + add_child(_slot_update_timer); + } +} + void OrchestratorScriptGraphsComponentPanel::_bind_methods() { ADD_SIGNAL(MethodInfo("show_graph_requested", PropertyInfo(Variant::STRING, "graph_name"))); diff --git a/src/editor/component_panels/graphs_panel.h b/src/editor/component_panels/graphs_panel.h index 4a642d6c..6d138641 100644 --- a/src/editor/component_panels/graphs_panel.h +++ b/src/editor/component_panels/graphs_panel.h @@ -19,6 +19,8 @@ #include "editor/component_panels/component_panel.h" +#include + class OrchestratorScriptGraphsComponentPanel : public OrchestratorScriptComponentPanel { GDCLASS(OrchestratorScriptGraphsComponentPanel, OrchestratorScriptComponentPanel); @@ -34,6 +36,8 @@ class OrchestratorScriptGraphsComponentPanel : public OrchestratorScriptComponen CM_DISCONNECT_SLOT }; + Timer* _slot_update_timer{ nullptr }; + protected: //~ Begin OrchestratorScriptComponentPanel Interface String _get_unique_name_prefix() const override { return "NewEventGraph"; } @@ -50,6 +54,9 @@ class OrchestratorScriptGraphsComponentPanel : public OrchestratorScriptComponen void _handle_button_clicked(TreeItem* p_item, int p_column, int p_id, int p_mouse_button) override; //~ End OrchestratorScriptComponentPanel Interface + /// Updates the slot icons on tree items + void _update_slots(); + /// Notifies the script view to show the specified graph associated with the tree item /// @param p_item the graph tree item, should not be null void _show_graph_item(TreeItem* p_item); @@ -67,9 +74,6 @@ class OrchestratorScriptGraphsComponentPanel : public OrchestratorScriptComponen /// @param p_item the graph function item, should not be null void _remove_graph_function(TreeItem* p_item); - /// Handles disconnecting a signal slot function from the signal - void _disconnect_slot(TreeItem* p_item); - /// Default constructor OrchestratorScriptGraphsComponentPanel() = default; @@ -78,6 +82,10 @@ class OrchestratorScriptGraphsComponentPanel : public OrchestratorScriptComponen void update() override; //~ End OrchestratorScriptViewSection Interface + //~ Begin Wrapped Interface + void _notification(int p_what); + //~ End Wrapped Interface + /// Constructs the graphs component panel /// @param p_orchestration the orchestration explicit OrchestratorScriptGraphsComponentPanel(Orchestration* p_orchestration);