From 7bd0eae635e7b3943078871b9e86b45cffa0827c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20=27dreamsComeTrue=27=20Jasi=C5=84ski?= Date: Sun, 9 Feb 2020 10:10:58 +0100 Subject: [PATCH] Add "Replace in Files" functionality to text editors The Soft Reload Script shortcut was changed from Ctrl + Shift + R to Ctrl + Alt + R to avoid conflicts. --- editor/find_in_files.cpp | 72 +++++++++++++++++++++++-- editor/find_in_files.h | 16 ++++++ editor/plugins/script_editor_plugin.cpp | 19 ++++++- editor/plugins/script_editor_plugin.h | 2 + editor/plugins/script_text_editor.cpp | 7 +++ editor/plugins/script_text_editor.h | 1 + editor/plugins/text_editor.cpp | 6 +++ editor/plugins/text_editor.h | 1 + 8 files changed, 119 insertions(+), 5 deletions(-) diff --git a/editor/find_in_files.cpp b/editor/find_in_files.cpp index 417b5fe40c5..0ad61740539 100644 --- a/editor/find_in_files.cpp +++ b/editor/find_in_files.cpp @@ -325,6 +325,17 @@ FindInFilesDialog::FindInFilesDialog() { _search_text_line_edit->connect("text_entered", this, "_on_search_text_entered"); gc->add_child(_search_text_line_edit); + _replace_label = memnew(Label); + _replace_label->set_text(TTR("Replace:")); + _replace_label->hide(); + gc->add_child(_replace_label); + + _replace_text_line_edit = memnew(LineEdit); + _replace_text_line_edit->set_h_size_flags(SIZE_EXPAND_FILL); + _replace_text_line_edit->connect("text_entered", this, "_on_replace_text_entered"); + _replace_text_line_edit->hide(); + gc->add_child(_replace_text_line_edit); + gc->add_child(memnew(Control)); // Space to maintain the grid aligned. { @@ -385,6 +396,8 @@ FindInFilesDialog::FindInFilesDialog() { Button *cancel_button = get_ok(); cancel_button->set_text(TTR("Cancel")); + + _mode = SEARCH_MODE; } void FindInFilesDialog::set_search_text(String text) { @@ -392,11 +405,40 @@ void FindInFilesDialog::set_search_text(String text) { _on_search_text_modified(text); } +void FindInFilesDialog::set_replace_text(String text) { + _replace_text_line_edit->set_text(text); +} + +void FindInFilesDialog::set_find_in_files_mode(FindInFilesMode p_mode) { + if (_mode == p_mode) { + return; + } + + _mode = p_mode; + + if (p_mode == SEARCH_MODE) { + set_title(TTR("Find in Files")); + _replace_label->hide(); + _replace_text_line_edit->hide(); + } else if (p_mode == REPLACE_MODE) { + set_title(TTR("Replace in Files")); + _replace_label->show(); + _replace_text_line_edit->show(); + } + + // After hiding some child controls, recalculate proper dialog size. + set_size(Size2(get_size().x, 0)); +} + String FindInFilesDialog::get_search_text() const { String text = _search_text_line_edit->get_text(); return text.strip_edges(); } +String FindInFilesDialog::get_replace_text() const { + return _replace_text_line_edit->get_text(); +} + bool FindInFilesDialog::is_match_case() const { return _match_case_checkbox->is_pressed(); } @@ -473,9 +515,26 @@ void FindInFilesDialog::_on_search_text_modified(String text) { } void FindInFilesDialog::_on_search_text_entered(String text) { - // This allows to trigger a global search without leaving the keyboard + // This allows to trigger a global search without leaving the keyboard. if (!_find_button->is_disabled()) { - custom_action("find"); + if (_mode == SEARCH_MODE) { + custom_action("find"); + } + } + + if (!_replace_button->is_disabled()) { + if (_mode == REPLACE_MODE) { + custom_action("replace"); + } + } +} + +void FindInFilesDialog::_on_replace_text_entered(String text) { + // This allows to trigger a global search without leaving the keyboard. + if (!_replace_button->is_disabled()) { + if (_mode == REPLACE_MODE) { + custom_action("replace"); + } } } @@ -492,6 +551,7 @@ void FindInFilesDialog::_bind_methods() { ClassDB::bind_method("_on_folder_selected", &FindInFilesDialog::_on_folder_selected); ClassDB::bind_method("_on_search_text_modified", &FindInFilesDialog::_on_search_text_modified); ClassDB::bind_method("_on_search_text_entered", &FindInFilesDialog::_on_search_text_entered); + ClassDB::bind_method("_on_replace_text_entered", &FindInFilesDialog::_on_replace_text_entered); ADD_SIGNAL(MethodInfo(SIGNAL_FIND_REQUESTED)); ADD_SIGNAL(MethodInfo(SIGNAL_REPLACE_REQUESTED)); @@ -575,7 +635,7 @@ FindInFilesPanel::FindInFilesPanel() { _replace_container->add_child(_replace_line_edit); _replace_all_button = memnew(Button); - _replace_all_button->set_text(TTR("Replace all (no undo)")); + _replace_all_button->set_text(TTR("Replace All (NO UNDO)")); _replace_all_button->connect("pressed", this, "_on_replace_all_clicked"); _replace_container->add_child(_replace_all_button); @@ -602,6 +662,10 @@ void FindInFilesPanel::set_with_replace(bool with_replace) { } } +void FindInFilesPanel::set_replace_text(String text) { + _replace_line_edit->set_text(text); +} + void FindInFilesPanel::clear() { _file_items.clear(); _result_items.clear(); @@ -906,7 +970,7 @@ void FindInFilesPanel::apply_replaces_in_file(String fpath, const Vector } String FindInFilesPanel::get_replace_text() { - return _replace_line_edit->get_text().strip_edges(); + return _replace_line_edit->get_text(); } void FindInFilesPanel::update_replace_buttons() { diff --git a/editor/find_in_files.h b/editor/find_in_files.h index 80da0923820..93e9d5b5320 100644 --- a/editor/find_in_files.h +++ b/editor/find_in_files.h @@ -97,14 +97,23 @@ class FindInFilesDialog : public AcceptDialog { GDCLASS(FindInFilesDialog, AcceptDialog); public: + enum FindInFilesMode { + SEARCH_MODE, + REPLACE_MODE + }; + static const char *SIGNAL_FIND_REQUESTED; static const char *SIGNAL_REPLACE_REQUESTED; FindInFilesDialog(); void set_search_text(String text); + void set_replace_text(String text); + + void set_find_in_files_mode(FindInFilesMode p_mode); String get_search_text() const; + String get_replace_text() const; bool is_match_case() const; bool is_whole_words() const; String get_folder() const; @@ -121,8 +130,14 @@ private: void _on_folder_selected(String path); void _on_search_text_modified(String text); void _on_search_text_entered(String text); + void _on_replace_text_entered(String text); + FindInFilesMode _mode; LineEdit *_search_text_line_edit; + + Label *_replace_label; + LineEdit *_replace_text_line_edit; + LineEdit *_folder_line_edit; CheckBox *_match_case_checkbox; CheckBox *_whole_words_checkbox; @@ -151,6 +166,7 @@ public: FindInFiles *get_finder() const { return _finder; } void set_with_replace(bool with_replace); + void set_replace_text(String text); void start_search(); void stop_search(); diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index 4dd87b6fc01..e1d7787fdbc 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -61,6 +61,7 @@ void ScriptEditorBase::_bind_methods() { ADD_SIGNAL(MethodInfo("go_to_help", PropertyInfo(Variant::STRING, "what"))); // TODO: This signal is no use for VisualScript. ADD_SIGNAL(MethodInfo("search_in_files_requested", PropertyInfo(Variant::STRING, "text"))); + ADD_SIGNAL(MethodInfo("replace_in_files_requested", PropertyInfo(Variant::STRING, "text"))); } static bool _is_built_in_script(Script *p_script) { @@ -1037,6 +1038,9 @@ void ScriptEditor::_menu_option(int p_option) { case SEARCH_IN_FILES: { _on_find_in_files_requested(""); } break; + case REPLACE_IN_FILES: { + _on_replace_in_files_requested(""); + } break; case SEARCH_HELP: { help_search_dialog->popup_dialog(); } break; @@ -2132,6 +2136,7 @@ bool ScriptEditor::edit(const RES &p_resource, int p_line, int p_col, bool p_gra se->connect("go_to_help", this, "_help_class_goto"); se->connect("request_save_history", this, "_save_history"); se->connect("search_in_files_requested", this, "_on_find_in_files_requested"); + se->connect("replace_in_files_requested", this, "_on_replace_in_files_requested"); //test for modification, maybe the script was not edited but was loaded @@ -2842,10 +2847,12 @@ void ScriptEditor::_update_selected_editor_menu() { script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find_previous", TTR("Find Previous"), KEY_MASK_SHIFT | KEY_F3), HELP_SEARCH_FIND_PREVIOUS); script_search_menu->get_popup()->add_separator(); script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find_in_files", TTR("Find in Files"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_F), SEARCH_IN_FILES); + script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/replace_in_files", TTR("Replace in Files"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_R), REPLACE_IN_FILES); script_search_menu->show(); } else { if (tab_container->get_child_count() == 0) { script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find_in_files", TTR("Find in Files"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_F), SEARCH_IN_FILES); + script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/replace_in_files", TTR("Replace in Files"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_R), REPLACE_IN_FILES); script_search_menu->show(); } else { script_search_menu->hide(); @@ -2986,10 +2993,18 @@ void ScriptEditor::_script_changed() { } void ScriptEditor::_on_find_in_files_requested(String text) { + find_in_files_dialog->set_find_in_files_mode(FindInFilesDialog::SEARCH_MODE); find_in_files_dialog->set_search_text(text); find_in_files_dialog->popup_centered_minsize(); } +void ScriptEditor::_on_replace_in_files_requested(String text) { + find_in_files_dialog->set_find_in_files_mode(FindInFilesDialog::REPLACE_MODE); + find_in_files_dialog->set_search_text(text); + find_in_files_dialog->set_replace_text(""); + find_in_files_dialog->popup_centered_minsize(); +} + void ScriptEditor::_on_find_in_files_result_selected(String fpath, int line_number, int begin, int end) { if (ResourceLoader::exists(fpath)) { RES res = ResourceLoader::load(fpath); @@ -3040,6 +3055,7 @@ void ScriptEditor::_start_find_in_files(bool with_replace) { f->set_filter(find_in_files_dialog->get_filter()); find_in_files->set_with_replace(with_replace); + find_in_files->set_replace_text(find_in_files_dialog->get_replace_text()); find_in_files->start_search(); editor->make_bottom_panel_item_visible(find_in_files); @@ -3115,6 +3131,7 @@ void ScriptEditor::_bind_methods() { ClassDB::bind_method("_filter_methods_text_changed", &ScriptEditor::_filter_methods_text_changed); ClassDB::bind_method("_update_recent_scripts", &ScriptEditor::_update_recent_scripts); ClassDB::bind_method("_on_find_in_files_requested", &ScriptEditor::_on_find_in_files_requested); + ClassDB::bind_method("_on_replace_in_files_requested", &ScriptEditor::_on_replace_in_files_requested); ClassDB::bind_method("_start_find_in_files", &ScriptEditor::_start_find_in_files); ClassDB::bind_method("_on_find_in_files_result_selected", &ScriptEditor::_on_find_in_files_result_selected); ClassDB::bind_method("_on_find_in_files_modified_files", &ScriptEditor::_on_find_in_files_modified_files); @@ -3263,7 +3280,7 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/save_as", TTR("Save As...")), FILE_SAVE_AS); file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/save_all", TTR("Save All"), KEY_MASK_SHIFT | KEY_MASK_ALT | KEY_S), FILE_SAVE_ALL); file_menu->get_popup()->add_separator(); - file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/reload_script_soft", TTR("Soft Reload Script"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_R), FILE_TOOL_RELOAD_SOFT); + file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/reload_script_soft", TTR("Soft Reload Script"), KEY_MASK_CMD | KEY_MASK_ALT | KEY_R), FILE_TOOL_RELOAD_SOFT); file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/copy_path", TTR("Copy Script Path")), FILE_COPY_PATH); file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/show_in_file_system", TTR("Show in FileSystem")), SHOW_IN_FILE_SYSTEM); file_menu->get_popup()->add_separator(); diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h index 055cfd308d4..d413d81b2dd 100644 --- a/editor/plugins/script_editor_plugin.h +++ b/editor/plugins/script_editor_plugin.h @@ -160,6 +160,7 @@ class ScriptEditor : public PanelContainer { DEBUG_KEEP_DEBUGGER_OPEN, DEBUG_WITH_EXTERNAL_EDITOR, SEARCH_IN_FILES, + REPLACE_IN_FILES, SEARCH_HELP, SEARCH_WEBSITE, HELP_SEARCH_FIND, @@ -399,6 +400,7 @@ class ScriptEditor : public PanelContainer { Error _save_text_file(Ref p_text_file, const String &p_path); void _on_find_in_files_requested(String text); + void _on_replace_in_files_requested(String text); void _on_find_in_files_result_selected(String fpath, int line_number, int begin, int end); void _start_find_in_files(bool with_replace); void _on_find_in_files_modified_files(PoolStringArray paths); diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index eca39c80492..7f679316a4d 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -1235,6 +1235,11 @@ void ScriptTextEditor::_edit_option(int p_op) { // So this will be delegated to the ScriptEditor. emit_signal("search_in_files_requested", selected_text); } break; + case REPLACE_IN_FILES: { + String selected_text = code_editor->get_text_edit()->get_selection_text(); + + emit_signal("replace_in_files_requested", selected_text); + } break; case SEARCH_LOCATE_FUNCTION: { quick_open->popup_dialog(get_functions()); quick_open->set_title(TTR("Go to Function")); @@ -1805,6 +1810,7 @@ void ScriptTextEditor::_enable_code_editor() { search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/replace"), SEARCH_REPLACE); search_menu->get_popup()->add_separator(); search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find_in_files"), SEARCH_IN_FILES); + search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/replace_in_files"), REPLACE_IN_FILES); search_menu->get_popup()->add_separator(); search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/contextual_help"), HELP_CONTEXTUAL); search_menu->get_popup()->connect("id_pressed", this, "_edit_option"); @@ -2020,6 +2026,7 @@ void ScriptTextEditor::register_editor() { #endif ED_SHORTCUT("script_text_editor/find_in_files", TTR("Find in Files..."), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_F); + ED_SHORTCUT("script_text_editor/replace_in_files", TTR("Replace in Files..."), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_R); #ifdef OSX_ENABLED ED_SHORTCUT("script_text_editor/contextual_help", TTR("Contextual Help"), KEY_MASK_ALT | KEY_MASK_SHIFT | KEY_SPACE); diff --git a/editor/plugins/script_text_editor.h b/editor/plugins/script_text_editor.h index a073524064a..970d9e58580 100644 --- a/editor/plugins/script_text_editor.h +++ b/editor/plugins/script_text_editor.h @@ -135,6 +135,7 @@ class ScriptTextEditor : public ScriptEditorBase { SEARCH_LOCATE_FUNCTION, SEARCH_GOTO_LINE, SEARCH_IN_FILES, + REPLACE_IN_FILES, BOOKMARK_TOGGLE, BOOKMARK_GOTO_NEXT, BOOKMARK_GOTO_PREV, diff --git a/editor/plugins/text_editor.cpp b/editor/plugins/text_editor.cpp index 4c5b6bb16d9..df23867978f 100644 --- a/editor/plugins/text_editor.cpp +++ b/editor/plugins/text_editor.cpp @@ -450,6 +450,11 @@ void TextEditor::_edit_option(int p_op) { // So this will be delegated to the ScriptEditor. emit_signal("search_in_files_requested", selected_text); } break; + case REPLACE_IN_FILES: { + String selected_text = code_editor->get_text_edit()->get_selection_text(); + + emit_signal("replace_in_files_requested", selected_text); + } break; case SEARCH_GOTO_LINE: { goto_line_dialog->popup_find_line(tx); } break; @@ -616,6 +621,7 @@ TextEditor::TextEditor() { search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/replace"), SEARCH_REPLACE); search_menu->get_popup()->add_separator(); search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find_in_files"), SEARCH_IN_FILES); + search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/replace_in_files"), REPLACE_IN_FILES); edit_menu = memnew(MenuButton); edit_hb->add_child(edit_menu); diff --git a/editor/plugins/text_editor.h b/editor/plugins/text_editor.h index 17fac95521d..7d007725757 100644 --- a/editor/plugins/text_editor.h +++ b/editor/plugins/text_editor.h @@ -89,6 +89,7 @@ private: SEARCH_FIND_PREV, SEARCH_REPLACE, SEARCH_IN_FILES, + REPLACE_IN_FILES, SEARCH_GOTO_LINE, BOOKMARK_TOGGLE, BOOKMARK_GOTO_NEXT,