mirror of
				https://github.com/godotengine/godot.git
				synced 2025-10-31 13:41:03 +00:00 
			
		
		
		
	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.
This commit is contained in:
		
							parent
							
								
									c3606a87fe
								
							
						
					
					
						commit
						7bd0eae635
					
				
					 8 changed files with 119 additions and 5 deletions
				
			
		|  | @ -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,12 +515,29 @@ 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()) { | ||||
| 		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"); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void FindInFilesDialog::_on_folder_selected(String path) { | ||||
| 	int i = path.find("://"); | ||||
| 	if (i != -1) { | ||||
|  | @ -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<Result> | |||
| } | ||||
| 
 | ||||
| String FindInFilesPanel::get_replace_text() { | ||||
| 	return _replace_line_edit->get_text().strip_edges(); | ||||
| 	return _replace_line_edit->get_text(); | ||||
| } | ||||
| 
 | ||||
| void FindInFilesPanel::update_replace_buttons() { | ||||
|  |  | |||
|  | @ -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(); | ||||
|  |  | |||
|  | @ -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(); | ||||
|  |  | |||
|  | @ -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<TextFile> 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); | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
|  | @ -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, | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
|  | @ -89,6 +89,7 @@ private: | |||
| 		SEARCH_FIND_PREV, | ||||
| 		SEARCH_REPLACE, | ||||
| 		SEARCH_IN_FILES, | ||||
| 		REPLACE_IN_FILES, | ||||
| 		SEARCH_GOTO_LINE, | ||||
| 		BOOKMARK_TOGGLE, | ||||
| 		BOOKMARK_GOTO_NEXT, | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Dominik 'dreamsComeTrue' Jasiński
						Dominik 'dreamsComeTrue' Jasiński