Skip to content

Commit

Permalink
GH-58 Initial support for undo/redo
Browse files Browse the repository at this point in the history
* Moving nodes
* Changing pin default values
* Connect and disconnect pins
* Renaming components (graphs, functions, variables, signals)
* Changing the Orchestration base type
* Export variable toggle in component panel
* Inspector changes already supported by Godot
  • Loading branch information
Naros committed Jul 14, 2024
1 parent 0edcefa commit ed4648e
Show file tree
Hide file tree
Showing 33 changed files with 449 additions and 188 deletions.
30 changes: 26 additions & 4 deletions src/editor/component_panels/component_panel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <godot_cpp/classes/accept_dialog.hpp>
#include <godot_cpp/classes/button.hpp>
#include <godot_cpp/classes/confirmation_dialog.hpp>
#include <godot_cpp/classes/editor_undo_redo_manager.hpp>
#include <godot_cpp/classes/h_box_container.hpp>
#include <godot_cpp/classes/input_event_mouse_button.hpp>
#include <godot_cpp/classes/label.hpp>
Expand Down Expand Up @@ -78,9 +79,16 @@ void OrchestratorScriptComponentPanel::_tree_item_edited()
return;
}

_handle_item_renamed(old_name, new_name);

update();
if (_can_be_renamed(old_name, new_name))
{
EditorUndoRedoManager* undo = OrchestratorPlugin::get_singleton()->get_undo_redo();
undo->create_action("Orchestration: Rename " + _get_item_name());
undo->add_do_method(this, "_rename_item", old_name, new_name);
undo->add_do_method(this, "update");
undo->add_undo_method(this, "_rename_item", new_name, old_name);
undo->add_undo_method(this, "update");
undo->commit_action();
}
}

void OrchestratorScriptComponentPanel::_tree_item_mouse_selected(const Vector2& p_position, int p_button)
Expand Down Expand Up @@ -111,6 +119,10 @@ void OrchestratorScriptComponentPanel::_remove_confirmed()
_handle_remove(_tree->get_selected());

update();

// Clear history related to component panels
EditorUndoRedoManager* undo = OrchestratorPlugin::get_singleton()->get_undo_redo();
undo->get_history_undo_redo(undo->get_object_history_id(this))->clear_history();
}
}

