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

GH-58 Initial support for undo/redo #526

Open
wants to merge 1 commit into
base: main
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
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
Loading