From ed4648e0f18bfef6c6f83965baceb07618eb1974 Mon Sep 17 00:00:00 2001 From: Chris Cranford Date: Sun, 14 Jul 2024 11:39:31 -0400 Subject: [PATCH] GH-58 Initial support for undo/redo * 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 --- .../component_panels/component_panel.cpp | 30 ++++++- src/editor/component_panels/component_panel.h | 17 +++- .../component_panels/functions_panel.cpp | 8 +- src/editor/component_panels/functions_panel.h | 3 +- src/editor/component_panels/graphs_panel.cpp | 7 +- src/editor/component_panels/graphs_panel.h | 3 +- src/editor/component_panels/signals_panel.cpp | 7 +- src/editor/component_panels/signals_panel.h | 3 +- .../component_panels/variables_panel.cpp | 14 ++- src/editor/component_panels/variables_panel.h | 3 +- src/editor/graph/graph_edit.cpp | 81 ++++++++++------- src/editor/graph/graph_edit.h | 3 + src/editor/graph/graph_node.cpp | 13 ++- src/editor/graph/graph_node.h | 2 +- .../graph/pins/graph_node_pin_bitfield.cpp | 13 ++- src/editor/graph/pins/graph_node_pin_bool.cpp | 25 ++++-- src/editor/graph/pins/graph_node_pin_bool.h | 4 + .../graph/pins/graph_node_pin_color.cpp | 26 ++++-- src/editor/graph/pins/graph_node_pin_color.h | 4 + src/editor/graph/pins/graph_node_pin_enum.cpp | 39 +++++++-- src/editor/graph/pins/graph_node_pin_enum.h | 4 +- src/editor/graph/pins/graph_node_pin_file.cpp | 46 ++++++---- src/editor/graph/pins/graph_node_pin_file.h | 15 ++-- .../pins/graph_node_pin_input_action.cpp | 24 ++++- .../graph/pins/graph_node_pin_node_path.cpp | 21 ++++- .../graph/pins/graph_node_pin_numeric.cpp | 57 ++++++++---- .../graph/pins/graph_node_pin_numeric.h | 13 ++- .../graph/pins/graph_node_pin_string.cpp | 87 +++++++++++-------- src/editor/graph/pins/graph_node_pin_string.h | 19 ++-- .../graph/pins/graph_node_pin_struct.cpp | 30 ++++++- src/editor/graph/pins/graph_node_pin_struct.h | 4 + src/script/node.cpp | 6 +- src/script/node_pin.cpp | 6 +- 33 files changed, 449 insertions(+), 188 deletions(-) diff --git a/src/editor/component_panels/component_panel.cpp b/src/editor/component_panels/component_panel.cpp index aa185db0..f3dc8d21 100644 --- a/src/editor/component_panels/component_panel.cpp +++ b/src/editor/component_panels/component_panel.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -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) @@ -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(); } } @@ -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(); @@ -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"))); } diff --git a/src/editor/component_panels/component_panel.h b/src/editor/component_panels/component_panel.h index 7820ead5..b6f3d9fa 100644 --- a/src/editor/component_panels/component_panel.h +++ b/src/editor/component_panels/component_panel.h @@ -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"; } @@ -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 diff --git a/src/editor/component_panels/functions_panel.cpp b/src/editor/component_panels/functions_panel.cpp index 8841b51a..e67ab714 100644 --- a/src/editor/component_panels/functions_panel.cpp +++ b/src/editor/component_panels/functions_panel.cpp @@ -152,7 +152,7 @@ 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)) { @@ -160,9 +160,13 @@ bool OrchestratorScriptFunctionsComponentPanel::_handle_item_renamed(const Strin 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) diff --git a/src/editor/component_panels/functions_panel.h b/src/editor/component_panels/functions_panel.h index 79a87d77..c07c1e0b 100644 --- a/src/editor/component_panels/functions_panel.h +++ b/src/editor/component_panels/functions_panel.h @@ -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; diff --git a/src/editor/component_panels/graphs_panel.cpp b/src/editor/component_panels/graphs_panel.cpp index be451dc0..eb5156a0 100644 --- a/src/editor/component_panels/graphs_panel.cpp +++ b/src/editor/component_panels/graphs_panel.cpp @@ -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) diff --git a/src/editor/component_panels/graphs_panel.h b/src/editor/component_panels/graphs_panel.h index 4a642d6c..3cc93d08 100644 --- a/src/editor/component_panels/graphs_panel.h +++ b/src/editor/component_panels/graphs_panel.h @@ -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 diff --git a/src/editor/component_panels/signals_panel.cpp b/src/editor/component_panels/signals_panel.cpp index 80f7d4ac..f9fb8aec 100644 --- a/src/editor/component_panels/signals_panel.cpp +++ b/src/editor/component_panels/signals_panel.cpp @@ -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) diff --git a/src/editor/component_panels/signals_panel.h b/src/editor/component_panels/signals_panel.h index a7d920c2..e11981f3 100644 --- a/src/editor/component_panels/signals_panel.h +++ b/src/editor/component_panels/signals_panel.h @@ -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 diff --git a/src/editor/component_panels/variables_panel.cpp b/src/editor/component_panels/variables_panel.cpp index f41fbf82..a67dcf2f 100644 --- a/src/editor/component_panels/variables_panel.cpp +++ b/src/editor/component_panels/variables_panel.cpp @@ -22,6 +22,7 @@ #include "editor/plugins/inspector_plugin_variable.h" #include "editor/plugins/orchestrator_editor_plugin.h" +#include #include #include @@ -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) @@ -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(); } } diff --git a/src/editor/component_panels/variables_panel.h b/src/editor/component_panels/variables_panel.h index f876a873..8dff1412 100644 --- a/src/editor/component_panels/variables_panel.h +++ b/src/editor/component_panels/variables_panel.h @@ -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; diff --git a/src/editor/graph/graph_edit.cpp b/src/editor/graph/graph_edit.cpp index 9451a1b9..0f83a8c6 100644 --- a/src/editor/graph/graph_edit.cpp +++ b/src/editor/graph/graph_edit.cpp @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -280,6 +281,10 @@ 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")); @@ -287,6 +292,40 @@ void OrchestratorGraphEdit::_bind_methods() 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(p_source)) + { + if (OrchestratorGraphNode* target = _get_by_name(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(p_source)) + { + if (OrchestratorGraphNode* target = _get_by_name(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); @@ -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(p_from_node)) - { - if (OrchestratorGraphNode* target = _get_by_name(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(p_from_node)) - { - if (OrchestratorGraphNode* target = _get_by_name(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) diff --git a/src/editor/graph/graph_edit.h b/src/editor/graph/graph_edit.h index 60445b0c..7d8c9bad 100644 --- a/src/editor/graph/graph_edit.h +++ b/src/editor/graph/graph_edit.h @@ -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 diff --git a/src/editor/graph/graph_node.cpp b/src/editor/graph/graph_node.cpp index 1b54393b..8e582377 100644 --- a/src/editor/graph/graph_node.cpp +++ b/src/editor/graph/graph_node.cpp @@ -16,6 +16,7 @@ // #include "graph_node.h" +#include "common/callable_lambda.h" #include "common/logger.h" #include "common/macros.h" #include "common/scene_utils.h" @@ -30,6 +31,7 @@ #include #include +#include #include #include #include @@ -371,6 +373,9 @@ void OrchestratorGraphNode::_update_node_attributes() if (_resize_on_update()) call_deferred("set_size", Vector2()); + if (!get_position_offset().is_equal_approx(_node->get_position())) + set_position_offset(_node->get_position()); + // Some pin changes may affect the titlebar // We explicitly update the title here on change to capture that possibility _update_titlebar(); @@ -632,9 +637,13 @@ int32_t OrchestratorGraphNode::get_port_at_position(const Vector2& p_position, E return -1; } -void OrchestratorGraphNode::_on_node_moved([[maybe_unused]] Vector2 p_old_pos, Vector2 p_new_pos) +void OrchestratorGraphNode::_on_node_moved(const Vector2 p_old_pos, const Vector2 p_new_pos) { - _node->set_position(p_new_pos); + EditorUndoRedoManager* undo = OrchestratorPlugin::get_singleton()->get_undo_redo(); + undo->create_action("Orchestration: Move nodes", UndoRedo::MERGE_ALL); + undo->add_do_method(_node.ptr(), "set_position", p_new_pos); + undo->add_undo_method(_node.ptr(), "set_position", p_old_pos); + undo->commit_action(); } void OrchestratorGraphNode::_on_node_resized() diff --git a/src/editor/graph/graph_node.h b/src/editor/graph/graph_node.h index 589a30ca..8a29f343 100644 --- a/src/editor/graph/graph_node.h +++ b/src/editor/graph/graph_node.h @@ -220,7 +220,7 @@ class OrchestratorGraphNode : public GraphNode /// Called when the graph node is moved /// @param p_old_pos old position /// @param p_new_pos new position - void _on_node_moved(Vector2 p_old_pos, Vector2 p_new_pos); + void _on_node_moved(const Vector2 p_old_pos, const Vector2 p_new_pos); /// Called when the graph node is resized void _on_node_resized(); diff --git a/src/editor/graph/pins/graph_node_pin_bitfield.cpp b/src/editor/graph/pins/graph_node_pin_bitfield.cpp index 9fcb6e72..55e07c5e 100644 --- a/src/editor/graph/pins/graph_node_pin_bitfield.cpp +++ b/src/editor/graph/pins/graph_node_pin_bitfield.cpp @@ -19,9 +19,11 @@ #include "api/extension_db.h" #include "common/scene_utils.h" #include "common/string_utils.h" +#include "editor/plugins/orchestrator_editor_plugin.h" #include #include +#include #include #include #include @@ -35,6 +37,8 @@ OrchestratorGraphNodePinBitField::OrchestratorGraphNodePinBitField(OrchestratorG void OrchestratorGraphNodePinBitField::_bind_methods() { + // Needed for undo/redo + ClassDB::bind_method(D_METHOD("_update_button_value"), &OrchestratorGraphNodePinBitField::_update_button_value); } String OrchestratorGraphNodePinBitField::_get_enum_prefix(const PackedStringArray& p_values) @@ -121,8 +125,13 @@ void OrchestratorGraphNodePinBitField::_on_bit_toggle(bool p_state, int64_t p_en else current_value &= ~p_enum_value; - _pin->set_default_value(current_value); - _update_button_value(); + EditorUndoRedoManager* undo = OrchestratorPlugin::get_singleton()->get_undo_redo(); + undo->create_action("Orchestration: Change bitfield pin", UndoRedo::MERGE_ENDS); + undo->add_do_method(_pin.ptr(), "set_default_value", current_value); + undo->add_do_method(this, "_update_button_value"); + undo->add_undo_method(_pin.ptr(), "set_default_value", _pin->get_effective_default_value()); + undo->add_undo_method(this, "_update_button_value"); + undo->commit_action(); } void OrchestratorGraphNodePinBitField::_on_hide_flags(PopupPanel* p_panel) diff --git a/src/editor/graph/pins/graph_node_pin_bool.cpp b/src/editor/graph/pins/graph_node_pin_bool.cpp index 7e1439bb..e0595276 100644 --- a/src/editor/graph/pins/graph_node_pin_bool.cpp +++ b/src/editor/graph/pins/graph_node_pin_bool.cpp @@ -16,7 +16,10 @@ // #include "graph_node_pin_bool.h" -#include +#include "common/callable_lambda.h" +#include "editor/plugins/orchestrator_editor_plugin.h" + +#include OrchestratorGraphNodePinBool::OrchestratorGraphNodePinBool(OrchestratorGraphNode* p_node, const Ref& p_pin) : OrchestratorGraphNodePin(p_node, p_pin) @@ -29,15 +32,21 @@ void OrchestratorGraphNodePinBool::_bind_methods() void OrchestratorGraphNodePinBool::_on_default_value_changed(bool p_new_value) { - _pin->set_default_value(p_new_value); + EditorUndoRedoManager* undo = OrchestratorPlugin::get_singleton()->get_undo_redo(); + undo->create_action("Orchestration: Change boolean pin"); + undo->add_do_method(_pin.ptr(), "set_default_value", p_new_value); + undo->add_do_method(_check_box, "set_pressed_no_signal", p_new_value); + undo->add_undo_method(_pin.ptr(), "set_default_value", _pin->get_default_value()); + undo->add_undo_method(_check_box, "set_pressed_no_signal", _pin->get_default_value()); + undo->commit_action(); } Control* OrchestratorGraphNodePinBool::_get_default_value_widget() { - CheckBox* check_box = memnew(CheckBox); - check_box->set_focus_mode(FOCUS_NONE); - check_box->set_h_size_flags(SIZE_EXPAND_FILL); - check_box->set_pressed(_pin->get_default_value()); - check_box->connect("toggled", callable_mp(this, &OrchestratorGraphNodePinBool::_on_default_value_changed)); - return check_box; + _check_box = memnew(CheckBox); + _check_box->set_focus_mode(FOCUS_NONE); + _check_box->set_h_size_flags(SIZE_EXPAND_FILL); + _check_box->set_pressed(_pin->get_default_value()); + _check_box->connect("toggled", callable_mp(this, &OrchestratorGraphNodePinBool::_on_default_value_changed)); + return _check_box; } diff --git a/src/editor/graph/pins/graph_node_pin_bool.h b/src/editor/graph/pins/graph_node_pin_bool.h index 96387cdc..8b32d7bf 100644 --- a/src/editor/graph/pins/graph_node_pin_bool.h +++ b/src/editor/graph/pins/graph_node_pin_bool.h @@ -19,6 +19,8 @@ #include "editor/graph/graph_node_pin.h" +#include + /// An implementation of OrchestratorGraphNodePin for boolean pin types that provides a check-box /// to represent the default value associated with the pin. class OrchestratorGraphNodePinBool : public OrchestratorGraphNodePin @@ -28,6 +30,8 @@ class OrchestratorGraphNodePinBool : public OrchestratorGraphNodePin static void _bind_methods(); protected: + CheckBox* _check_box{ nullptr }; + OrchestratorGraphNodePinBool() = default; /// Called when the default value is changed in the UI. diff --git a/src/editor/graph/pins/graph_node_pin_color.cpp b/src/editor/graph/pins/graph_node_pin_color.cpp index 7e3d83ab..768f5238 100644 --- a/src/editor/graph/pins/graph_node_pin_color.cpp +++ b/src/editor/graph/pins/graph_node_pin_color.cpp @@ -16,7 +16,9 @@ // #include "graph_node_pin_color.h" -#include +#include "editor/plugins/orchestrator_editor_plugin.h" + +#include OrchestratorGraphNodePinColor::OrchestratorGraphNodePinColor(OrchestratorGraphNode* p_node, const Ref& p_pin) : OrchestratorGraphNodePin(p_node, p_pin) @@ -29,16 +31,22 @@ void OrchestratorGraphNodePinColor::_bind_methods() void OrchestratorGraphNodePinColor::_on_default_value_changed(const Color& p_new_value) { - _pin->set_default_value(p_new_value); + EditorUndoRedoManager* undo = OrchestratorPlugin::get_singleton()->get_undo_redo(); + undo->create_action("Orchestration: Change color pin", UndoRedo::MERGE_ENDS); + undo->add_do_method(_pin.ptr(), "set_default_value", p_new_value); + undo->add_do_method(_button, "set_pick_color", p_new_value); + undo->add_undo_method(_pin.ptr(), "set_default_value", _pin->get_default_value()); + undo->add_undo_method(_button, "set_pick_color", _pin->get_default_value()); + undo->commit_action(); } Control* OrchestratorGraphNodePinColor::_get_default_value_widget() { - ColorPickerButton* button = memnew(ColorPickerButton); - button->set_focus_mode(Control::FOCUS_NONE); - button->set_h_size_flags(Control::SIZE_EXPAND); - button->set_custom_minimum_size(Vector2(24, 24)); - button->set_pick_color(_pin->get_default_value()); - button->connect("color_changed", callable_mp(this, &OrchestratorGraphNodePinColor::_on_default_value_changed)); - return button; + _button = memnew(ColorPickerButton); + _button->set_focus_mode(Control::FOCUS_NONE); + _button->set_h_size_flags(Control::SIZE_EXPAND); + _button->set_custom_minimum_size(Vector2(24, 24)); + _button->set_pick_color(_pin->get_default_value()); + _button->connect("color_changed", callable_mp(this, &OrchestratorGraphNodePinColor::_on_default_value_changed)); + return _button; } diff --git a/src/editor/graph/pins/graph_node_pin_color.h b/src/editor/graph/pins/graph_node_pin_color.h index eb0e4a82..0f7d5155 100644 --- a/src/editor/graph/pins/graph_node_pin_color.h +++ b/src/editor/graph/pins/graph_node_pin_color.h @@ -19,6 +19,8 @@ #include "editor/graph/graph_node_pin.h" +#include + /// An implementation of OrchestratorGraphNodePin for color types, offering a color picker button /// that opens a color dialog that the user can interact with. class OrchestratorGraphNodePinColor : public OrchestratorGraphNodePin @@ -28,6 +30,8 @@ class OrchestratorGraphNodePinColor : public OrchestratorGraphNodePin static void _bind_methods(); protected: + ColorPickerButton* _button{ nullptr }; + OrchestratorGraphNodePinColor() = default; /// Called when the default value is changed in the UI. diff --git a/src/editor/graph/pins/graph_node_pin_enum.cpp b/src/editor/graph/pins/graph_node_pin_enum.cpp index 91af2610..b44ce1b7 100644 --- a/src/editor/graph/pins/graph_node_pin_enum.cpp +++ b/src/editor/graph/pins/graph_node_pin_enum.cpp @@ -18,7 +18,9 @@ #include "api/extension_db.h" #include "common/string_utils.h" +#include "editor/plugins/orchestrator_editor_plugin.h" +#include #include #include @@ -31,15 +33,36 @@ void OrchestratorGraphNodePinEnum::_bind_methods() { } -void OrchestratorGraphNodePinEnum::_on_item_selected(int p_index, OptionButton* p_button) +void OrchestratorGraphNodePinEnum::_on_item_selected(int p_index) { if (p_index >= 0 && p_index < _items.size()) { const ListItem& item = _items[p_index]; - _pin->set_default_value(item.value); + if (uint64_t(_pin->get_effective_default_value()) != item.value) + { + int selected_index = 0; + + uint64_t default_value = _pin->get_effective_default_value(); + for (int index = 0; index < _items.size(); ++index) + { + if (_items[index].value == default_value) + { + selected_index = index; + break; + } + } + + EditorUndoRedoManager* undo = OrchestratorPlugin::get_singleton()->get_undo_redo(); + undo->create_action("Orchestration: Change enum pin"); + undo->add_do_method(_pin.ptr(), "set_default_value", item.value); + undo->add_do_method(_button, "select", p_index); + undo->add_undo_method(_pin.ptr(), "set_default_value", _pin->get_effective_default_value()); + undo->add_undo_method(_button, "select", selected_index); + undo->commit_action(); + } } - p_button->release_focus(); + _button->release_focus(); } void OrchestratorGraphNodePinEnum::_generate_items() @@ -156,18 +179,18 @@ String OrchestratorGraphNodePinEnum::_generate_friendly_name(const String& p_pre Control* OrchestratorGraphNodePinEnum::_get_default_value_widget() { - OptionButton* button = memnew(OptionButton); - button->connect("item_selected", callable_mp(this, &OrchestratorGraphNodePinEnum::_on_item_selected).bind(button)); + _button = memnew(OptionButton); + _button->connect("item_selected", callable_mp(this, &OrchestratorGraphNodePinEnum::_on_item_selected)); _generate_items(); int effective_default = _pin->get_effective_default_value(); for (const ListItem& item : _items) { - button->add_item(item.friendly_name); + _button->add_item(item.friendly_name); if (effective_default == item.value) - button->select(button->get_item_count() - 1); + _button->select(_button->get_item_count() - 1); } - return button; + return _button; } \ No newline at end of file diff --git a/src/editor/graph/pins/graph_node_pin_enum.h b/src/editor/graph/pins/graph_node_pin_enum.h index e6a8fc91..aa85a95d 100644 --- a/src/editor/graph/pins/graph_node_pin_enum.h +++ b/src/editor/graph/pins/graph_node_pin_enum.h @@ -42,12 +42,12 @@ class OrchestratorGraphNodePinEnum : public OrchestratorGraphNodePin }; protected: + OptionButton* _button{ nullptr }; List _items; //! All the items that are in the drop-down list /// Dispatched when the user makes a selection. /// @param p_index the choice index that was selected - /// @param p_button the button widget - void _on_item_selected(int p_index, OptionButton* p_button); + void _on_item_selected(int p_index); /// Generate the list of items for the drop-down void _generate_items(); diff --git a/src/editor/graph/pins/graph_node_pin_file.cpp b/src/editor/graph/pins/graph_node_pin_file.cpp index 00e774b4..61fcd643 100644 --- a/src/editor/graph/pins/graph_node_pin_file.cpp +++ b/src/editor/graph/pins/graph_node_pin_file.cpp @@ -19,9 +19,11 @@ #include "common/scene_utils.h" #include "common/string_utils.h" #include "editor/file_dialog.h" +#include "editor/plugins/orchestrator_editor_plugin.h" #include "script/nodes/script_nodes.h" #include +#include OrchestratorGraphNodePinFile::OrchestratorGraphNodePinFile(OrchestratorGraphNode* p_node, const Ref& p_pin) : OrchestratorGraphNodePin(p_node, p_pin) @@ -40,14 +42,14 @@ String OrchestratorGraphNodePinFile::_get_default_text() const return "Assign..."; } -void OrchestratorGraphNodePinFile::_on_clear_file(Button* p_button) +void OrchestratorGraphNodePinFile::_on_clear_file() { _pin->set_default_value(""); - p_button->set_text(_get_default_text()); + _file_button->set_text(_get_default_text()); _clear_button->set_visible(false); } -void OrchestratorGraphNodePinFile::_on_show_file_dialog(Button* p_button) +void OrchestratorGraphNodePinFile::_on_show_file_dialog() { OrchestratorFileDialog* dialog = memnew(OrchestratorFileDialog); dialog->set_file_mode(FileDialog::FILE_MODE_OPEN_FILE); @@ -60,21 +62,29 @@ void OrchestratorGraphNodePinFile::_on_show_file_dialog(Button* p_button) add_child(dialog); - dialog->connect("file_selected", callable_mp(this, &OrchestratorGraphNodePinFile::_on_file_selected).bind(dialog, p_button)); - dialog->connect("canceled", callable_mp(this, &OrchestratorGraphNodePinFile::_on_file_canceled).bind(dialog, p_button)); + dialog->connect("file_selected", callable_mp(this, &OrchestratorGraphNodePinFile::_on_file_selected).bind(dialog)); + dialog->connect("canceled", callable_mp(this, &OrchestratorGraphNodePinFile::_on_file_canceled).bind(dialog)); dialog->popup_file_dialog(); } -void OrchestratorGraphNodePinFile::_on_file_selected(const String& p_file_name, FileDialog* p_dialog, Button* p_button) +void OrchestratorGraphNodePinFile::_on_file_selected(const String& p_file_name, FileDialog* p_dialog) { - p_button->set_text(p_file_name); - _pin->set_default_value(p_file_name); - _clear_button->set_visible(p_button->get_text() != _get_default_text()); + EditorUndoRedoManager* undo = OrchestratorPlugin::get_singleton()->get_undo_redo(); + undo->create_action("Orchestration: Change file pin"); + undo->add_do_method(_pin.ptr(), "set_default_value", p_file_name); + undo->add_do_method(_file_button, "set_text", p_file_name); + undo->add_do_method(_clear_button, "set_visible", p_file_name != _get_default_text()); + + const String value = _pin->get_effective_default_value(); + undo->add_undo_method(_pin.ptr(), "set_default_value", value); + undo->add_undo_method(_file_button, "set_text", value.is_empty() ? _get_default_text() : value); + undo->add_undo_method(_clear_button, "set_visible", !value.is_empty()); + undo->commit_action(); p_dialog->queue_free(); } -void OrchestratorGraphNodePinFile::_on_file_canceled(FileDialog* p_dialog, Button* p_button) +void OrchestratorGraphNodePinFile::_on_file_canceled(FileDialog* p_dialog) { p_dialog->queue_free(); } @@ -84,18 +94,18 @@ Control* OrchestratorGraphNodePinFile::_get_default_value_widget() HBoxContainer* container = memnew(HBoxContainer); container->add_theme_constant_override("separation", 1); - Button* file_button = memnew(Button); - file_button->set_custom_minimum_size(Vector2(28, 0)); - file_button->set_focus_mode(Control::FOCUS_NONE); - file_button->set_text(StringUtils::default_if_empty(_pin->get_effective_default_value(), _get_default_text())); - file_button->connect("pressed", callable_mp(this, &OrchestratorGraphNodePinFile::_on_show_file_dialog).bind(file_button)); - container->add_child(file_button); + _file_button = memnew(Button); + _file_button->set_custom_minimum_size(Vector2(28, 0)); + _file_button->set_focus_mode(Control::FOCUS_NONE); + _file_button->set_text(StringUtils::default_if_empty(_pin->get_effective_default_value(), _get_default_text())); + _file_button->connect("pressed", callable_mp(this, &OrchestratorGraphNodePinFile::_on_show_file_dialog)); + container->add_child(_file_button); _clear_button = memnew(Button); _clear_button->set_focus_mode(Control::FOCUS_NONE); _clear_button->set_button_icon(SceneUtils::get_editor_icon("Reload")); - _clear_button->connect("pressed", callable_mp(this, &OrchestratorGraphNodePinFile::_on_clear_file).bind(file_button)); - _clear_button->set_visible(file_button->get_text() != _get_default_text()); + _clear_button->connect("pressed", callable_mp(this, &OrchestratorGraphNodePinFile::_on_clear_file)); + _clear_button->set_visible(_file_button->get_text() != _get_default_text()); container->add_child(_clear_button); return container; diff --git a/src/editor/graph/pins/graph_node_pin_file.h b/src/editor/graph/pins/graph_node_pin_file.h index 67e43fb9..9519cc72 100644 --- a/src/editor/graph/pins/graph_node_pin_file.h +++ b/src/editor/graph/pins/graph_node_pin_file.h @@ -36,7 +36,8 @@ class OrchestratorGraphNodePinFile : public OrchestratorGraphNodePin static void _bind_methods(); protected: - Button* _clear_button; + Button* _file_button{ nullptr }; + Button* _clear_button{ nullptr }; OrchestratorGraphNodePinFile() = default; @@ -48,23 +49,19 @@ class OrchestratorGraphNodePinFile : public OrchestratorGraphNodePin String _get_default_text() const; /// Dispatched when the clear button is clicked - /// @param p_button the button control, should not be null - void _on_clear_file(Button* p_button); + void _on_clear_file(); /// Dispatched when the button is clicked - /// @param p_button the button control, should not be null - void _on_show_file_dialog(Button* p_button); + void _on_show_file_dialog(); /// Dispatched when a file is selected /// @param p_file_name the selected file /// @param p_dialog the file dialog, should not be null - /// @param p_button the button control, should not be null - void _on_file_selected(const String& p_file_name, FileDialog* p_dialog, Button* p_button); + void _on_file_selected(const String& p_file_name, FileDialog* p_dialog); /// Dispatched when the file dialog window is closed or cancelled /// @param p_dialog the file dialog, should not be null - /// @param p_button the button control, should not be null - void _on_file_canceled(FileDialog* p_dialog, Button* p_button); + void _on_file_canceled(FileDialog* p_dialog); public: OrchestratorGraphNodePinFile(OrchestratorGraphNode* p_node, const Ref& p_pin); diff --git a/src/editor/graph/pins/graph_node_pin_input_action.cpp b/src/editor/graph/pins/graph_node_pin_input_action.cpp index 6b2e3c15..c46f38c0 100644 --- a/src/editor/graph/pins/graph_node_pin_input_action.cpp +++ b/src/editor/graph/pins/graph_node_pin_input_action.cpp @@ -17,8 +17,10 @@ #include "graph_node_pin_input_action.h" #include "common/callable_lambda.h" +#include "editor/plugins/orchestrator_editor_plugin.h" #include +#include #include #include #include @@ -74,7 +76,27 @@ Control* OrchestratorGraphNodePinInputAction::_get_default_value_widget() _button->connect("item_selected", callable_mp_lambda(this, [&](int index) { const String action_name = _button->get_item_text(index); - _pin->set_default_value(action_name); + if (_pin->get_effective_default_value() != action_name) + { + int selected_index = 0; + for (int i = 0; i < _button->get_item_count(); ++i) + { + const String button_item = _button->get_item_text(i); + if (_pin->get_effective_default_value() == button_item) + { + selected_index = i; + break; + } + } + + EditorUndoRedoManager* undo = OrchestratorPlugin::get_singleton()->get_undo_redo(); + undo->create_action("Orchestration: Change input action pin"); + undo->add_do_method(_pin.ptr(), "set_default_value", action_name); + undo->add_do_method(_button, "select", index); + undo->add_undo_method(_pin.ptr(), "set_default_value", _pin->get_effective_default_value()); + undo->add_undo_method(_button, "select", selected_index); + undo->commit_action(); + } _button->release_focus(); })); diff --git a/src/editor/graph/pins/graph_node_pin_node_path.cpp b/src/editor/graph/pins/graph_node_pin_node_path.cpp index 0780e4a5..41db611c 100644 --- a/src/editor/graph/pins/graph_node_pin_node_path.cpp +++ b/src/editor/graph/pins/graph_node_pin_node_path.cpp @@ -27,6 +27,7 @@ #include "script/script.h" #include +#include #include #include @@ -185,14 +186,26 @@ void OrchestratorGraphNodePinNodePath::_show_property_dialog_for_object(Object* void OrchestratorGraphNodePinNodePath::_reset() { - _set_pin_value(Variant()); + _set_pin_value(""); } void OrchestratorGraphNodePinNodePath::_set_pin_value(const Variant& p_pin_value) { - _pin->set_default_value(p_pin_value); - _button->set_text(StringUtils::default_if_empty(_pin->get_effective_default_value(), DEFAULT_TEXT)); - _reset_button->set_visible(!_button->get_text().match(DEFAULT_TEXT)); + if (_pin->get_effective_default_value() != p_pin_value) + { + const String new_button_text = StringUtils::default_if_empty(p_pin_value, DEFAULT_TEXT); + const bool reset_visible = !new_button_text.match(DEFAULT_TEXT); + + EditorUndoRedoManager* undo = OrchestratorPlugin::get_singleton()->get_undo_redo(); + undo->create_action("Orchestration: Change node path pin"); + undo->add_do_method(_pin.ptr(), "set_default_value", p_pin_value); + undo->add_do_method(_button, "set_text", new_button_text); + undo->add_do_method(_reset_button, "set_visible", reset_visible); + undo->add_undo_method(_pin.ptr(), "set_default_value", _pin->get_effective_default_value()); + undo->add_undo_method(_button, "set_text", _button->get_text()); + undo->add_undo_method(_reset_button, "set_visible", _reset_button->is_visible()); + undo->commit_action(); + } } void OrchestratorGraphNodePinNodePath::_pin_connected(int p_type, int p_index) diff --git a/src/editor/graph/pins/graph_node_pin_numeric.cpp b/src/editor/graph/pins/graph_node_pin_numeric.cpp index b2bc5158..1c784396 100644 --- a/src/editor/graph/pins/graph_node_pin_numeric.cpp +++ b/src/editor/graph/pins/graph_node_pin_numeric.cpp @@ -16,7 +16,9 @@ // #include "graph_node_pin_numeric.h" -#include +#include "editor/plugins/orchestrator_editor_plugin.h" + +#include OrchestratorGraphNodePinNumeric::OrchestratorGraphNodePinNumeric(OrchestratorGraphNode* p_node, const Ref& p_pin) : OrchestratorGraphNodePin(p_node, p_pin) @@ -29,42 +31,59 @@ void OrchestratorGraphNodePinNumeric::_bind_methods() bool OrchestratorGraphNodePinNumeric::_set_default_value(const String& p_value) { + if (String(_pin->get_effective_default_value()) == p_value) + return false; + switch (_pin->get_type()) { case Variant::INT: - _pin->set_default_value(p_value.to_int()); + { + EditorUndoRedoManager* undo = OrchestratorPlugin::get_singleton()->get_undo_redo(); + undo->create_action("Orchestration: Change integer pin"); + undo->add_do_method(_pin.ptr(), "set_default_value", p_value.to_int()); + undo->add_do_method(_line_edit, "set_text", p_value); + undo->add_undo_method(_pin.ptr(), "set_default_value", _pin->get_effective_default_value()); + undo->add_undo_method(_line_edit, "set_text", String(_pin->get_effective_default_value())); + undo->commit_action(); return true; - + } case Variant::FLOAT: - _pin->set_default_value(p_value.to_float()); + { + EditorUndoRedoManager* undo = OrchestratorPlugin::get_singleton()->get_undo_redo(); + undo->create_action("Orchestration: Change float pin"); + undo->add_do_method(_pin.ptr(), "set_default_value", p_value.to_float()); + undo->add_do_method(_line_edit, "set_text", p_value); + undo->add_undo_method(_pin.ptr(), "set_default_value", _pin->get_effective_default_value()); + undo->add_undo_method(_line_edit, "set_text", String(_pin->get_effective_default_value())); + undo->commit_action(); return true; - + } default: ERR_PRINT("Cannot set default value for an unknown numeric pin type"); return false; } } -void OrchestratorGraphNodePinNumeric::_on_text_submitted(const String& p_value, LineEdit* p_line_edit) +void OrchestratorGraphNodePinNumeric::_on_text_submitted(const String& p_value) { - if (_set_default_value(p_value) && p_line_edit) - p_line_edit->release_focus(); + if (_set_default_value(p_value)) + _line_edit->release_focus(); } -void OrchestratorGraphNodePinNumeric::_on_focus_lost(const LineEdit* p_line_edit) +void OrchestratorGraphNodePinNumeric::_on_focus_lost() { - _set_default_value(p_line_edit->get_text()); + _set_default_value(_line_edit->get_text()); } Control* OrchestratorGraphNodePinNumeric::_get_default_value_widget() { - LineEdit* line_edit = memnew(LineEdit); - line_edit->set_expand_to_text_length_enabled(true); - line_edit->set_h_size_flags(Control::SIZE_EXPAND); - line_edit->set_text(_pin->get_effective_default_value()); - line_edit->add_theme_constant_override("minimum_character_width", 0); - line_edit->set_select_all_on_focus(true); - line_edit->connect("text_submitted", callable_mp(this, &OrchestratorGraphNodePinNumeric::_on_text_submitted).bind(line_edit)); - line_edit->connect("focus_exited", callable_mp(this, &OrchestratorGraphNodePinNumeric::_on_focus_lost).bind(line_edit)); - return line_edit; + _line_edit = memnew(LineEdit); + _line_edit->set_expand_to_text_length_enabled(true); + _line_edit->set_h_size_flags(Control::SIZE_EXPAND); + _line_edit->set_text(_pin->get_effective_default_value()); + _line_edit->add_theme_constant_override("minimum_character_width", 0); + _line_edit->set_select_all_on_focus(true); + _line_edit->connect("text_submitted", callable_mp(this, &OrchestratorGraphNodePinNumeric::_on_text_submitted)); + _line_edit->connect("focus_exited", callable_mp(this, &OrchestratorGraphNodePinNumeric::_on_focus_lost)); + return _line_edit; } diff --git a/src/editor/graph/pins/graph_node_pin_numeric.h b/src/editor/graph/pins/graph_node_pin_numeric.h index 4990a260..e6823011 100644 --- a/src/editor/graph/pins/graph_node_pin_numeric.h +++ b/src/editor/graph/pins/graph_node_pin_numeric.h @@ -19,10 +19,7 @@ #include "editor/graph/graph_node_pin.h" -namespace godot -{ - class LineEdit; -} +#include /// An implementation of OrchestratorGraphNodePin for types that want to represent their default values /// using a string-based text field for numeric data entry. @@ -33,6 +30,8 @@ class OrchestratorGraphNodePinNumeric : public OrchestratorGraphNodePin static void _bind_methods(); protected: + LineEdit* _line_edit{ nullptr }; + OrchestratorGraphNodePinNumeric() = default; /// Sets the default value. @@ -42,12 +41,10 @@ class OrchestratorGraphNodePinNumeric : public OrchestratorGraphNodePin /// Called when the user hits "ENTER" in the line edit widget. /// @param p_value the submitted value - /// @param p_line_edit the line edit widget - void _on_text_submitted(const String& p_value, LineEdit* p_line_edit); + void _on_text_submitted(const String& p_value); /// Called when focus is lost on the line edit widget. - /// @param p_line_edit the line edit widget - void _on_focus_lost(const LineEdit* p_line_edit); + void _on_focus_lost(); //~ Begin OrchestratorGraphNodePin Interface Control* _get_default_value_widget() override; diff --git a/src/editor/graph/pins/graph_node_pin_string.cpp b/src/editor/graph/pins/graph_node_pin_string.cpp index ecde9c42..4b0f76b8 100644 --- a/src/editor/graph/pins/graph_node_pin_string.cpp +++ b/src/editor/graph/pins/graph_node_pin_string.cpp @@ -16,6 +16,9 @@ // #include "graph_node_pin_string.h" +#include "editor/plugins/orchestrator_editor_plugin.h" + +#include #include #include @@ -30,60 +33,74 @@ void OrchestratorGraphNodePinString::_bind_methods() void OrchestratorGraphNodePinString::_set_default_value(const String& p_value) { - _pin->set_default_value(p_value); + if (_pin->get_effective_default_value() != p_value) + { + if (_pin->is_multiline_text()) + { + // TextEdit reacts weirdly with UndoRedo - for now skipped. + _pin->set_default_value(p_value); + } + else + { + EditorUndoRedoManager* undo = OrchestratorPlugin::get_singleton()->get_undo_redo(); + undo->create_action("Orchestration: Change string pin"); + undo->add_do_method(_pin.ptr(), "set_default_value", p_value); + undo->add_do_method(_line_edit, "set_text", p_value); + undo->add_undo_method(_pin.ptr(), "set_default_value", _pin->get_effective_default_value()); + undo->add_undo_method(_line_edit, "set_text", _pin->get_effective_default_value()); + undo->commit_action(); + } + } } -void OrchestratorGraphNodePinString::_on_text_changed(TextEdit* p_text_edit) +void OrchestratorGraphNodePinString::_on_text_changed() { - if (p_text_edit) - _set_default_value(p_text_edit->get_text()); + if (_text_edit) + _set_default_value(_text_edit->get_text()); } -void OrchestratorGraphNodePinString::_on_text_submitted(const String& p_value, LineEdit* p_line_edit) +void OrchestratorGraphNodePinString::_on_text_submitted(const String& p_value) { - if (p_line_edit) + if (_line_edit) { - _set_default_value(p_line_edit->get_text()); - p_line_edit->release_focus(); + _set_default_value(p_value); + _line_edit->release_focus(); } } -void OrchestratorGraphNodePinString::_on_focus_lost(LineEdit* p_line_edit) +void OrchestratorGraphNodePinString::_on_focus_lost() { - if (p_line_edit) - _set_default_value(p_line_edit->get_text()); + if (_line_edit) + _set_default_value(_line_edit->get_text()); } Control* OrchestratorGraphNodePinString::_get_default_value_widget() { if (_pin->is_multiline_text()) { - TextEdit* text_edit = memnew(TextEdit); - text_edit->set_placeholder("No value..."); - text_edit->set_h_size_flags(Control::SIZE_EXPAND); - text_edit->set_v_size_flags(Control::SIZE_EXPAND); - text_edit->set_h_grow_direction(Control::GROW_DIRECTION_END); - text_edit->set_custom_minimum_size(Vector2(350, 0)); - text_edit->set_text(_pin->get_effective_default_value()); - text_edit->set_autowrap_mode(TextServer::AUTOWRAP_WORD_SMART); - text_edit->set_line_wrapping_mode(TextEdit::LINE_WRAPPING_BOUNDARY); - text_edit->set_fit_content_height_enabled(true); - text_edit->connect("text_changed", - callable_mp(this, &OrchestratorGraphNodePinString::_on_text_changed).bind(text_edit)); - return text_edit; + _text_edit = memnew(TextEdit); + _text_edit->set_placeholder("No value..."); + _text_edit->set_h_size_flags(Control::SIZE_EXPAND); + _text_edit->set_v_size_flags(Control::SIZE_EXPAND); + _text_edit->set_h_grow_direction(Control::GROW_DIRECTION_END); + _text_edit->set_custom_minimum_size(Vector2(350, 0)); + _text_edit->set_text(_pin->get_effective_default_value()); + _text_edit->set_autowrap_mode(TextServer::AUTOWRAP_WORD_SMART); + _text_edit->set_line_wrapping_mode(TextEdit::LINE_WRAPPING_BOUNDARY); + _text_edit->set_fit_content_height_enabled(true); + _text_edit->connect("text_changed", callable_mp(this, &OrchestratorGraphNodePinString::_on_text_changed)); + return _text_edit; } - LineEdit* line_edit = memnew(LineEdit); - line_edit->set_custom_minimum_size(Vector2(30, 0)); - line_edit->set_expand_to_text_length_enabled(true); - line_edit->set_h_size_flags(Control::SIZE_EXPAND); - line_edit->set_text(_pin->get_effective_default_value()); - line_edit->set_select_all_on_focus(true); - line_edit->connect("text_submitted", - callable_mp(this, &OrchestratorGraphNodePinString::_on_text_submitted).bind(line_edit)); - line_edit->connect("focus_exited", - callable_mp(this, &OrchestratorGraphNodePinString::_on_focus_lost).bind(line_edit)); - return line_edit; + _line_edit = memnew(LineEdit); + _line_edit->set_custom_minimum_size(Vector2(30, 0)); + _line_edit->set_expand_to_text_length_enabled(true); + _line_edit->set_h_size_flags(Control::SIZE_EXPAND); + _line_edit->set_text(_pin->get_effective_default_value()); + _line_edit->set_select_all_on_focus(true); + _line_edit->connect("text_submitted", callable_mp(this, &OrchestratorGraphNodePinString::_on_text_submitted)); + _line_edit->connect("focus_exited", callable_mp(this, &OrchestratorGraphNodePinString::_on_focus_lost)); + return _line_edit; } bool OrchestratorGraphNodePinString::_render_default_value_below_label() const diff --git a/src/editor/graph/pins/graph_node_pin_string.h b/src/editor/graph/pins/graph_node_pin_string.h index a0634edf..e4c19a47 100644 --- a/src/editor/graph/pins/graph_node_pin_string.h +++ b/src/editor/graph/pins/graph_node_pin_string.h @@ -19,11 +19,8 @@ #include "editor/graph/graph_node_pin.h" -namespace godot -{ - class LineEdit; - class TextEdit; -} +#include +#include /// An implementation of OrchestratorGraphNodePin for types that want to represent their default values /// using a string-based text field for data entry. @@ -34,6 +31,9 @@ class OrchestratorGraphNodePinString : public OrchestratorGraphNodePin static void _bind_methods(); protected: + TextEdit* _text_edit{ nullptr }; + LineEdit* _line_edit{ nullptr }; + OrchestratorGraphNodePinString() = default; /// Sets the default value on the pin @@ -41,17 +41,14 @@ class OrchestratorGraphNodePinString : public OrchestratorGraphNodePin void _set_default_value(const String& p_value); /// Called when the text edit's (multi-line text) text changes - /// @param p_text_edit the text edit widget - void _on_text_changed(TextEdit* p_text_edit); + void _on_text_changed(); /// Called when the line edit's text submitted handler. /// @param p_value the text value submitted - /// @param p_line_edit the line edit widget - void _on_text_submitted(const String& p_value, LineEdit* p_line_edit); + void _on_text_submitted(const String& p_value); /// Called when focus is lost on the line edit widget. - /// @param p_line_edit the line edit widget - void _on_focus_lost(LineEdit* p_line_edit); + void _on_focus_lost(); //~ Begin OrchestratorGraphNodePin Interface Control* _get_default_value_widget() override; diff --git a/src/editor/graph/pins/graph_node_pin_struct.cpp b/src/editor/graph/pins/graph_node_pin_struct.cpp index 7f1fd5a0..ec261aa1 100644 --- a/src/editor/graph/pins/graph_node_pin_struct.cpp +++ b/src/editor/graph/pins/graph_node_pin_struct.cpp @@ -18,7 +18,9 @@ #include "api/extension_db.h" #include "common/variant_utils.h" +#include "editor/plugins/orchestrator_editor_plugin.h" +#include #include #include @@ -29,6 +31,21 @@ OrchestratorGraphNodePinStruct::OrchestratorGraphNodePinStruct(OrchestratorGraph void OrchestratorGraphNodePinStruct::_bind_methods() { + // Needed for undo/redo + ClassDB::bind_method(D_METHOD("_set_ui_value", "value"), &OrchestratorGraphNodePinStruct::_set_ui_value); +} + +void OrchestratorGraphNodePinStruct::_set_ui_value(const Variant& p_value) +{ + const PackedStringArray property_paths = _get_property_paths(_pin->get_type()); + for (int i = 0; i < property_paths.size(); i++) + { + const String& property_path = property_paths[i]; + PackedStringArray property_name_parts = property_path.split("."); + + Variant value = p_value.get(property_name_parts[0]); + _set_ui_value_by_property_path(property_path, i, value); + } } int OrchestratorGraphNodePinStruct::_get_grid_columns(Variant::Type p_type) const @@ -156,7 +173,18 @@ void OrchestratorGraphNodePinStruct::_set_default_value_from_line_edits() _get_ui_value_by_property_path(property_path, i, value); pin_value.set(property_name_parts[0], value); } - _pin->set_default_value(pin_value); + + Variant value = _pin->get_effective_default_value(); + if (value != pin_value) + { + EditorUndoRedoManager* undo = OrchestratorPlugin::get_singleton()->get_undo_redo(); + undo->create_action("Orchestration: Change struct pin"); + undo->add_do_method(_pin.ptr(), "set_default_value", pin_value); + undo->add_do_method(this, "_set_ui_value", pin_value); + undo->add_undo_method(_pin.ptr(), "set_default_value", value); + undo->add_undo_method(this, "_set_ui_value", value); + undo->commit_action(); + } } Control* OrchestratorGraphNodePinStruct::_get_default_value_widget() diff --git a/src/editor/graph/pins/graph_node_pin_struct.h b/src/editor/graph/pins/graph_node_pin_struct.h index 4362c409..bfde432d 100644 --- a/src/editor/graph/pins/graph_node_pin_struct.h +++ b/src/editor/graph/pins/graph_node_pin_struct.h @@ -33,6 +33,10 @@ class OrchestratorGraphNodePinStruct : public OrchestratorGraphNodePin Vector _edits; //! Line edits for each sub-component + /// Sets the value in the pin edits + /// @param p_value + void _set_ui_value(const Variant& p_value); + /// Calculates the number of grid columns for the pin type /// @param p_type the pin type /// @return the number of grid columns diff --git a/src/script/node.cpp b/src/script/node.cpp index 5296c6d9..7f579b25 100644 --- a/src/script/node.cpp +++ b/src/script/node.cpp @@ -111,7 +111,11 @@ void OScriptNode::set_size(const Vector2& p_size) void OScriptNode::set_position(const Vector2& p_position) { - _position = p_position; + if (_position != p_position) + { + _position = p_position; + emit_changed(); + } } #if GODOT_VERSION >= 0x040300 diff --git a/src/script/node_pin.cpp b/src/script/node_pin.cpp index 8ba438f8..7b3a2d2d 100644 --- a/src/script/node_pin.cpp +++ b/src/script/node_pin.cpp @@ -25,6 +25,10 @@ void OScriptNodePin::_bind_methods() { + // Needed for undo/redo + ClassDB::bind_method(D_METHOD("set_default_value", "value"), &OScriptNodePin::set_default_value); + ClassDB::bind_method(D_METHOD("get_default_value"), &OScriptNodePin::get_default_value); + BIND_ENUM_CONSTANT(EPinDirection::PD_Input) BIND_ENUM_CONSTANT(EPinDirection::PD_Output) BIND_ENUM_CONSTANT(EPinDirection::PD_MAX) @@ -342,7 +346,7 @@ void OScriptNodePin::set_default_value(const Variant& p_default_value) { set_block_signals(true); node->pin_default_value_changed(Ref(this)); - set_block_signals(true); + set_block_signals(false); } emit_changed(); }