Expand Down Expand Up @@ -233,7 +245,13 @@ void OrchestratorScriptComponentPanel::_show_notification(const String& p_messag

void OrchestratorScriptComponentPanel::_confirm_removal(TreeItem* p_item)
{
_confirm->set_text(_get_remove_confirm_text(p_item) + "\n\nDo you want to continue?");
String remove_text = _get_remove_confirm_text(p_item);
if (!_can_remove_be_undone())
remove_text += "\n\nThis operation cannnot be undone.\nDo you want to continue?";
else
remove_text += "\n\nDo you want to continue?";

_confirm->set_text(remove_text);
_confirm->set_ok_button_text("Yes");
_confirm->set_cancel_button_text("No");
_confirm->reset_size();
Expand Down Expand Up @@ -386,6 +404,10 @@ void OrchestratorScriptComponentPanel::_notification(int p_what)

void OrchestratorScriptComponentPanel::_bind_methods()
{
// Needed for undo/redo
ClassDB::bind_method(D_METHOD("update"), &OrchestratorScriptComponentPanel::update);
ClassDB::bind_method(D_METHOD("_rename_item", "old_name", "new_name"), &OrchestratorScriptComponentPanel::_rename_item);

ADD_SIGNAL(MethodInfo("scroll_to_item", PropertyInfo(Variant::OBJECT, "item")));
}

Expand Down
17 changes: 16 additions & 1 deletion src/editor/component_panels/component_panel.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,10 @@ class OrchestratorScriptComponentPanel : public VBoxContainer
/// @return the HBoxContainer child of the top panel, never null
HBoxContainer* _get_panel_hbox() const { return _panel_hbox; }

/// Specifies whether remove can be undone.
/// @return true if the remove can be undone, false otherwise.
virtual bool _can_remove_be_undone() const { return false; }

/// Get the prefix used for creating new elements
/// @return the new unique name prefix
virtual String _get_unique_name_prefix() const { return "item"; }
Expand Down Expand Up @@ -159,10 +163,21 @@ class OrchestratorScriptComponentPanel : public VBoxContainer
/// Handles when an item is selected
virtual void _handle_item_selected() { }

/// Returns whether an item can be renamed
/// @param p_old_name the old name
/// @param p_new_name the new name
/// @return true if the rename can be applied, false otherwise
virtual bool _can_be_renamed(const String& p_old_name, const String& p_new_name) { return false; }

/// Renames an item
/// @param p_old_name the old name
/// @param p_new_name the new name
void _rename_item(const String& p_old_name, const String& p_new_name) { _handle_item_renamed(p_old_name, p_new_name); }

/// Handles when an item is renamed
/// @param p_old_name the old name
/// @param p_new_name the new name
virtual bool _handle_item_renamed(const String& p_old_name, const String& p_new_name) { return false; }
virtual void _handle_item_renamed(const String& p_old_name, const String& p_new_name) { }

/// Handles the removal of the item
/// @param p_item the tree item to be removed
Expand Down
8 changes: 6 additions & 2 deletions src/editor/component_panels/functions_panel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,17 +152,21 @@ void OrchestratorScriptFunctionsComponentPanel::_handle_item_activated(TreeItem*
_show_function_graph(p_item);
}

bool OrchestratorScriptFunctionsComponentPanel::_handle_item_renamed(const String& p_old_name, const String& p_new_name)
bool OrchestratorScriptFunctionsComponentPanel::_can_be_renamed(const String& p_old_name, const String& p_new_name)
{
if (_get_existing_names().has(p_new_name))
{
_show_notification("A function with the name '" + p_new_name + "' already exists.");
return false;
}

return true;
}

void OrchestratorScriptFunctionsComponentPanel::_handle_item_renamed(const String& p_old_name, const String& p_new_name)
{
_orchestration->rename_function(p_old_name, p_new_name);
emit_signal("graph_renamed", p_old_name, p_new_name);
return true;
}

void OrchestratorScriptFunctionsComponentPanel::_handle_remove(TreeItem* p_item)
Expand Down
3 changes: 2 additions & 1 deletion src/editor/component_panels/functions_panel.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ class OrchestratorScriptFunctionsComponentPanel : public OrchestratorScriptCompo
bool _handle_add_new_item(const String& p_name) override;
void _handle_item_selected() override;
void _handle_item_activated(TreeItem* p_item) override;
bool _handle_item_renamed(const String& p_old_name, const String& p_new_name) override;
bool _can_be_renamed(const String& p_old_name, const String& p_new_name) override;
void _handle_item_renamed(const String& p_old_name, const String& p_new_name) override;
void _handle_remove(TreeItem* p_item) override;
void _handle_button_clicked(TreeItem* p_item, int p_column, int p_id, int p_mouse_button) override;
Dictionary _handle_drag_data(const Vector2& p_position) override;
Expand Down
7 changes: 5 additions & 2 deletions src/editor/component_panels/graphs_panel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -176,17 +176,20 @@ void OrchestratorScriptGraphsComponentPanel::_handle_item_activated(TreeItem* p_
_focus_graph_function(p_item);
}

bool OrchestratorScriptGraphsComponentPanel::_handle_item_renamed(const String& p_old_name, const String& p_new_name)
bool OrchestratorScriptGraphsComponentPanel::_can_be_renamed(const String& p_old_name, const String& p_new_name)
{
if (_get_existing_names().has(p_new_name))
{
_show_notification("A graph with the name '" + p_new_name + "' already exists.");
return false;
}
return true;
}

void OrchestratorScriptGraphsComponentPanel::_handle_item_renamed(const String& p_old_name, const String& p_new_name)
{
_orchestration->rename_graph(p_old_name, p_new_name);
emit_signal("graph_renamed", p_old_name, p_new_name);
return true;
}

void OrchestratorScriptGraphsComponentPanel::_handle_remove(TreeItem* p_item)
Expand Down
3 changes: 2 additions & 1 deletion src/editor/component_panels/graphs_panel.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ class OrchestratorScriptGraphsComponentPanel : public OrchestratorScriptComponen
void _handle_context_menu(int p_id) override;
bool _handle_add_new_item(const String& p_name) override;
void _handle_item_activated(TreeItem* p_item) override;
bool _handle_item_renamed(const String& p_old_name, const String& p_new_name) override;
bool _can_be_renamed(const String& p_old_name, const String& p_new_name) override;
void _handle_item_renamed(const String& p_old_name, const String& p_new_name) override;
void _handle_remove(TreeItem* p_item) override;
void _handle_button_clicked(TreeItem* p_item, int p_column, int p_id, int p_mouse_button) override;
//~ End OrchestratorScriptComponentPanel Interface
Expand Down
7 changes: 5 additions & 2 deletions src/editor/component_panels/signals_panel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,16 +82,19 @@ void OrchestratorScriptSignalsComponentPanel::_handle_item_activated(TreeItem* p
OrchestratorPlugin::get_singleton()->get_editor_interface()->edit_resource(signal);
}

bool OrchestratorScriptSignalsComponentPanel::_handle_item_renamed(const String& p_old_name, const String& p_new_name)
bool OrchestratorScriptSignalsComponentPanel::_can_be_renamed(const String& p_old_name, const String& p_new_name)
{
if (_get_existing_names().has(p_new_name))
{
_show_notification("A signal with the name '" + p_new_name + "' already exists.");
return false;
}
return true;
}

void OrchestratorScriptSignalsComponentPanel::_handle_item_renamed(const String& p_old_name, const String& p_new_name)
{
_orchestration->rename_custom_user_signal(p_old_name, p_new_name);
return true;
}

void OrchestratorScriptSignalsComponentPanel::_handle_remove(TreeItem* p_item)
Expand Down
3 changes: 2 additions & 1 deletion src/editor/component_panels/signals_panel.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ class OrchestratorScriptSignalsComponentPanel : public OrchestratorScriptCompone
bool _handle_add_new_item(const String& p_name) override;
void _handle_item_selected() override;
void _handle_item_activated(TreeItem* p_item) override;
bool _handle_item_renamed(const String& p_old_name, const String& p_new_name) override;
bool _can_be_renamed(const String& p_old_name, const String& p_new_name) override;
void _handle_item_renamed(const String& p_old_name, const String& p_new_name) override;
void _handle_remove(TreeItem* p_item) override;
Dictionary _handle_drag_data(const Vector2& p_position) override;
//~ End OrchestratorScriptViewSection Interface
Expand Down
14 changes: 11 additions & 3 deletions src/editor/component_panels/variables_panel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "editor/plugins/inspector_plugin_variable.h"
#include "editor/plugins/orchestrator_editor_plugin.h"

#include <godot_cpp/classes/editor_undo_redo_manager.hpp>
#include <godot_cpp/classes/popup_menu.hpp>
#include <godot_cpp/classes/tree.hpp>

Expand Down Expand Up @@ -144,16 +145,19 @@ void OrchestratorScriptVariablesComponentPanel::_handle_item_activated(TreeItem*
OrchestratorPlugin::get_singleton()->get_editor_interface()->edit_resource(variable);
}

bool OrchestratorScriptVariablesComponentPanel::_handle_item_renamed(const String& p_old_name, const String& p_new_name)
bool OrchestratorScriptVariablesComponentPanel::_can_be_renamed(const String& p_old_name, const String& p_new_name)
{
if (_get_existing_names().has(p_new_name))
{
_show_notification("A variable with the name '" + p_new_name + "' already exists.");
return false;
}
return true;
}

void OrchestratorScriptVariablesComponentPanel::_handle_item_renamed(const String& p_old_name, const String& p_new_name)
{
_orchestration->rename_variable(p_old_name, p_new_name);
return true;
}

void OrchestratorScriptVariablesComponentPanel::_handle_remove(TreeItem* p_item)
Expand Down Expand Up @@ -183,7 +187,11 @@ void OrchestratorScriptVariablesComponentPanel::_handle_button_clicked(TreeItem*
else if (p_column == 0 && p_id == 3)
{
// Visibility changed on variable
variable->set_exported(!variable->is_exported());
EditorUndoRedoManager* undo = OrchestratorPlugin::get_singleton()->get_undo_redo();
undo->create_action("Toggle Variable Visibility");
undo->add_do_method(variable.ptr(), "set_exported", !variable->is_exported());
undo->add_undo_method(variable.ptr(), "set_exported", variable->is_exported());
undo->commit_action();
update();
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/editor/component_panels/variables_panel.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ class OrchestratorScriptVariablesComponentPanel : public OrchestratorScriptCompo
bool _handle_add_new_item(const String& p_name) override;
void _handle_item_selected() override;
void _handle_item_activated(TreeItem* p_item) override;
bool _handle_item_renamed(const String& p_old_name, const String& p_new_name) override;
bool _can_be_renamed(const String& p_old_name, const String& p_new_name) override;
void _handle_item_renamed(const String& p_old_name, const String& p_new_name) override;
void _handle_remove(TreeItem* p_item) override;
void _handle_button_clicked(TreeItem* p_item, int p_column, int p_id, int p_mouse_button) override;
Dictionary _handle_drag_data(const Vector2& p_position) override;
Expand Down
81 changes: 51 additions & 30 deletions src/editor/graph/graph_edit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
#include <godot_cpp/classes/confirmation_dialog.hpp>
#include <godot_cpp/classes/display_server.hpp>
#include <godot_cpp/classes/editor_inspector.hpp>
#include <godot_cpp/classes/editor_undo_redo_manager.hpp>
#include <godot_cpp/classes/geometry2d.hpp>
#include <godot_cpp/classes/input.hpp>
#include <godot_cpp/classes/input_event_action.hpp>
Expand Down Expand Up @@ -280,13 +281,51 @@ void OrchestratorGraphEdit::_bind_methods()
{
ClassDB::bind_method(D_METHOD("_synchronize_child_order"), &OrchestratorGraphEdit::_synchronize_child_order);

// Needed for undo/redo
ClassDB::bind_method(D_METHOD("_link", "source", "source_port", "target", "target_port"), &OrchestratorGraphEdit::_link);
ClassDB::bind_method(D_METHOD("_unlink", "source", "source_port", "target", "target_port"), &OrchestratorGraphEdit::_unlink);

ADD_SIGNAL(MethodInfo("nodes_changed"));
ADD_SIGNAL(MethodInfo("focus_requested", PropertyInfo(Variant::OBJECT, "target")));
ADD_SIGNAL(MethodInfo("collapse_selected_to_function"));
ADD_SIGNAL(MethodInfo("expand_node", PropertyInfo(Variant::INT, "node_id")));
ADD_SIGNAL(MethodInfo("validation_requested"));
}

void OrchestratorGraphEdit::_link(const StringName& p_source, int p_source_port, const StringName& p_target, int p_target_port)
{
if (OrchestratorGraphNode* source = _get_by_name<OrchestratorGraphNode>(p_source))
{
if (OrchestratorGraphNode* target = _get_by_name<OrchestratorGraphNode>(p_target))
{
OrchestratorGraphNodePin* source_pin = source->get_output_pin(p_source_port);
OrchestratorGraphNodePin* target_pin = target->get_input_pin(p_target_port);
if (!source_pin || !target_pin)
return;

// Connect the two pins
source_pin->link(target_pin);
}
}
}

void OrchestratorGraphEdit::_unlink(const StringName& p_source, int p_source_port, const StringName& p_target, int p_target_port)
{
if (OrchestratorGraphNode* source = _get_by_name<OrchestratorGraphNode>(p_source))
{
if (OrchestratorGraphNode* target = _get_by_name<OrchestratorGraphNode>(p_target))
{
OrchestratorGraphNodePin* source_pin = source->get_output_pin(p_source_port);
OrchestratorGraphNodePin* target_pin = target->get_input_pin(p_target_port);
if (!source_pin || !target_pin)
return;

// Disconnect the two pins
source_pin->unlink(target_pin);
}
}
}

void OrchestratorGraphEdit::clear_selection()
{
set_selected(nullptr);
Expand Down Expand Up @@ -1292,42 +1331,24 @@ void OrchestratorGraphEdit::_on_action_menu_action_selected(OrchestratorGraphAct
p_handler->execute(this, _saved_mouse_position);
}

void OrchestratorGraphEdit::_on_connection(const StringName& p_from_node, int p_from_port, const StringName& p_to_node,
int p_to_port)
void OrchestratorGraphEdit::_on_connection(const StringName& p_from_node, int p_from_port, const StringName& p_to_node, int p_to_port)
{
_drag_context.reset();

if (OrchestratorGraphNode* source = _get_by_name<OrchestratorGraphNode>(p_from_node))
{
if (OrchestratorGraphNode* target = _get_by_name<OrchestratorGraphNode>(p_to_node))
{
OrchestratorGraphNodePin* source_pin = source->get_output_pin(p_from_port);
OrchestratorGraphNodePin* target_pin = target->get_input_pin(p_to_port);
if (!source_pin || !target_pin)
return;

// Connect the two pins
source_pin->link(target_pin);
}
}
EditorUndoRedoManager* undo = OrchestratorPlugin::get_singleton()->get_undo_redo();
undo->create_action("Orchestration: Connect nodes");
undo->add_do_method(this, "_link", p_from_node, p_from_port, p_to_node, p_to_port);
undo->add_undo_method(this, "_unlink", p_from_node, p_from_port, p_to_node, p_to_port);
undo->commit_action();
}

void OrchestratorGraphEdit::_on_disconnection(const StringName& p_from_node, int p_from_port, const StringName& p_to_node,
int p_to_port)
void OrchestratorGraphEdit::_on_disconnection(const StringName& p_from_node, int p_from_port, const StringName& p_to_node, int p_to_port)
{
if (OrchestratorGraphNode* source = _get_by_name<OrchestratorGraphNode>(p_from_node))
{
if (OrchestratorGraphNode* target = _get_by_name<OrchestratorGraphNode>(p_to_node))
{
OrchestratorGraphNodePin* source_pin = source->get_output_pin(p_from_port);
OrchestratorGraphNodePin* target_pin = target->get_input_pin(p_to_port);
if (!source_pin || !target_pin)
return;

// Disconnect the two pins
source_pin->unlink(target_pin);
}
}
EditorUndoRedoManager* undo = OrchestratorPlugin::get_singleton()->get_undo_redo();
undo->create_action("Orchestration: Disconnect nodes");
undo->add_do_method(this, "_unlink", p_from_node, p_from_port, p_to_node, p_to_port);
undo->add_undo_method(this, "_link", p_from_node, p_from_port, p_to_node, p_to_port);
undo->commit_action();
}

void OrchestratorGraphEdit::_on_attempt_connection_from_empty(const StringName& p_to_node, int p_to_port, const Vector2& p_position)
Expand Down
3 changes: 3 additions & 0 deletions src/editor/graph/graph_edit.h
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,9 @@ class OrchestratorGraphEdit : public GraphEdit
protected:
static void _bind_methods();

void _link(const StringName& p_source, int p_source_port, const StringName& p_target, int p_target_port);
void _unlink(const StringName& p_source, int p_source_port, const StringName& p_target, int p_target_port);

public:
// The OrchestratorGraphEdit maintains a static clipboard so that data can be shared across different graph
// instances easily in the tab view, and so these methods are called by the MainView during the
Expand Down
Loading

0 comments on commit ed4648e

Please sign in to comment.