From 812c94a20c42f804ce5f96cb631e5bc6b3cd7942 Mon Sep 17 00:00:00 2001 From: robert yevdokimov Date: Fri, 22 Aug 2025 21:41:42 +0400 Subject: [PATCH] Fix `GridMap` Move Action Undo/Redo/Cancel --- .../gridmap/editor/grid_map_editor_plugin.cpp | 95 +++++++++++++++---- .../gridmap/editor/grid_map_editor_plugin.h | 6 +- 2 files changed, 82 insertions(+), 19 deletions(-) diff --git a/modules/gridmap/editor/grid_map_editor_plugin.cpp b/modules/gridmap/editor/grid_map_editor_plugin.cpp index 0299f0f1384..301d3eea516 100644 --- a/modules/gridmap/editor/grid_map_editor_plugin.cpp +++ b/modules/gridmap/editor/grid_map_editor_plugin.cpp @@ -149,33 +149,38 @@ void GridMapEditor::_menu_option(int p_option) { options->get_popup()->set_item_checked(idx, !options->get_popup()->is_item_checked(idx)); } break; - case MENU_OPTION_SELECTION_DUPLICATE: - case MENU_OPTION_SELECTION_CUT: { + case MENU_OPTION_SELECTION_DUPLICATE: { if (!(selection.active && input_action == INPUT_NONE)) { break; } _set_clipboard_data(); + clipboard_is_move = false; - if (p_option == MENU_OPTION_SELECTION_CUT) { - _delete_selection(); + if (!clipboard_items.is_empty()) { + _setup_paste_mode(); + } + } break; + + case MENU_OPTION_SELECTION_MOVE: { + if (!(selection.active && input_action == INPUT_NONE)) { + break; } - input_action = INPUT_PASTE; - paste_indicator.click = selection.click; - paste_indicator.current = cursor_gridpos; - paste_indicator.begin = selection.begin; - paste_indicator.end = selection.end; - paste_indicator.distance_from_cursor = cursor_gridpos - paste_indicator.begin; - paste_indicator.orientation = 0; - _update_paste_indicator(); + _set_clipboard_data(); + clipboard_is_move = true; + + if (!clipboard_items.is_empty()) { + _delete_selection(); + _setup_paste_mode(); + } } break; case MENU_OPTION_SELECTION_CLEAR: { if (!selection.active) { break; } - _delete_selection(); + _delete_selection_with_undo(); } break; case MENU_OPTION_SELECTION_FILL: { @@ -465,6 +470,21 @@ void GridMapEditor::_delete_selection() { return; } + for (int i = selection.begin.x; i <= selection.end.x; i++) { + for (int j = selection.begin.y; j <= selection.end.y; j++) { + for (int k = selection.begin.z; k <= selection.end.z; k++) { + Vector3i selected = Vector3i(i, j, k); + node->set_cell_item(selected, GridMap::INVALID_CELL_ITEM); + } + } + } +} + +void GridMapEditor::_delete_selection_with_undo() { + if (!selection.active) { + return; + } + EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); undo_redo->create_action(TTR("GridMap Delete Selection")); for (int i = selection.begin.x; i <= selection.end.x; i++) { @@ -481,6 +501,17 @@ void GridMapEditor::_delete_selection() { undo_redo->commit_action(); } +void GridMapEditor::_setup_paste_mode() { + input_action = INPUT_PASTE; + paste_indicator.click = selection.click; + paste_indicator.current = cursor_gridpos; + paste_indicator.begin = selection.begin; + paste_indicator.end = selection.end; + paste_indicator.distance_from_cursor = cursor_gridpos - paste_indicator.begin; + paste_indicator.orientation = 0; + _update_paste_indicator(); +} + void GridMapEditor::_fill_selection() { if (!selection.active) { return; @@ -511,6 +542,7 @@ void GridMapEditor::_clear_clipboard_data() { } clipboard_items.clear(); + clipboard_is_move = false; } void GridMapEditor::_set_clipboard_data() { @@ -583,6 +615,20 @@ void GridMapEditor::_update_paste_indicator() { } } +void GridMapEditor::_cancel_pending_move() { + if (input_action == INPUT_PASTE) { + if (clipboard_is_move) { + for (const ClipboardItem &item : clipboard_items) { + Vector3 original_position = paste_indicator.begin + item.grid_offset; + node->set_cell_item(Vector3i(original_position), item.cell_item, item.orientation); + } + } + _clear_clipboard_data(); + input_action = INPUT_NONE; + _update_paste_indicator(); + } +} + void GridMapEditor::_do_paste() { int idx = options->get_popup()->get_item_index(MENU_OPTION_PASTE_SELECTS); bool reselect = options->get_popup()->is_item_checked(idx); @@ -591,7 +637,18 @@ void GridMapEditor::_do_paste() { rot = node->get_basis_with_orthogonal_index(paste_indicator.orientation); EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); - undo_redo->create_action(TTR("GridMap Paste Selection")); + + if (clipboard_is_move) { + undo_redo->create_action(TTR("GridMap Move Selection")); + + for (const ClipboardItem &item : clipboard_items) { + Vector3 original_position = paste_indicator.begin + item.grid_offset; + undo_redo->add_undo_method(node, "set_cell_item", original_position, item.cell_item, item.orientation); + undo_redo->add_do_method(node, "set_cell_item", original_position, GridMap::INVALID_CELL_ITEM); + } + } else { + undo_redo->create_action(TTR("GridMap Paste Selection")); + } for (const ClipboardItem &item : clipboard_items) { Vector3 position = rot.xform(item.grid_offset) + paste_indicator.current - paste_indicator.distance_from_cursor; @@ -674,9 +731,7 @@ EditorPlugin::AfterGUIInput GridMapEditor::forward_spatial_input_event(Camera3D // Hard key actions: if (k->get_keycode() == Key::ESCAPE) { if (input_action == INPUT_PASTE) { - _clear_clipboard_data(); - input_action = INPUT_NONE; - _update_paste_indicator(); + _cancel_pending_move(); return EditorPlugin::AFTER_GUI_INPUT_STOP; } else if (selection.active) { _set_selection(false); @@ -1017,6 +1072,8 @@ void GridMapEditor::edit(GridMap *p_gridmap) { } } + _cancel_pending_move(); + node = p_gridmap; input_action = INPUT_NONE; @@ -1169,6 +1226,7 @@ void GridMapEditor::_notification(int p_what) { } break; case NOTIFICATION_EXIT_TREE: { + _cancel_pending_move(); _clear_clipboard_data(); for (int i = 0; i < 3; i++) { @@ -1427,7 +1485,7 @@ GridMapEditor::GridMapEditor() { move_action_button->set_shortcut(ED_SHORTCUT("grid_map/move_tool", TTRC("Move"), Key::X, true)); fill_action_button->set_accessibility_name(TTRC("Move")); move_action_button->connect(SceneStringName(pressed), - callable_mp(this, &GridMapEditor::_menu_option).bind(MENU_OPTION_SELECTION_CUT)); + callable_mp(this, &GridMapEditor::_menu_option).bind(MENU_OPTION_SELECTION_MOVE)); action_buttons->add_child(move_action_button); viewport_shortcut_buttons.push_back(move_action_button); @@ -1803,6 +1861,7 @@ void GridMapEditorPlugin::make_visible(bool p_visible) { EditorNode::get_bottom_panel()->make_item_visible(grid_map_editor); grid_map_editor->set_process(true); } else { + grid_map_editor->_cancel_pending_move(); grid_map_editor->_show_viewports_transform_gizmo(true); panel_button->hide(); if (grid_map_editor->is_visible_in_tree()) { diff --git a/modules/gridmap/editor/grid_map_editor_plugin.h b/modules/gridmap/editor/grid_map_editor_plugin.h index 398ea278895..8dd8c8e6ccb 100644 --- a/modules/gridmap/editor/grid_map_editor_plugin.h +++ b/modules/gridmap/editor/grid_map_editor_plugin.h @@ -138,6 +138,7 @@ class GridMapEditor : public VBoxContainer { }; LocalVector clipboard_items; + bool clipboard_is_move = false; Color default_color; Color erase_color; @@ -196,7 +197,7 @@ class GridMapEditor : public VBoxContainer { MENU_OPTION_CURSOR_CLEAR_ROTATION, MENU_OPTION_PASTE_SELECTS, MENU_OPTION_SELECTION_DUPLICATE, - MENU_OPTION_SELECTION_CUT, + MENU_OPTION_SELECTION_MOVE, MENU_OPTION_SELECTION_CLEAR, MENU_OPTION_SELECTION_FILL, MENU_OPTION_GRIDMAP_SETTINGS @@ -236,6 +237,7 @@ class GridMapEditor : public VBoxContainer { void _set_clipboard_data(); void _update_paste_indicator(); void _do_paste(); + void _cancel_pending_move(); void _show_viewports_transform_gizmo(bool p_value); void _update_selection_transform(); void _validate_selection(); @@ -248,7 +250,9 @@ class GridMapEditor : public VBoxContainer { void _floor_mouse_exited(); void _delete_selection(); + void _delete_selection_with_undo(); void _fill_selection(); + void _setup_paste_mode(); bool do_input_action(Camera3D *p_camera, const Point2 &p_point, bool p_click);