diff options
Diffstat (limited to 'editor')
-rw-r--r-- | editor/create_dialog.cpp | 21 | ||||
-rw-r--r-- | editor/editor_data.cpp | 25 | ||||
-rw-r--r-- | editor/editor_data.h | 1 | ||||
-rw-r--r-- | editor/editor_node.cpp | 28 | ||||
-rw-r--r-- | editor/filesystem_dock.cpp | 72 | ||||
-rw-r--r-- | editor/filesystem_dock.h | 5 | ||||
-rw-r--r-- | editor/find_in_files.cpp | 829 | ||||
-rw-r--r-- | editor/find_in_files.h | 184 | ||||
-rw-r--r-- | editor/import/resource_importer_wav.cpp | 28 | ||||
-rw-r--r-- | editor/plugins/script_editor_plugin.cpp | 172 | ||||
-rw-r--r-- | editor/plugins/script_editor_plugin.h | 13 | ||||
-rw-r--r-- | editor/plugins/script_text_editor.cpp | 23 | ||||
-rw-r--r-- | editor/plugins/script_text_editor.h | 2 | ||||
-rw-r--r-- | editor/plugins/tile_set_editor_plugin.cpp | 6 | ||||
-rw-r--r-- | editor/project_settings_editor.cpp | 45 | ||||
-rw-r--r-- | editor/project_settings_editor.h | 6 | ||||
-rw-r--r-- | editor/property_editor.cpp | 53 | ||||
-rw-r--r-- | editor/settings_config_dialog.cpp | 16 |
18 files changed, 1374 insertions, 155 deletions
diff --git a/editor/create_dialog.cpp b/editor/create_dialog.cpp index 13ef6e9e0e..78fb35e354 100644 --- a/editor/create_dialog.cpp +++ b/editor/create_dialog.cpp @@ -433,26 +433,7 @@ Object *CreateDialog::instance_selected() { custom = md; if (custom != String()) { - if (EditorNode::get_editor_data().get_custom_types().has(custom)) { - - for (int i = 0; i < EditorNode::get_editor_data().get_custom_types()[custom].size(); i++) { - if (EditorNode::get_editor_data().get_custom_types()[custom][i].name == selected->get_text(0)) { - Ref<Texture> icon = EditorNode::get_editor_data().get_custom_types()[custom][i].icon; - Ref<Script> script = EditorNode::get_editor_data().get_custom_types()[custom][i].script; - String name = selected->get_text(0); - - Object *ob = ClassDB::instance(custom); - ERR_FAIL_COND_V(!ob, NULL); - if (ob->is_class("Node")) { - ob->call("set_name", name); - } - ob->set_script(script.get_ref_ptr()); - if (icon.is_valid()) - ob->set_meta("_editor_icon", icon); - return ob; - } - } - } + return EditorNode::get_editor_data().instance_custom_type(selected->get_text(0), custom); } else { return ClassDB::instance(selected->get_text(0)); } diff --git a/editor/editor_data.cpp b/editor/editor_data.cpp index 95ed40d889..ef9265ecd2 100644 --- a/editor/editor_data.cpp +++ b/editor/editor_data.cpp @@ -452,6 +452,31 @@ void EditorData::add_custom_type(const String &p_type, const String &p_inherits, custom_types[p_inherits].push_back(ct); } +Object *EditorData::instance_custom_type(const String &p_type, const String &p_inherits) { + + if (get_custom_types().has(p_inherits)) { + + for (int i = 0; i < get_custom_types()[p_inherits].size(); i++) { + if (get_custom_types()[p_inherits][i].name == p_type) { + Ref<Texture> icon = get_custom_types()[p_inherits][i].icon; + Ref<Script> script = get_custom_types()[p_inherits][i].script; + + Object *ob = ClassDB::instance(p_inherits); + ERR_FAIL_COND_V(!ob, NULL); + if (ob->is_class("Node")) { + ob->call("set_name", p_type); + } + ob->set_script(script.get_ref_ptr()); + if (icon.is_valid()) + ob->set_meta("_editor_icon", icon); + return ob; + } + } + } + + return NULL; +} + void EditorData::remove_custom_type(const String &p_type) { for (Map<String, Vector<CustomType> >::Element *E = custom_types.front(); E; E = E->next()) { diff --git a/editor/editor_data.h b/editor/editor_data.h index 844145853d..1a498a6150 100644 --- a/editor/editor_data.h +++ b/editor/editor_data.h @@ -171,6 +171,7 @@ public: void restore_editor_global_states(); void add_custom_type(const String &p_type, const String &p_inherits, const Ref<Script> &p_script, const Ref<Texture> &p_icon); + Object *instance_custom_type(const String &p_type, const String &p_inherits); void remove_custom_type(const String &p_type); const Map<String, Vector<CustomType> > &get_custom_types() const { return custom_types; } diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 0bca78b493..141769b16a 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -1234,29 +1234,25 @@ void EditorNode::_dialog_action(String p_file) { } break; case FILE_EXPORT_TILESET: { - Ref<TileSet> ml; - if (FileAccess::exists(p_file)) { - ml = ResourceLoader::load(p_file, "TileSet"); + Ref<TileSet> tileset; + if (FileAccess::exists(p_file) && file_export_lib_merge->is_pressed()) { + tileset = ResourceLoader::load(p_file, "TileSet"); - if (ml.is_null()) { - if (file_export_lib_merge->is_pressed()) { - current_option = -1; - accept->get_ok()->set_text(TTR("I see..")); - accept->set_text(TTR("Can't load TileSet for merging!")); - accept->popup_centered_minsize(); - return; - } - } else if (!file_export_lib_merge->is_pressed()) { - ml->clear(); + if (tileset.is_null()) { + current_option = -1; + accept->get_ok()->set_text(TTR("I see..")); + accept->set_text(TTR("Can't load TileSet for merging!")); + accept->popup_centered_minsize(); + return; } } else { - ml = Ref<TileSet>(memnew(TileSet)); + tileset = Ref<TileSet>(memnew(TileSet)); } - TileSetEditor::update_library_file(editor_data.get_edited_scene_root(), ml, true); + TileSetEditor::update_library_file(editor_data.get_edited_scene_root(), tileset, true); - Error err = ResourceSaver::save(p_file, ml); + Error err = ResourceSaver::save(p_file, tileset); if (err) { current_option = -1; diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index d5783c9b1a..16223dbb16 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -719,12 +719,13 @@ void FileSystemDock::_push_to_history() { button_hist_next->set_disabled(history_pos == history.size() - 1); } -void FileSystemDock::_get_all_files_in_dir(EditorFileSystemDirectory *efsd, Vector<String> &files) const { +void FileSystemDock::_get_all_items_in_dir(EditorFileSystemDirectory *efsd, Vector<String> &files, Vector<String> &folders) const { if (efsd == NULL) return; for (int i = 0; i < efsd->get_subdir_count(); i++) { - _get_all_files_in_dir(efsd->get_subdir(i), files); + folders.push_back(efsd->get_subdir(i)->get_path()); + _get_all_items_in_dir(efsd->get_subdir(i), files, folders); } for (int i = 0; i < efsd->get_file_count(); i++) { files.push_back(efsd->get_file_path(i)); @@ -746,7 +747,8 @@ void FileSystemDock::_find_remaps(EditorFileSystemDirectory *efsd, const Map<Str } } -void FileSystemDock::_try_move_item(const FileOrFolder &p_item, const String &p_new_path, Map<String, String> &p_renames) const { +void FileSystemDock::_try_move_item(const FileOrFolder &p_item, const String &p_new_path, + Map<String, String> &p_file_renames, Map<String, String> &p_folder_renames) const { //Ensure folder paths end with "/" String old_path = (p_item.is_file || p_item.path.ends_with("/")) ? p_item.path : (p_item.path + "/"); String new_path = (p_item.is_file || p_new_path.ends_with("/")) ? p_new_path : (p_new_path + "/"); @@ -763,11 +765,13 @@ void FileSystemDock::_try_move_item(const FileOrFolder &p_item, const String &p_ } //Build a list of files which will have new paths as a result of this operation - Vector<String> changed_paths; + Vector<String> file_changed_paths; + Vector<String> folder_changed_paths; if (p_item.is_file) { - changed_paths.push_back(old_path); + file_changed_paths.push_back(old_path); } else { - _get_all_files_in_dir(EditorFileSystem::get_singleton()->get_filesystem_path(old_path), changed_paths); + folder_changed_paths.push_back(old_path); + _get_all_items_in_dir(EditorFileSystem::get_singleton()->get_filesystem_path(old_path), file_changed_paths, folder_changed_paths); } DirAccess *da = DirAccess::create(DirAccess::ACCESS_RESOURCES); @@ -783,12 +787,12 @@ void FileSystemDock::_try_move_item(const FileOrFolder &p_item, const String &p_ } // update scene if it is open - for (int i = 0; i < changed_paths.size(); ++i) { - String new_item_path = p_item.is_file ? new_path : changed_paths[i].replace_first(old_path, new_path); - if (ResourceLoader::get_resource_type(new_item_path) == "PackedScene" && editor->is_scene_open(changed_paths[i])) { + for (int i = 0; i < file_changed_paths.size(); ++i) { + String new_item_path = p_item.is_file ? new_path : file_changed_paths[i].replace_first(old_path, new_path); + if (ResourceLoader::get_resource_type(new_item_path) == "PackedScene" && editor->is_scene_open(file_changed_paths[i])) { EditorData *ed = &editor->get_editor_data(); for (int j = 0; j < ed->get_edited_scene_count(); j++) { - if (ed->get_scene_path(j) == changed_paths[i]) { + if (ed->get_scene_path(j) == file_changed_paths[i]) { ed->get_edited_scene_root(j)->set_filename(new_item_path); break; } @@ -797,9 +801,12 @@ void FileSystemDock::_try_move_item(const FileOrFolder &p_item, const String &p_ } //Only treat as a changed dependency if it was successfully moved - for (int i = 0; i < changed_paths.size(); ++i) { - p_renames[changed_paths[i]] = changed_paths[i].replace_first(old_path, new_path); - print_line(" Remap: " + changed_paths[i] + " -> " + p_renames[changed_paths[i]]); + for (int i = 0; i < file_changed_paths.size(); ++i) { + p_file_renames[file_changed_paths[i]] = file_changed_paths[i].replace_first(old_path, new_path); + print_line(" Remap: " + file_changed_paths[i] + " -> " + p_file_renames[file_changed_paths[i]]); + } + for (int i = 0; i < folder_changed_paths.size(); ++i) { + p_folder_renames[folder_changed_paths[i]] = folder_changed_paths[i].replace_first(old_path, new_path); } } else { EditorNode::get_singleton()->add_io_error(TTR("Error moving:") + "\n" + old_path + "\n"); @@ -912,6 +919,25 @@ void FileSystemDock::_update_dependencies_after_move(const Map<String, String> & } } +void FileSystemDock::_update_favorite_dirs_list_after_move(const Map<String, String> &p_renames) const { + + Vector<String> favorite_dirs = EditorSettings::get_singleton()->get_favorite_dirs(); + Vector<String> new_favorite_dirs; + + for (int i = 0; i < favorite_dirs.size(); i++) { + String old_path = favorite_dirs[i] + "/"; + + if (p_renames.has(old_path)) { + String new_path = p_renames[old_path]; + new_favorite_dirs.push_back(new_path.substr(0, new_path.length() - 1)); + } else { + new_favorite_dirs.push_back(favorite_dirs[i]); + } + } + + EditorSettings::get_singleton()->set_favorite_dirs(new_favorite_dirs); +} + void FileSystemDock::_make_dir_confirm() { String dir_name = make_dir_dialog_text->get_text().strip_edges(); @@ -970,10 +996,12 @@ void FileSystemDock::_rename_operation_confirm() { } memdelete(da); - Map<String, String> renames; - _try_move_item(to_rename, new_path, renames); - _update_dependencies_after_move(renames); - _update_resource_paths_after_move(renames); + Map<String, String> file_renames; + Map<String, String> folder_renames; + _try_move_item(to_rename, new_path, file_renames, folder_renames); + _update_dependencies_after_move(file_renames); + _update_resource_paths_after_move(file_renames); + _update_favorite_dirs_list_after_move(folder_renames); //Rescan everything print_line("call rescan!"); @@ -1017,15 +1045,17 @@ void FileSystemDock::_duplicate_operation_confirm() { void FileSystemDock::_move_operation_confirm(const String &p_to_path) { - Map<String, String> renames; + Map<String, String> file_renames; + Map<String, String> folder_renames; for (int i = 0; i < to_move.size(); i++) { String old_path = to_move[i].path.ends_with("/") ? to_move[i].path.substr(0, to_move[i].path.length() - 1) : to_move[i].path; String new_path = p_to_path.plus_file(old_path.get_file()); - _try_move_item(to_move[i], new_path, renames); + _try_move_item(to_move[i], new_path, file_renames, folder_renames); } - _update_dependencies_after_move(renames); - _update_resource_paths_after_move(renames); + _update_dependencies_after_move(file_renames); + _update_resource_paths_after_move(file_renames); + _update_favorite_dirs_list_after_move(folder_renames); print_line("call rescan!"); _rescan(); diff --git a/editor/filesystem_dock.h b/editor/filesystem_dock.h index 377316d1ba..c8448a1022 100644 --- a/editor/filesystem_dock.h +++ b/editor/filesystem_dock.h @@ -176,12 +176,13 @@ private: void _file_selected(); void _dir_selected(); - void _get_all_files_in_dir(EditorFileSystemDirectory *efsd, Vector<String> &files) const; + void _get_all_items_in_dir(EditorFileSystemDirectory *efsd, Vector<String> &files, Vector<String> &folders) const; void _find_remaps(EditorFileSystemDirectory *efsd, const Map<String, String> &renames, Vector<String> &to_remaps) const; - void _try_move_item(const FileOrFolder &p_item, const String &p_new_path, Map<String, String> &p_renames) const; + void _try_move_item(const FileOrFolder &p_item, const String &p_new_path, Map<String, String> &p_file_renames, Map<String, String> &p_folder_renames) const; void _try_duplicate_item(const FileOrFolder &p_item, const String &p_new_path) const; void _update_dependencies_after_move(const Map<String, String> &p_renames) const; void _update_resource_paths_after_move(const Map<String, String> &p_renames) const; + void _update_favorite_dirs_list_after_move(const Map<String, String> &p_renames) const; void _make_dir_confirm(); void _rename_operation_confirm(); diff --git a/editor/find_in_files.cpp b/editor/find_in_files.cpp new file mode 100644 index 0000000000..9442bbc0e8 --- /dev/null +++ b/editor/find_in_files.cpp @@ -0,0 +1,829 @@ +/*************************************************************************/ +/* find_in_files.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "find_in_files.h" +#include "core/os/dir_access.h" +#include "core/os/os.h" +#include "editor_scale.h" +#include "scene/gui/box_container.h" +#include "scene/gui/button.h" +#include "scene/gui/check_box.h" +#include "scene/gui/file_dialog.h" +#include "scene/gui/grid_container.h" +#include "scene/gui/item_list.h" +#include "scene/gui/label.h" +#include "scene/gui/line_edit.h" +#include "scene/gui/progress_bar.h" + +#define ROOT_PREFIX "res://" + +const char *FindInFiles::SIGNAL_RESULT_FOUND = "result_found"; +const char *FindInFiles::SIGNAL_FINISHED = "finished"; + +// TODO Would be nice in Vector and PoolVectors +template <typename T> +inline void pop_back(T &container) { + container.resize(container.size() - 1); +} + +// TODO Copied from TextEdit private, would be nice to extract it in a single place +static bool is_text_char(CharType c) { + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_'; +} + +FindInFiles::FindInFiles() { + _root_prefix = ROOT_PREFIX; + _extension_filter.insert("gd"); + _extension_filter.insert("cs"); + _searching = false; + _whole_words = true; + _match_case = true; +} + +void FindInFiles::set_search_text(String p_pattern) { + _pattern = p_pattern; +} + +void FindInFiles::set_whole_words(bool p_whole_word) { + _whole_words = p_whole_word; +} + +void FindInFiles::set_match_case(bool p_match_case) { + _match_case = p_match_case; +} + +void FindInFiles::set_folder(String folder) { + _root_dir = folder; +} + +void FindInFiles::set_filter(const Set<String> &exts) { + _extension_filter = exts; +} + +void FindInFiles::_notification(int p_notification) { + if (p_notification == NOTIFICATION_PROCESS) { + _process(); + } +} + +void FindInFiles::start() { + if (_pattern == "") { + print_line("Nothing to search, pattern is empty"); + emit_signal(SIGNAL_FINISHED); + return; + } + if (_extension_filter.size() == 0) { + print_line("Nothing to search, filter matches no files"); + emit_signal(SIGNAL_FINISHED); + return; + } + + // Init search + _current_dir = ""; + PoolStringArray init_folder; + init_folder.append(_root_dir); + _folders_stack.push_back(init_folder); + + _initial_files_count = 0; + + _searching = true; + set_process(true); +} + +void FindInFiles::stop() { + _searching = false; + _current_dir = ""; + set_process(false); +} + +void FindInFiles::_process() { + // This part can be moved to a thread if needed + + OS &os = *OS::get_singleton(); + float duration = 0.0; + while (duration < 1.0 / 120.0) { + float time_before = os.get_ticks_msec(); + _iterate(); + duration += (os.get_ticks_msec() - time_before); + } +} + +void FindInFiles::_iterate() { + + if (_folders_stack.size() != 0) { + + // Scan folders first so we can build a list of files and have progress info later + + PoolStringArray &folders_to_scan = _folders_stack[_folders_stack.size() - 1]; + + if (folders_to_scan.size() != 0) { + // Scan one folder below + + String folder_name = folders_to_scan[folders_to_scan.size() - 1]; + pop_back(folders_to_scan); + + _current_dir = _current_dir.plus_file(folder_name); + + PoolStringArray sub_dirs; + _scan_dir(_root_prefix + _current_dir, sub_dirs); + + if (sub_dirs.size() != 0) { + _folders_stack.push_back(sub_dirs); + } + + } else { + // Go back one level + + pop_back(_folders_stack); + _current_dir = _current_dir.get_base_dir(); + + if (_folders_stack.size() == 0) { + // All folders scanned + _initial_files_count = _files_to_scan.size(); + } + } + + } else if (_files_to_scan.size() != 0) { + + // Then scan files + + String fpath = _files_to_scan[_files_to_scan.size() - 1]; + pop_back(_files_to_scan); + _scan_file(_root_prefix + fpath); + + } else { + print_line("Search complete"); + set_process(false); + _current_dir = ""; + _searching = false; + emit_signal(SIGNAL_FINISHED); + } +} + +float FindInFiles::get_progress() const { + if (_initial_files_count != 0) { + return static_cast<float>(_initial_files_count - _files_to_scan.size()) / static_cast<float>(_initial_files_count); + } + return 0; +} + +void FindInFiles::_scan_dir(String path, PoolStringArray &out_folders) { + + DirAccess *dir = DirAccess::open(path); + if (dir == NULL) { + print_line("Cannot open directory! " + path); + return; + } + + //print_line(String("Scanning ") + path); + + dir->list_dir_begin(); + + for (int i = 0; i < 1000; ++i) { + String file = dir->get_next(); + + if (file == "") + break; + + // Ignore special dirs and hidden dirs (such as .git and .import) + if (file == "." || file == ".." || file.begins_with(".")) + continue; + + if (dir->current_is_dir()) + out_folders.append(file); + + else { + String file_ext = file.get_extension(); + if (_extension_filter.has(file_ext)) { + _files_to_scan.push_back(file); + } + } + } +} + +void FindInFiles::_scan_file(String fpath) { + + FileAccess *f = FileAccess::open(fpath, FileAccess::READ); + if (f == NULL) { + f->close(); + print_line(String("Cannot open file ") + fpath); + return; + } + + int line_number = 0; + + while (!f->eof_reached()) { + + // line number starts at 1 + ++line_number; + + int begin = 0; + int end = 0; + + String line = f->get_line(); + + // Find all occurrences in the current line + while (true) { + begin = _match_case ? line.find(_pattern, end) : line.findn(_pattern, end); + + if (begin == -1) + break; + + end = begin + _pattern.length(); + + if (_whole_words) { + if (begin > 0 && is_text_char(line[begin - 1])) { + continue; + } + if (end < line.size() && is_text_char(line[end])) { + continue; + } + } + + emit_signal(SIGNAL_RESULT_FOUND, fpath, line_number, begin, end, line); + } + } + + f->close(); +} + +void FindInFiles::_bind_methods() { + + ADD_SIGNAL(MethodInfo(SIGNAL_RESULT_FOUND, + PropertyInfo(Variant::STRING, "path"), + PropertyInfo(Variant::INT, "line_number"), + PropertyInfo(Variant::INT, "begin"), + PropertyInfo(Variant::INT, "end"), + PropertyInfo(Variant::STRING, "text"))); + + ADD_SIGNAL(MethodInfo(SIGNAL_FINISHED)); +} + +//----------------------------------------------------------------------------- +const char *FindInFilesDialog::SIGNAL_FIND_REQUESTED = "find_requested"; +const char *FindInFilesDialog::SIGNAL_REPLACE_REQUESTED = "replace_requested"; + +FindInFilesDialog::FindInFilesDialog() { + + set_custom_minimum_size(Size2(400, 190)); + set_resizable(true); + set_title(TTR("Find in files")); + + VBoxContainer *vbc = memnew(VBoxContainer); + vbc->set_anchor_and_margin(MARGIN_LEFT, ANCHOR_BEGIN, 8 * EDSCALE); + vbc->set_anchor_and_margin(MARGIN_TOP, ANCHOR_BEGIN, 8 * EDSCALE); + vbc->set_anchor_and_margin(MARGIN_RIGHT, ANCHOR_END, -8 * EDSCALE); + vbc->set_anchor_and_margin(MARGIN_BOTTOM, ANCHOR_END, -8 * EDSCALE); + add_child(vbc); + + GridContainer *gc = memnew(GridContainer); + gc->set_columns(2); + vbc->add_child(gc); + + Label *find_label = memnew(Label); + find_label->set_text(TTR("Find: ")); + gc->add_child(find_label); + + _search_text_line_edit = memnew(LineEdit); + _search_text_line_edit->set_h_size_flags(SIZE_EXPAND_FILL); + _search_text_line_edit->connect("text_changed", this, "_on_search_text_modified"); + _search_text_line_edit->connect("text_entered", this, "_on_search_text_entered"); + gc->add_child(_search_text_line_edit); + + { + Control *placeholder = memnew(Control); + gc->add_child(placeholder); + } + + { + HBoxContainer *hbc = memnew(HBoxContainer); + + _whole_words_checkbox = memnew(CheckBox); + _whole_words_checkbox->set_text(TTR("Whole words")); + _whole_words_checkbox->set_pressed(true); + hbc->add_child(_whole_words_checkbox); + + _match_case_checkbox = memnew(CheckBox); + _match_case_checkbox->set_text(TTR("Match case")); + _match_case_checkbox->set_pressed(true); + hbc->add_child(_match_case_checkbox); + + gc->add_child(hbc); + } + + Label *folder_label = memnew(Label); + folder_label->set_text(TTR("Folder: ")); + gc->add_child(folder_label); + + { + HBoxContainer *hbc = memnew(HBoxContainer); + + Label *prefix_label = memnew(Label); + prefix_label->set_text(ROOT_PREFIX); + hbc->add_child(prefix_label); + + _folder_line_edit = memnew(LineEdit); + _folder_line_edit->set_h_size_flags(SIZE_EXPAND_FILL); + hbc->add_child(_folder_line_edit); + + Button *folder_button = memnew(Button); + folder_button->set_text("..."); + folder_button->connect("pressed", this, "_on_folder_button_pressed"); + hbc->add_child(folder_button); + + _folder_dialog = memnew(FileDialog); + _folder_dialog->set_mode(FileDialog::MODE_OPEN_DIR); + _folder_dialog->connect("dir_selected", this, "_on_folder_selected"); + add_child(_folder_dialog); + + gc->add_child(hbc); + } + + Label *filter_label = memnew(Label); + filter_label->set_text(TTR("Filter: ")); + gc->add_child(filter_label); + + { + HBoxContainer *hbc = memnew(HBoxContainer); + + Vector<String> exts; + exts.push_back("gd"); + exts.push_back("cs"); + + for (int i = 0; i < exts.size(); ++i) { + CheckBox *cb = memnew(CheckBox); + cb->set_text(exts[i]); + cb->set_pressed(true); + hbc->add_child(cb); + _filters.push_back(cb); + } + + gc->add_child(hbc); + } + + { + Control *placeholder = memnew(Control); + placeholder->set_custom_minimum_size(Size2(0, EDSCALE * 16)); + vbc->add_child(placeholder); + } + + { + HBoxContainer *hbc = memnew(HBoxContainer); + hbc->set_alignment(HBoxContainer::ALIGN_CENTER); + + _find_button = memnew(Button); + _find_button->set_text(TTR("Find...")); + _find_button->connect("pressed", this, "_on_find_button_pressed"); + _find_button->set_disabled(true); + hbc->add_child(_find_button); + + { + Control *placeholder = memnew(Control); + placeholder->set_custom_minimum_size(Size2(EDSCALE * 16, 0)); + hbc->add_child(placeholder); + } + + _replace_button = memnew(Button); + _replace_button->set_text(TTR("Replace...")); + _replace_button->connect("pressed", this, "_on_replace_button_pressed"); + _replace_button->set_disabled(true); + hbc->add_child(_replace_button); + + { + Control *placeholder = memnew(Control); + placeholder->set_custom_minimum_size(Size2(EDSCALE * 16, 0)); + hbc->add_child(placeholder); + } + + Button *cancel_button = memnew(Button); + cancel_button->set_text(TTR("Cancel")); + cancel_button->connect("pressed", this, "hide"); + hbc->add_child(cancel_button); + + vbc->add_child(hbc); + } +} + +void FindInFilesDialog::set_search_text(String text) { + _search_text_line_edit->set_text(text); +} + +String FindInFilesDialog::get_search_text() const { + String text = _search_text_line_edit->get_text(); + return text.strip_edges(); +} + +bool FindInFilesDialog::is_match_case() const { + return _match_case_checkbox->is_pressed(); +} + +bool FindInFilesDialog::is_whole_words() const { + return _whole_words_checkbox->is_pressed(); +} + +String FindInFilesDialog::get_folder() const { + String text = _folder_line_edit->get_text(); + return text.strip_edges(); +} + +Set<String> FindInFilesDialog::get_filter() const { + Set<String> filters; + for (int i = 0; i < _filters.size(); ++i) { + CheckBox *cb = _filters[i]; + if (cb->is_pressed()) { + filters.insert(_filters[i]->get_text()); + } + } + return filters; +} + +void FindInFilesDialog::_notification(int p_what) { + if (p_what == NOTIFICATION_VISIBILITY_CHANGED) { + if (is_visible()) { + // Doesn't work more than once if not deferred... + _search_text_line_edit->call_deferred("grab_focus"); + _search_text_line_edit->select_all(); + } + } +} + +void FindInFilesDialog::_on_folder_button_pressed() { + _folder_dialog->popup_centered_ratio(); +} + +void FindInFilesDialog::_on_find_button_pressed() { + emit_signal(SIGNAL_FIND_REQUESTED); + hide(); +} + +void FindInFilesDialog::_on_replace_button_pressed() { + emit_signal(SIGNAL_REPLACE_REQUESTED); + hide(); +} + +void FindInFilesDialog::_on_search_text_modified(String text) { + + ERR_FAIL_COND(!_find_button); + ERR_FAIL_COND(!_replace_button); + + _find_button->set_disabled(get_search_text().empty()); + _replace_button->set_disabled(get_search_text().empty()); +} + +void FindInFilesDialog::_on_search_text_entered(String text) { + // This allows to trigger a global search without leaving the keyboard + if (!_find_button->is_disabled()) + _on_find_button_pressed(); +} + +void FindInFilesDialog::_on_folder_selected(String path) { + int i = path.find("://"); + if (i != -1) + path = path.right(i + 3); + _folder_line_edit->set_text(path); +} + +void FindInFilesDialog::_bind_methods() { + + ClassDB::bind_method("_on_folder_button_pressed", &FindInFilesDialog::_on_folder_button_pressed); + ClassDB::bind_method("_on_find_button_pressed", &FindInFilesDialog::_on_find_button_pressed); + ClassDB::bind_method("_on_replace_button_pressed", &FindInFilesDialog::_on_replace_button_pressed); + 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); + + ADD_SIGNAL(MethodInfo(SIGNAL_FIND_REQUESTED)); + ADD_SIGNAL(MethodInfo(SIGNAL_REPLACE_REQUESTED)); +} + +//----------------------------------------------------------------------------- +const char *FindInFilesPanel::SIGNAL_RESULT_SELECTED = "result_selected"; +const char *FindInFilesPanel::SIGNAL_FILES_MODIFIED = "files_modified"; + +FindInFilesPanel::FindInFilesPanel() { + + _finder = memnew(FindInFiles); + _finder->connect(FindInFiles::SIGNAL_RESULT_FOUND, this, "_on_result_found"); + _finder->connect(FindInFiles::SIGNAL_FINISHED, this, "_on_finished"); + add_child(_finder); + + VBoxContainer *vbc = memnew(VBoxContainer); + vbc->set_anchor_and_margin(MARGIN_LEFT, ANCHOR_BEGIN, 0); + vbc->set_anchor_and_margin(MARGIN_TOP, ANCHOR_BEGIN, 0); + vbc->set_anchor_and_margin(MARGIN_RIGHT, ANCHOR_END, 0); + vbc->set_anchor_and_margin(MARGIN_BOTTOM, ANCHOR_END, 0); + add_child(vbc); + + { + HBoxContainer *hbc = memnew(HBoxContainer); + + Label *find_label = memnew(Label); + find_label->set_text(TTR("Find: ")); + hbc->add_child(find_label); + + _search_text_label = memnew(Label); + _search_text_label->add_font_override("font", get_font("source", "EditorFonts")); + hbc->add_child(_search_text_label); + + _progress_bar = memnew(ProgressBar); + _progress_bar->set_h_size_flags(SIZE_EXPAND_FILL); + hbc->add_child(_progress_bar); + set_progress_visible(false); + + _status_label = memnew(Label); + hbc->add_child(_status_label); + + _cancel_button = memnew(Button); + _cancel_button->set_text(TTR("Cancel")); + _cancel_button->connect("pressed", this, "_on_cancel_button_clicked"); + _cancel_button->set_disabled(true); + hbc->add_child(_cancel_button); + + vbc->add_child(hbc); + } + + // In the future, this should be replaced by a more specific list container, + // which can highlight text regions and change opacity for enabled/disabled states + _results_display = memnew(ItemList); + _results_display->add_font_override("font", get_font("source", "EditorFonts")); + _results_display->set_v_size_flags(SIZE_EXPAND_FILL); + _results_display->connect("item_selected", this, "_on_result_selected"); + vbc->add_child(_results_display); + + { + _replace_container = memnew(HBoxContainer); + + Label *replace_label = memnew(Label); + replace_label->set_text(TTR("Replace: ")); + _replace_container->add_child(replace_label); + + _replace_line_edit = memnew(LineEdit); + _replace_line_edit->set_h_size_flags(SIZE_EXPAND_FILL); + _replace_line_edit->connect("text_changed", this, "_on_replace_text_changed"); + _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->connect("pressed", this, "_on_replace_all_clicked"); + _replace_container->add_child(_replace_all_button); + + _replace_container->hide(); + + vbc->add_child(_replace_container); + } +} + +void FindInFilesPanel::set_with_replace(bool with_replace) { + + _replace_container->set_visible(with_replace); +} + +void FindInFilesPanel::start_search() { + + _results_display->clear(); + _status_label->set_text(TTR("Searching...")); + _search_text_label->set_text(_finder->get_search_text()); + + set_process(true); + set_progress_visible(true); + + _finder->start(); + + update_replace_buttons(); + _cancel_button->set_disabled(false); +} + +void FindInFilesPanel::stop_search() { + + _finder->stop(); + + _status_label->set_text(""); + update_replace_buttons(); + set_progress_visible(false); + _cancel_button->set_disabled(true); +} + +void FindInFilesPanel::_notification(int p_what) { + if (p_what == NOTIFICATION_PROCESS) { + _progress_bar->set_as_ratio(_finder->get_progress()); + } +} + +void FindInFilesPanel::_on_result_found(String fpath, int line_number, int begin, int end, String text) { + + int i = _results_display->get_item_count(); + _results_display->add_item(fpath + ": " + String::num(line_number) + ": " + text.replace("\t", " ")); + _results_display->set_item_metadata(i, varray(fpath, line_number, begin, end)); +} + +void FindInFilesPanel::_on_finished() { + + _status_label->set_text(TTR("Search complete")); + update_replace_buttons(); + set_progress_visible(false); + _cancel_button->set_disabled(true); +} + +void FindInFilesPanel::_on_cancel_button_clicked() { + stop_search(); +} + +void FindInFilesPanel::_on_result_selected(int i) { + + Array meta = _results_display->get_item_metadata(i); + emit_signal(SIGNAL_RESULT_SELECTED, meta[0], meta[1], meta[2], meta[3]); +} + +void FindInFilesPanel::_on_replace_text_changed(String text) { + update_replace_buttons(); +} + +void FindInFilesPanel::_on_replace_all_clicked() { + + String replace_text = get_replace_text(); + ERR_FAIL_COND(replace_text.empty()); + + String last_fpath; + PoolIntArray locations; + PoolStringArray modified_files; + + for (int i = 0; i < _results_display->get_item_count(); ++i) { + + Array meta = _results_display->get_item_metadata(i); + + String fpath = meta[0]; + + // Results are sorted by file, so we can batch replaces + if (fpath != last_fpath) { + if (locations.size() != 0) { + apply_replaces_in_file(last_fpath, locations, replace_text); + modified_files.append(last_fpath); + locations.resize(0); + } + } + + locations.append(meta[1]); // line_number + locations.append(meta[2]); // begin + locations.append(meta[3]); // end + + last_fpath = fpath; + } + + if (locations.size() != 0) { + apply_replaces_in_file(last_fpath, locations, replace_text); + modified_files.append(last_fpath); + } + + // Hide replace bar so we can't trigger the action twice without doing a new search + set_with_replace(false); + + emit_signal(SIGNAL_FILES_MODIFIED, modified_files); +} + +// Same as get_line, but preserves line ending characters +class ConservativeGetLine { +public: + String get_line(FileAccess *f) { + + _line_buffer.clear(); + + CharType c = f->get_8(); + + while (!f->eof_reached()) { + + if (c == '\n') { + _line_buffer.push_back(c); + _line_buffer.push_back(0); + return String::utf8(_line_buffer.ptr()); + + } else if (c == '\0') { + _line_buffer.push_back(c); + return String::utf8(_line_buffer.ptr()); + + } else if (c != '\r') { + _line_buffer.push_back(c); + } + + c = f->get_8(); + } + + _line_buffer.push_back(0); + return String::utf8(_line_buffer.ptr()); + } + +private: + Vector<char> _line_buffer; +}; + +void FindInFilesPanel::apply_replaces_in_file(String fpath, PoolIntArray locations, String text) { + + ERR_FAIL_COND(locations.size() % 3 != 0); + + //print_line(String("Replacing {0} occurrences in {1}").format(varray(fpath, locations.size() / 3))); + + // If the file is already open, I assume the editor will reload it. + // If there are unsaved changes, the user will be asked on focus, + // however that means either loosing changes or loosing replaces. + + FileAccess *f = FileAccess::open(fpath, FileAccess::READ); + ERR_FAIL_COND(f == NULL); + + String buffer; + int current_line = 1; + + ConservativeGetLine conservative; + + String line = conservative.get_line(f); + + PoolIntArray::Read locations_read = locations.read(); + for (int i = 0; i < locations.size(); i += 3) { + + int repl_line_number = locations_read[i]; + int repl_begin = locations_read[i + 1]; + int repl_end = locations_read[i + 2]; + + while (current_line < repl_line_number) { + buffer += line; + line = conservative.get_line(f); + ++current_line; + } + + line = line.left(repl_begin) + text + line.right(repl_end); + } + + buffer += line; + + while (!f->eof_reached()) { + buffer += conservative.get_line(f); + } + + // Now the modified contents are in the buffer, rewrite the file with our changes + + Error err = f->reopen(fpath, FileAccess::WRITE); + ERR_FAIL_COND(err != OK); + + f->store_string(buffer); + + f->close(); +} + +String FindInFilesPanel::get_replace_text() { + return _replace_line_edit->get_text().strip_edges(); +} + +void FindInFilesPanel::update_replace_buttons() { + + String text = get_replace_text(); + bool disabled = text.empty() || _finder->is_searching(); + + _replace_all_button->set_disabled(disabled); +} + +void FindInFilesPanel::set_progress_visible(bool visible) { + _progress_bar->set_self_modulate(Color(1, 1, 1, visible ? 1 : 0)); +} + +void FindInFilesPanel::_bind_methods() { + + ClassDB::bind_method("_on_result_found", &FindInFilesPanel::_on_result_found); + ClassDB::bind_method("_on_finished", &FindInFilesPanel::_on_finished); + ClassDB::bind_method("_on_cancel_button_clicked", &FindInFilesPanel::_on_cancel_button_clicked); + ClassDB::bind_method("_on_result_selected", &FindInFilesPanel::_on_result_selected); + ClassDB::bind_method("_on_replace_text_changed", &FindInFilesPanel::_on_replace_text_changed); + ClassDB::bind_method("_on_replace_all_clicked", &FindInFilesPanel::_on_replace_all_clicked); + + ADD_SIGNAL(MethodInfo(SIGNAL_RESULT_SELECTED, + PropertyInfo(Variant::STRING, "path"), + PropertyInfo(Variant::INT, "line_number"), + PropertyInfo(Variant::INT, "begin"), + PropertyInfo(Variant::INT, "end"))); + + ADD_SIGNAL(MethodInfo(SIGNAL_FILES_MODIFIED, PropertyInfo(Variant::STRING, "paths"))); +} diff --git a/editor/find_in_files.h b/editor/find_in_files.h new file mode 100644 index 0000000000..d57184960b --- /dev/null +++ b/editor/find_in_files.h @@ -0,0 +1,184 @@ +/*************************************************************************/ +/* find_in_files.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef FIND_IN_FILES_H +#define FIND_IN_FILES_H + +#include "scene/gui/dialogs.h" + +// Performs the actual search +class FindInFiles : public Node { + GDCLASS(FindInFiles, Node) +public: + static const char *SIGNAL_RESULT_FOUND; + static const char *SIGNAL_FINISHED; + + FindInFiles(); + + void set_search_text(String p_pattern); + void set_whole_words(bool p_whole_word); + void set_match_case(bool p_match_case); + void set_folder(String folder); + void set_filter(const Set<String> &exts); + + String get_search_text() const { return _pattern; } + + bool is_whole_words() const { return _whole_words; } + bool is_match_case() const { return _match_case; } + + void start(); + void stop(); + + bool is_searching() const { return _searching; } + float get_progress() const; + +protected: + void _notification(int p_notification); + + static void _bind_methods(); + +private: + void _process(); + void _iterate(); + void _scan_dir(String path, PoolStringArray &out_folders); + void _scan_file(String fpath); + + // Config + String _pattern; + Set<String> _extension_filter; + String _root_prefix; + String _root_dir; + bool _whole_words; + bool _match_case; + + // State + bool _searching; + String _current_dir; + Vector<PoolStringArray> _folders_stack; + Vector<String> _files_to_scan; + int _initial_files_count; +}; + +class LineEdit; +class CheckBox; +class FileDialog; + +// Prompts search parameters +class FindInFilesDialog : public WindowDialog { + GDCLASS(FindInFilesDialog, WindowDialog) +public: + static const char *SIGNAL_FIND_REQUESTED; + static const char *SIGNAL_REPLACE_REQUESTED; + + FindInFilesDialog(); + + void set_search_text(String text); + + String get_search_text() const; + bool is_match_case() const; + bool is_whole_words() const; + String get_folder() const; + Set<String> get_filter() const; + +protected: + static void _bind_methods(); + + void _notification(int p_what); + +private: + void _on_folder_button_pressed(); + void _on_find_button_pressed(); + void _on_replace_button_pressed(); + void _on_folder_selected(String path); + void _on_search_text_modified(String text); + void _on_search_text_entered(String text); + + LineEdit *_search_text_line_edit; + LineEdit *_folder_line_edit; + Vector<CheckBox *> _filters; + CheckBox *_match_case_checkbox; + CheckBox *_whole_words_checkbox; + Button *_find_button; + Button *_replace_button; + FileDialog *_folder_dialog; +}; + +class Button; +class ItemList; +class ProgressBar; + +// Display search results +class FindInFilesPanel : public Control { + GDCLASS(FindInFilesPanel, Control) +public: + static const char *SIGNAL_RESULT_SELECTED; + static const char *SIGNAL_FILES_MODIFIED; + + FindInFilesPanel(); + + FindInFiles *get_finder() const { return _finder; } + + void set_with_replace(bool with_replace); + + void start_search(); + void stop_search(); + +protected: + static void _bind_methods(); + + void _notification(int p_what); + +private: + void _on_result_found(String fpath, int line_number, int begin, int end, String text); + void _on_finished(); + void _on_cancel_button_clicked(); + void _on_result_selected(int i); + void _on_replace_text_changed(String text); + void _on_replace_all_clicked(); + + void apply_replaces_in_file(String fpath, PoolIntArray locations, String text); + + void update_replace_buttons(); + String get_replace_text(); + void set_progress_visible(bool visible); + + FindInFiles *_finder; + Label *_search_text_label; + ItemList *_results_display; + Label *_status_label; + Button *_cancel_button; + ProgressBar *_progress_bar; + + HBoxContainer *_replace_container; + LineEdit *_replace_line_edit; + Button *_replace_all_button; +}; + +#endif // FIND_IN_FILES_H diff --git a/editor/import/resource_importer_wav.cpp b/editor/import/resource_importer_wav.cpp index 03155b3a48..debdeb1c4a 100644 --- a/editor/import/resource_importer_wav.cpp +++ b/editor/import/resource_importer_wav.cpp @@ -304,17 +304,23 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s int limit_rate_hz = p_options["force/max_rate_hz"]; if (limit_rate && rate > limit_rate_hz && rate > 0 && frames > 0) { //resampleeee!!! - int new_data_frames = frames * limit_rate_hz / rate; + int new_data_frames = (int)(frames * (float)limit_rate_hz / (float)rate); + + print_line("\tresampling ratio: " + rtos((float)limit_rate_hz / (float)rate)); + print_line("\tnew frames: " + itos(new_data_frames)); + Vector<float> new_data; new_data.resize(new_data_frames * format_channels); for (int c = 0; c < format_channels; c++) { + float frac = .0f; + int ipos = 0; + for (int i = 0; i < new_data_frames; i++) { //simple cubic interpolation should be enough. - float pos = float(i) * frames / new_data_frames; - float mu = pos - Math::floor(pos); - int ipos = int(Math::floor(pos)); + + float mu = frac; float y0 = data[MAX(0, ipos - 1) * format_channels + c]; float y1 = data[ipos * format_channels + c]; @@ -330,14 +336,22 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s float res = (a0 * mu * mu2 + a1 * mu2 + a2 * mu + a3); new_data[i * format_channels + c] = res; + + // update position and always keep fractional part within ]0...1] + // in order to avoid 32bit floating point precision errors + + frac += (float)rate / (float)limit_rate_hz; + int tpos = (int)Math::floor(frac); + ipos += tpos; + frac -= tpos; } } if (loop) { - - loop_begin = loop_begin * new_data_frames / frames; - loop_end = loop_end * new_data_frames / frames; + loop_begin = (int)(loop_begin * (float)new_data_frames / (float)frames); + loop_end = (int)(loop_end * (float)new_data_frames / (float)frames); } + data = new_data; rate = limit_rate_hz; frames = new_data_frames; diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index 2ce36ee8d5..9193b3fbbf 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -39,9 +39,11 @@ #include "core/project_settings.h" #include "editor/editor_node.h" #include "editor/editor_settings.h" +#include "editor/find_in_files.h" #include "editor/node_dock.h" #include "editor/script_editor_debugger.h" #include "scene/main/viewport.h" +#include "script_text_editor.h" /*** SCRIPT EDITOR ****/ @@ -54,6 +56,8 @@ void ScriptEditorBase::_bind_methods() { ADD_SIGNAL(MethodInfo("request_open_script_at_line", PropertyInfo(Variant::OBJECT, "script"), PropertyInfo(Variant::INT, "line"))); ADD_SIGNAL(MethodInfo("request_save_history")); 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"))); } static bool _can_open_in_editor(Script *p_script) { @@ -298,15 +302,9 @@ void ScriptEditor::_script_created(Ref<Script> p_script) { void ScriptEditor::_goto_script_line2(int p_line) { - int selected = tab_container->get_current_tab(); - if (selected < 0 || selected >= tab_container->get_child_count()) - return; - - ScriptEditorBase *current = Object::cast_to<ScriptEditorBase>(tab_container->get_child(selected)); - if (!current) - return; - - current->goto_line(p_line); + ScriptEditorBase *current = _get_current_editor(); + if (current) + current->goto_line(p_line); } void ScriptEditor::_goto_script_line(REF p_script, int p_line) { @@ -316,19 +314,22 @@ void ScriptEditor::_goto_script_line(REF p_script, int p_line) { if (edit(p_script, p_line, 0)) { editor->push_item(p_script.ptr()); - int selected = tab_container->get_current_tab(); - if (selected < 0 || selected >= tab_container->get_child_count()) - return; - - ScriptEditorBase *current = Object::cast_to<ScriptEditorBase>(tab_container->get_child(selected)); - if (!current) - return; - - current->goto_line(p_line, true); + ScriptEditorBase *current = _get_current_editor(); + if (current) + current->goto_line(p_line, true); } } } +ScriptEditorBase *ScriptEditor::_get_current_editor() const { + + int selected = tab_container->get_current_tab(); + if (selected < 0 || selected >= tab_container->get_child_count()) + return NULL; + + return Object::cast_to<ScriptEditorBase>(tab_container->get_child(selected)); +} + void ScriptEditor::_update_history_arrows() { script_back->set_disabled(history_pos <= 0); @@ -587,7 +588,7 @@ void ScriptEditor::_close_docs_tab() { } void ScriptEditor::_copy_script_path() { - ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(tab_container->get_current_tab())); + ScriptEditorBase *se = _get_current_editor(); Ref<Script> script = se->get_edited_script(); OS::get_singleton()->set_clipboard(script->get_path()); } @@ -819,11 +820,8 @@ void ScriptEditor::_file_dialog_action(String p_file) { Ref<Script> ScriptEditor::_get_current_script() { - int selected = tab_container->get_current_tab(); - if (selected < 0 || selected >= tab_container->get_child_count()) - return NULL; + ScriptEditorBase *current = _get_current_editor(); - ScriptEditorBase *current = Object::cast_to<ScriptEditorBase>(tab_container->get_child(selected)); if (current) { return current->get_edited_script(); } else { @@ -939,11 +937,7 @@ void ScriptEditor::_menu_option(int p_option) { } } - int selected = tab_container->get_current_tab(); - if (selected < 0 || selected >= tab_container->get_child_count()) - return; - - ScriptEditorBase *current = Object::cast_to<ScriptEditorBase>(tab_container->get_child(selected)); + ScriptEditorBase *current = _get_current_editor(); if (current) { switch (p_option) { @@ -1033,7 +1027,7 @@ void ScriptEditor::_menu_option(int p_option) { _copy_script_path(); } break; case SHOW_IN_FILE_SYSTEM: { - ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(tab_container->get_current_tab())); + ScriptEditorBase *se = _get_current_editor(); Ref<Script> script = se->get_edited_script(); FileSystemDock *file_system_dock = EditorNode::get_singleton()->get_filesystem_dock(); file_system_dock->navigate_to_path(script->get_path()); @@ -1223,6 +1217,17 @@ void ScriptEditor::_notification(int p_what) { recent_scripts->set_as_minsize(); } break; + case CanvasItem::NOTIFICATION_VISIBILITY_CHANGED: { + + if (is_visible()) { + find_in_files_button->show(); + } else { + find_in_files->hide(); + find_in_files_button->hide(); + } + + } break; + default: break; } @@ -1230,15 +1235,11 @@ void ScriptEditor::_notification(int p_what) { bool ScriptEditor::can_take_away_focus() const { - int selected = tab_container->get_current_tab(); - if (selected < 0 || selected >= tab_container->get_child_count()) - return true; - - ScriptEditorBase *current = Object::cast_to<ScriptEditorBase>(tab_container->get_child(selected)); - if (!current) + ScriptEditorBase *current = _get_current_editor(); + if (current) + return current->can_lose_focus_on_node_selection(); + else return true; - - return current->can_lose_focus_on_node_selection(); } void ScriptEditor::close_builtin_scripts_from_scene(const String &p_scene) { @@ -1315,20 +1316,13 @@ void ScriptEditor::ensure_focus_current() { if (!is_inside_tree()) return; - int cidx = tab_container->get_current_tab(); - if (cidx < 0 || cidx >= tab_container->get_tab_count()) - return; - - Control *c = Object::cast_to<Control>(tab_container->get_child(cidx)); - ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(c); - if (!se) - return; - se->ensure_focus(); + ScriptEditorBase *current = _get_current_editor(); + if (current) + current->ensure_focus(); } void ScriptEditor::_members_overview_selected(int p_idx) { - Node *current = tab_container->get_child(tab_container->get_current_tab()); - ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(current); + ScriptEditorBase *se = _get_current_editor(); if (!se) { return; } @@ -1362,18 +1356,12 @@ void ScriptEditor::ensure_select_current() { if (tab_container->get_child_count() && tab_container->get_current_tab() >= 0) { - Node *current = tab_container->get_child(tab_container->get_current_tab()); - - ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(current); + ScriptEditorBase *se = _get_current_editor(); if (se) { - Ref<Script> script = se->get_edited_script(); - if (!grab_focus_block && is_visible_in_tree()) se->ensure_focus(); } - - EditorHelp *eh = Object::cast_to<EditorHelp>(current); } _update_selected_editor_menu(); @@ -1413,12 +1401,7 @@ struct _ScriptEditorItemData { void ScriptEditor::_update_members_overview_visibility() { - int selected = tab_container->get_current_tab(); - if (selected < 0 || selected >= tab_container->get_child_count()) - return; - - Node *current = tab_container->get_child(tab_container->get_current_tab()); - ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(current); + ScriptEditorBase *se = _get_current_editor(); if (!se) { members_overview->set_visible(false); return; @@ -1434,12 +1417,7 @@ void ScriptEditor::_update_members_overview_visibility() { void ScriptEditor::_update_members_overview() { members_overview->clear(); - int selected = tab_container->get_current_tab(); - if (selected < 0 || selected >= tab_container->get_child_count()) - return; - - Node *current = tab_container->get_child(tab_container->get_current_tab()); - ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(current); + ScriptEditorBase *se = _get_current_editor(); if (!se) { return; } @@ -1813,6 +1791,7 @@ bool ScriptEditor::edit(const Ref<Script> &p_script, int p_line, int p_col, bool se->connect("request_open_script_at_line", this, "_goto_script_line"); 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"); //test for modification, maybe the script was not edited but was loaded @@ -2530,6 +2509,48 @@ void ScriptEditor::_script_changed() { NodeDock::singleton->update_lists(); } +void ScriptEditor::_on_find_in_files_requested(String text) { + + find_in_files_dialog->set_search_text(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) { + + Ref<Resource> res = ResourceLoader::load(fpath); + edit(res); + + ScriptEditorBase *seb = _get_current_editor(); + + ScriptTextEditor *ste = Object::cast_to<ScriptTextEditor>(seb); + if (ste) { + ste->goto_line_selection(line_number - 1, begin, end); + } +} + +void ScriptEditor::_start_find_in_files(bool with_replace) { + + FindInFiles *f = find_in_files->get_finder(); + + f->set_search_text(find_in_files_dialog->get_search_text()); + f->set_match_case(find_in_files_dialog->is_match_case()); + f->set_whole_words(find_in_files_dialog->is_match_case()); + f->set_folder(find_in_files_dialog->get_folder()); + f->set_filter(find_in_files_dialog->get_filter()); + + find_in_files->set_with_replace(with_replace); + find_in_files->start_search(); + + find_in_files_button->set_pressed(true); + find_in_files->show(); +} + +void ScriptEditor::_on_find_in_files_modified_files(PoolStringArray paths) { + + _test_script_times_on_disk(); + _update_modified_scripts_for_external_editor(); +} + void ScriptEditor::_bind_methods() { ClassDB::bind_method("_file_dialog_action", &ScriptEditor::_file_dialog_action); @@ -2577,6 +2598,10 @@ void ScriptEditor::_bind_methods() { ClassDB::bind_method("_script_list_gui_input", &ScriptEditor::_script_list_gui_input); ClassDB::bind_method("_script_changed", &ScriptEditor::_script_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("_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); ClassDB::bind_method(D_METHOD("get_drag_data_fw", "point", "from"), &ScriptEditor::get_drag_data_fw); ClassDB::bind_method(D_METHOD("can_drop_data_fw", "point", "data", "from"), &ScriptEditor::can_drop_data_fw); @@ -2838,6 +2863,19 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { add_child(help_index); help_index->connect("open_class", this, "_help_class_open"); + find_in_files_dialog = memnew(FindInFilesDialog); + find_in_files_dialog->connect(FindInFilesDialog::SIGNAL_FIND_REQUESTED, this, "_start_find_in_files", varray(false)); + find_in_files_dialog->connect(FindInFilesDialog::SIGNAL_REPLACE_REQUESTED, this, "_start_find_in_files", varray(true)); + add_child(find_in_files_dialog); + find_in_files = memnew(FindInFilesPanel); + find_in_files_button = editor->add_bottom_panel_item(TTR("Search results"), find_in_files); + find_in_files_button->set_tooltip(TTR("Search in files")); + find_in_files->set_custom_minimum_size(Size2(0, 200)); + find_in_files->connect(FindInFilesPanel::SIGNAL_RESULT_SELECTED, this, "_on_find_in_files_result_selected"); + find_in_files->connect(FindInFilesPanel::SIGNAL_FILES_MODIFIED, this, "_on_find_in_files_modified_files"); + find_in_files->hide(); + find_in_files_button->hide(); + history_pos = -1; //debugger_gui->hide(); diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h index f947351089..9f37b18d7d 100644 --- a/editor/plugins/script_editor_plugin.h +++ b/editor/plugins/script_editor_plugin.h @@ -119,6 +119,8 @@ typedef SyntaxHighlighter *(*CreateSyntaxHighlighterFunc)(); typedef ScriptEditorBase *(*CreateScriptEditorFunc)(const Ref<Script> &p_script); class EditorScriptCodeCompletionCache; +class FindInFilesDialog; +class FindInFilesPanel; class ScriptEditor : public PanelContainer { @@ -217,6 +219,10 @@ class ScriptEditor : public PanelContainer { ToolButton *script_back; ToolButton *script_forward; + FindInFilesDialog *find_in_files_dialog; + FindInFilesPanel *find_in_files; + Button *find_in_files_button; + enum { SCRIPT_EDITOR_FUNC_MAX = 32, SYNTAX_HIGHLIGHTER_FUNC_MAX = 32 @@ -304,6 +310,8 @@ class ScriptEditor : public PanelContainer { void _update_window_menu(); void _script_created(Ref<Script> p_script); + ScriptEditorBase *_get_current_editor() const; + void _save_layout(); void _editor_settings_changed(); void _autosave_scripts(); @@ -359,6 +367,11 @@ class ScriptEditor : public PanelContainer { Ref<Script> _get_current_script(); Array _get_open_scripts() const; + void _on_find_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); + static void _open_script_request(const String &p_path); static ScriptEditor *script_editor; diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index 711a313902..bcc575a7ac 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -529,6 +529,14 @@ void ScriptTextEditor::goto_line(int p_line, bool p_with_error) { tx->call_deferred("cursor_set_line", p_line); } +void ScriptTextEditor::goto_line_selection(int p_line, int p_begin, int p_end) { + TextEdit *tx = code_editor->get_text_edit(); + tx->unfold_line(p_line); + tx->call_deferred("cursor_set_line", p_line); + tx->call_deferred("cursor_set_column", p_begin); + tx->select(p_line, p_begin, p_line, p_end); +} + void ScriptTextEditor::ensure_focus() { code_editor->get_text_edit()->grab_focus(); @@ -1173,6 +1181,15 @@ void ScriptTextEditor::_edit_option(int p_op) { code_editor->get_find_replace_bar()->popup_replace(); } break; + case SEARCH_IN_FILES: { + + String selected_text = code_editor->get_text_edit()->get_selection_text(); + + // Yep, because it doesn't make sense to instance this dialog for every single script open... + // So this will be delegated to the ScriptEditor + emit_signal("search_in_files_requested", selected_text); + + } break; case SEARCH_LOCATE_FUNCTION: { quick_open->popup(get_functions()); @@ -1660,6 +1677,8 @@ ScriptTextEditor::ScriptTextEditor() { search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find_previous"), SEARCH_FIND_PREV); 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_separator(); search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_function"), SEARCH_LOCATE_FUNCTION); search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_line"), SEARCH_GOTO_LINE); search_menu->get_popup()->add_separator(); @@ -1739,7 +1758,9 @@ void ScriptTextEditor::register_editor() { ED_SHORTCUT("script_text_editor/find_previous", TTR("Find Previous"), KEY_MASK_SHIFT | KEY_F3); ED_SHORTCUT("script_text_editor/replace", TTR("Replace.."), KEY_MASK_CMD | KEY_R); - ED_SHORTCUT("script_text_editor/goto_function", TTR("Goto Function.."), KEY_MASK_SHIFT | KEY_MASK_CMD | KEY_F); + 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/goto_function", TTR("Goto Function.."), KEY_MASK_ALT | KEY_MASK_CMD | KEY_F); ED_SHORTCUT("script_text_editor/goto_line", TTR("Goto Line.."), KEY_MASK_CMD | KEY_L); ED_SHORTCUT("script_text_editor/contextual_help", TTR("Contextual Help"), KEY_MASK_SHIFT | KEY_F1); diff --git a/editor/plugins/script_text_editor.h b/editor/plugins/script_text_editor.h index eb52d2593a..a93e1a6fa8 100644 --- a/editor/plugins/script_text_editor.h +++ b/editor/plugins/script_text_editor.h @@ -106,6 +106,7 @@ class ScriptTextEditor : public ScriptEditorBase { SEARCH_REPLACE, SEARCH_LOCATE_FUNCTION, SEARCH_GOTO_LINE, + SEARCH_IN_FILES, DEBUG_TOGGLE_BREAKPOINT, DEBUG_REMOVE_ALL_BREAKPOINTS, DEBUG_GOTO_NEXT_BREAKPOINT, @@ -170,6 +171,7 @@ public: virtual void tag_saved_version(); virtual void goto_line(int p_line, bool p_with_error = false); + void goto_line_selection(int p_line, int p_begin, int p_end); virtual void reload(bool p_soft); virtual void get_breakpoints(List<int> *p_breakpoints); diff --git a/editor/plugins/tile_set_editor_plugin.cpp b/editor/plugins/tile_set_editor_plugin.cpp index 2311439728..41692e805f 100644 --- a/editor/plugins/tile_set_editor_plugin.cpp +++ b/editor/plugins/tile_set_editor_plugin.cpp @@ -278,10 +278,11 @@ void TileSetEditor::_changed_callback(Object *p_changed, const char *p_prop) { preview->set_region_rect(tileset->tile_get_region(get_current_tile())); } else if (p_prop == StringName("name")) { update_tile_list_icon(); - } else if (p_prop == StringName("texture") || p_prop == StringName("tile_mode")) { + } else if (p_prop == StringName("texture") || p_prop == StringName("modulate") || p_prop == StringName("tile_mode")) { _on_tile_list_selected(get_current_tile()); workspace->update(); preview->set_texture(tileset->tile_get_texture(get_current_tile())); + preview->set_modulate(tileset->tile_get_modulate(get_current_tile())); preview->set_region_rect(tileset->tile_get_region(get_current_tile())); if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE) property_editor->show(); @@ -578,6 +579,7 @@ void TileSetEditor::_on_tile_list_selected(int p_index) { if (get_current_tile() >= 0) { current_item_index = p_index; preview->set_texture(tileset->tile_get_texture(get_current_tile())); + preview->set_modulate(tileset->tile_get_modulate(get_current_tile())); preview->set_region_rect(tileset->tile_get_region(get_current_tile())); workspace->set_custom_minimum_size(tileset->tile_get_region(get_current_tile()).size); update_workspace_tile_mode(); @@ -1736,6 +1738,7 @@ void TileSetEditor::update_tile_list() { region.position += pos; } tile_list->set_item_icon_region(tile_list->get_item_count() - 1, region); + tile_list->set_item_icon_modulate(tile_list->get_item_count() - 1, tileset->tile_get_modulate(E->get())); } if (tile_list->get_item_count() > 0 && selected_tile < tile_list->get_item_count()) { tile_list->select(selected_tile); @@ -1763,6 +1766,7 @@ void TileSetEditor::update_tile_list_icon() { tile_list->set_item_metadata(current_idx, E->get()); tile_list->set_item_icon(current_idx, tileset->tile_get_texture(E->get())); tile_list->set_item_icon_region(current_idx, region); + tile_list->set_item_icon_modulate(current_idx, tileset->tile_get_modulate(E->get())); tile_list->set_item_text(current_idx, tileset->tile_get_name(E->get())); current_idx += 1; } diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp index 75523cd843..6e3be343ba 100644 --- a/editor/project_settings_editor.cpp +++ b/editor/project_settings_editor.cpp @@ -31,6 +31,7 @@ #include "project_settings_editor.h" #include "core/global_constants.h" +#include "core/input_map.h" #include "core/os/keyboard.h" #include "core/project_settings.h" #include "core/translation.h" @@ -212,7 +213,7 @@ void ProjectSettingsEditor::_device_input_add() { Ref<InputEventMouseButton> mb; mb.instance(); mb->set_button_index(device_index->get_selected() + 1); - mb->set_device(device_id->get_value()); + mb->set_device(_get_current_device()); for (int i = 0; i < arr.size(); i++) { @@ -233,7 +234,7 @@ void ProjectSettingsEditor::_device_input_add() { jm.instance(); jm->set_axis(device_index->get_selected() >> 1); jm->set_axis_value(device_index->get_selected() & 1 ? 1 : -1); - jm->set_device(device_id->get_value()); + jm->set_device(_get_current_device()); for (int i = 0; i < arr.size(); i++) { @@ -254,7 +255,7 @@ void ProjectSettingsEditor::_device_input_add() { jb.instance(); jb->set_button_index(device_index->get_selected()); - jb->set_device(device_id->get_value()); + jb->set_device(_get_current_device()); for (int i = 0; i < arr.size(); i++) { @@ -289,6 +290,20 @@ void ProjectSettingsEditor::_device_input_add() { _show_last_added(ie, name); } +void ProjectSettingsEditor::_set_current_device(int i_device) { + device_id->select(i_device + 1); +} + +int ProjectSettingsEditor::_get_current_device() { + return device_id->get_selected() - 1; +} + +String ProjectSettingsEditor::_get_device_string(int i_device) { + if (i_device == InputMap::ALL_DEVICES) + return TTR("All Devices"); + return TTR("Device") + " " + itos(i_device); +} + void ProjectSettingsEditor::_press_a_key_confirm() { if (last_wait_for_key.is_null()) @@ -422,10 +437,10 @@ void ProjectSettingsEditor::_add_item(int p_item, Ref<InputEvent> p_exiting_even Ref<InputEventMouseButton> mb = p_exiting_event; if (mb.is_valid()) { device_index->select(mb->get_button_index() - 1); - device_id->set_value(mb->get_device()); + _set_current_device(mb->get_device()); device_input->get_ok()->set_text(TTR("Change")); } else { - device_id->set_value(0); + _set_current_device(0); device_input->get_ok()->set_text(TTR("Add")); } } break; @@ -443,10 +458,10 @@ void ProjectSettingsEditor::_add_item(int p_item, Ref<InputEvent> p_exiting_even Ref<InputEventJoypadMotion> jm = p_exiting_event; if (jm.is_valid()) { device_index->select(jm->get_axis() * 2 + (jm->get_axis_value() > 0 ? 1 : 0)); - device_id->set_value(jm->get_device()); + _set_current_device(jm->get_device()); device_input->get_ok()->set_text(TTR("Change")); } else { - device_id->set_value(0); + _set_current_device(0); device_input->get_ok()->set_text(TTR("Add")); } } break; @@ -464,10 +479,10 @@ void ProjectSettingsEditor::_add_item(int p_item, Ref<InputEvent> p_exiting_even Ref<InputEventJoypadButton> jb = p_exiting_event; if (jb.is_valid()) { device_index->select(jb->get_button_index()); - device_id->set_value(jb->get_device()); + _set_current_device(jb->get_device()); device_input->get_ok()->set_text(TTR("Change")); } else { - device_id->set_value(0); + _set_current_device(0); device_input->get_ok()->set_text(TTR("Add")); } @@ -676,7 +691,7 @@ void ProjectSettingsEditor::_update_actions() { if (jb.is_valid()) { - String str = TTR("Device") + " " + itos(jb->get_device()) + ", " + TTR("Button") + " " + itos(jb->get_button_index()); + String str = _get_device_string(jb->get_device()) + ", " + TTR("Button") + " " + itos(jb->get_button_index()); if (jb->get_button_index() >= 0 && jb->get_button_index() < JOY_BUTTON_MAX) str += String() + " (" + _button_names[jb->get_button_index()] + ")."; else @@ -689,7 +704,7 @@ void ProjectSettingsEditor::_update_actions() { Ref<InputEventMouseButton> mb = ie; if (mb.is_valid()) { - String str = TTR("Device") + " " + itos(mb->get_device()) + ", "; + String str = _get_device_string(mb->get_device()) + ", "; switch (mb->get_button_index()) { case BUTTON_LEFT: str += TTR("Left Button."); break; case BUTTON_RIGHT: str += TTR("Right Button."); break; @@ -710,7 +725,7 @@ void ProjectSettingsEditor::_update_actions() { int ax = jm->get_axis(); int n = 2 * ax + (jm->get_axis_value() < 0 ? 0 : 1); String desc = _axis_names[n]; - String str = TTR("Device") + " " + itos(jm->get_device()) + ", " + TTR("Axis") + " " + itos(ax) + " " + (jm->get_axis_value() < 0 ? "-" : "+") + desc + "."; + String str = _get_device_string(jm->get_device()) + ", " + TTR("Axis") + " " + itos(ax) + " " + (jm->get_axis_value() < 0 ? "-" : "+") + desc + "."; action->set_text(0, str); action->set_icon(0, get_icon("JoyAxis", "EditorIcons")); } @@ -1781,8 +1796,10 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) { l->set_text(TTR("Device:")); vbc_left->add_child(l); - device_id = memnew(SpinBox); - device_id->set_value(0); + device_id = memnew(OptionButton); + for (int i = -1; i < 8; i++) + device_id->add_item(_get_device_string(i)); + _set_current_device(0); vbc_left->add_child(device_id); VBoxContainer *vbc_right = memnew(VBoxContainer); diff --git a/editor/project_settings_editor.h b/editor/project_settings_editor.h index 9364b0ff2c..0ced88d7f6 100644 --- a/editor/project_settings_editor.h +++ b/editor/project_settings_editor.h @@ -80,7 +80,7 @@ class ProjectSettingsEditor : public AcceptDialog { ConfirmationDialog *press_a_key; Label *press_a_key_label; ConfirmationDialog *device_input; - SpinBox *device_id; + OptionButton *device_id; OptionButton *device_index; Label *device_index_label; MenuButton *popup_copy_to_feature; @@ -170,6 +170,10 @@ protected: void _notification(int p_what); static void _bind_methods(); + int _get_current_device(); + void _set_current_device(int i_device); + String _get_device_string(int i_device); + public: void add_translation(const String &p_translation); static ProjectSettingsEditor *get_singleton() { return singleton; } diff --git a/editor/property_editor.cpp b/editor/property_editor.cpp index 043add046e..4bd70d0c29 100644 --- a/editor/property_editor.cpp +++ b/editor/property_editor.cpp @@ -285,6 +285,11 @@ void CustomPropertyEditor::_menu_option(int p_which) { } Object *obj = ClassDB::instance(intype); + + if (!obj) { + obj = EditorNode::get_editor_data().instance_custom_type(intype, "Resource"); + } + ERR_BREAK(!obj); Resource *res = Object::cast_to<Resource>(obj); ERR_BREAK(!res); @@ -877,6 +882,12 @@ bool CustomPropertyEditor::edit(Object *p_owner, const String &p_name, Variant:: } else if (hint_text != "") { int idx = 0; + Vector<EditorData::CustomType> custom_resources; + + if (EditorNode::get_editor_data().get_custom_types().has("Resource")) { + custom_resources = EditorNode::get_editor_data().get_custom_types()["Resource"]; + } + for (int i = 0; i < hint_text.get_slice_count(","); i++) { String base = hint_text.get_slice(",", i); @@ -885,6 +896,11 @@ bool CustomPropertyEditor::edit(Object *p_owner, const String &p_name, Variant:: valid_inheritors.insert(base); List<StringName> inheritors; ClassDB::get_inheriters_from_class(base.strip_edges(), &inheritors); + + for (int i = 0; i < custom_resources.size(); i++) { + inheritors.push_back(custom_resources[i].name); + } + List<StringName>::Element *E = inheritors.front(); while (E) { valid_inheritors.insert(E->get()); @@ -893,14 +909,34 @@ bool CustomPropertyEditor::edit(Object *p_owner, const String &p_name, Variant:: for (Set<String>::Element *E = valid_inheritors.front(); E; E = E->next()) { String t = E->get(); - if (!ClassDB::can_instance(t)) + + bool is_custom_resource = false; + Ref<Texture> icon; + if (!custom_resources.empty()) { + for (int i = 0; i < custom_resources.size(); i++) { + if (custom_resources[i].name == t) { + is_custom_resource = true; + if (custom_resources[i].icon.is_valid()) + icon = custom_resources[i].icon; + break; + } + } + } + + if (!is_custom_resource && !ClassDB::can_instance(t)) continue; + inheritors_array.push_back(t); int id = TYPE_BASE_ID + idx; - if (has_icon(t, "EditorIcons")) { - menu->add_icon_item(get_icon(t, "EditorIcons"), vformat(TTR("New %s"), t), id); + if (!icon.is_valid() && has_icon(t, "EditorIcons")) { + icon = get_icon(t, "EditorIcons"); + } + + if (icon.is_valid()) { + + menu->add_icon_item(icon, vformat(TTR("New %s"), t), id); } else { menu->add_item(vformat(TTR("New %s"), t), id); @@ -1094,6 +1130,10 @@ void CustomPropertyEditor::_type_create_selected(int p_idx) { Object *obj = ClassDB::instance(intype); + if (!obj) { + obj = EditorNode::get_editor_data().instance_custom_type(intype, "Resource"); + } + ERR_FAIL_COND(!obj); Resource *res = Object::cast_to<Resource>(obj); @@ -1291,6 +1331,11 @@ void CustomPropertyEditor::_action_pressed(int p_which) { if (hint == PROPERTY_HINT_RESOURCE_TYPE) { Object *obj = ClassDB::instance(intype); + + if (!obj) { + obj = EditorNode::get_editor_data().instance_custom_type(intype, "Resource"); + } + ERR_BREAK(!obj); Resource *res = Object::cast_to<Resource>(obj); ERR_BREAK(!res); @@ -3939,7 +3984,7 @@ void PropertyEditor::_edit_button(Object *p_item, int p_column, int p_button) { if (_might_be_in_instance() && _get_instanced_node_original_property(prop, vorig)) { - _edit_set(prop, vorig); + _edit_set(prop, vorig.duplicate(true)); // Set, making sure to duplicate arrays properly return; } diff --git a/editor/settings_config_dialog.cpp b/editor/settings_config_dialog.cpp index d75ef5df8a..36d7a83930 100644 --- a/editor/settings_config_dialog.cpp +++ b/editor/settings_config_dialog.cpp @@ -199,6 +199,14 @@ void EditorSettingsDialog::_update_icons() { void EditorSettingsDialog::_update_shortcuts() { + Map<String, bool> collapsed; + + if (shortcuts->get_root() && shortcuts->get_root()->get_children()) { + for (TreeItem *item = shortcuts->get_root()->get_children(); item; item = item->get_next()) { + collapsed[item->get_text(0)] = item->is_collapsed(); + } + } + shortcuts->clear(); List<String> slist; @@ -223,7 +231,13 @@ void EditorSettingsDialog::_update_shortcuts() { section = sections[section_name]; } else { section = shortcuts->create_item(root); - section->set_text(0, section_name.capitalize()); + + String item_name = section_name.capitalize(); + section->set_text(0, item_name); + + if (collapsed.has(item_name)) { + section->set_collapsed(collapsed[item_name]); + } sections[section_name] = section; section->set_custom_bg_color(0, get_color("prop_subsection", "Editor")); |