Skip to content

Commit

Permalink
GH-628 Synchronize signal slot disconnect UI updates
Browse files Browse the repository at this point in the history
  • Loading branch information
Naros committed Aug 1, 2024
1 parent 10e8a74 commit 832ac92
Show file tree
Hide file tree
Showing 6 changed files with 174 additions and 87 deletions.
59 changes: 59 additions & 0 deletions src/editor/component_panels/component_panel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<OScript> script = _orchestration->get_self();
const Vector<Node*> 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<Dictionary> 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<Node>(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();
Expand Down
10 changes: 10 additions & 0 deletions src/editor/component_panels/component_panel.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
70 changes: 34 additions & 36 deletions src/editor/component_panels/functions_panel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<OScript> script = _orchestration->get_self();
const Vector<Node*> 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<OScript> script = _orchestration->get_self();
const Vector<Node*> 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<Dictionary> 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<Node>(ObjectDB::get_instance(signal.get_object_id())))
Ref<OScriptGraph> 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
Expand Down Expand Up @@ -217,16 +222,6 @@ void OrchestratorScriptFunctionsComponentPanel::update()
{
_clear_tree();

Vector<Node*> script_nodes;
String base_type;

if (_orchestration->get_type() == OrchestrationType::OT_Script)
{
const Ref<OScript> 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);

Expand All @@ -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)
Expand All @@ -255,6 +245,8 @@ void OrchestratorScriptFunctionsComponentPanel::update()
return;
}

_update_slots();

OrchestratorScriptComponentPanel::update();
}

Expand All @@ -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);
Expand Down
12 changes: 9 additions & 3 deletions src/editor/component_panels/functions_panel.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@

#include "editor/component_panels/component_panel.h"

#include <godot_cpp/classes/timer.hpp>

class OrchestratorScriptFunctionsComponentPanel : public OrchestratorScriptComponentPanel
{
GDCLASS(OrchestratorScriptFunctionsComponentPanel, OrchestratorScriptComponentPanel);
Expand All @@ -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"; }
Expand All @@ -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;

Expand All @@ -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
Expand Down
96 changes: 51 additions & 45 deletions src/editor/component_panels/graphs_panel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -53,35 +54,6 @@ void OrchestratorScriptGraphsComponentPanel::_remove_graph_function(TreeItem* p_
update();
}

void OrchestratorScriptGraphsComponentPanel::_disconnect_slot(TreeItem* p_item)
{
const Ref<OScript> script = _orchestration->get_self();
const Vector<Node*> 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<Dictionary> 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<Node>(ObjectDB::get_instance(signal.get_object_id())))
{
source->disconnect(signal.get_name(), callable);
update();
return;
}
}
}
}

PackedStringArray OrchestratorScriptGraphsComponentPanel::_get_existing_names() const
{
PackedStringArray result;
Expand Down Expand Up @@ -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<OScript> script = _orchestration->get_self();
const Vector<Node*> 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();
Expand All @@ -229,16 +231,6 @@ void OrchestratorScriptGraphsComponentPanel::update()
return;
}

Vector<Node*> script_nodes;
String base_type;

if (_orchestration->get_type() == OrchestrationType::OT_Script)
{
const Ref<OScript> 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);

Expand All @@ -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")));
Expand Down
Loading

0 comments on commit 832ac92

Please sign in to comment.