diff options
Diffstat (limited to 'editor/filesystem_dock.cpp')
-rw-r--r-- | editor/filesystem_dock.cpp | 265 |
1 files changed, 243 insertions, 22 deletions
diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index d4bd97a393..dd2eec6893 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -45,10 +45,13 @@ #include "editor/editor_resource_preview.h" #include "editor/editor_settings.h" #include "editor/editor_string_names.h" +#include "editor/editor_undo_redo_manager.h" #include "editor/gui/editor_dir_dialog.h" #include "editor/gui/editor_scene_tabs.h" #include "editor/import/3d/scene_import_settings.h" #include "editor/import_dock.h" +#include "editor/plugins/editor_context_menu_plugin.h" +#include "editor/plugins/editor_resource_conversion_plugin.h" #include "editor/plugins/editor_resource_tooltip_plugins.h" #include "editor/scene_create_dialog.h" #include "editor/scene_tree_dock.h" @@ -215,6 +218,7 @@ bool FileSystemDock::_create_tree(TreeItem *p_parent, EditorFileSystemDirectory // Set custom folder color (if applicable). bool has_custom_color = assigned_folder_colors.has(lpath); Color custom_color = has_custom_color ? folder_colors[assigned_folder_colors[lpath]] : Color(); + Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES); if (has_custom_color) { subdirectory_item->set_icon_modulate(0, editor_is_dark_theme ? custom_color : custom_color * ITEM_COLOR_SCALE); @@ -236,6 +240,10 @@ bool FileSystemDock::_create_tree(TreeItem *p_parent, EditorFileSystemDirectory subdirectory_item->set_text(0, dname); subdirectory_item->set_structured_text_bidi_override(0, TextServer::STRUCTURED_TEXT_FILE); subdirectory_item->set_icon(0, get_editor_theme_icon(SNAME("Folder"))); + if (da->is_link(lpath)) { + subdirectory_item->set_icon_overlay(0, get_editor_theme_icon(SNAME("LinkOverlay"))); + subdirectory_item->set_tooltip_text(0, vformat(TTR("Link to: %s"), da->read_link(lpath))); + } subdirectory_item->set_selectable(0, true); subdirectory_item->set_metadata(0, lpath); if (!p_select_in_favorites && (current_path == lpath || ((display_mode != DISPLAY_MODE_TREE_ONLY) && current_path.get_base_dir() == lpath))) { @@ -270,7 +278,7 @@ bool FileSystemDock::_create_tree(TreeItem *p_parent, EditorFileSystemDirectory List<FileInfo> file_list; for (int i = 0; i < p_dir->get_file_count(); i++) { String file_type = p_dir->get_file_type(i); - if (file_type != "TextFile" && _is_file_type_disabled_by_feature_profile(file_type)) { + if (file_type != "TextFile" && file_type != "OtherFile" && _is_file_type_disabled_by_feature_profile(file_type)) { // If type is disabled, file won't be displayed. continue; } @@ -308,6 +316,10 @@ bool FileSystemDock::_create_tree(TreeItem *p_parent, EditorFileSystemDirectory file_item->set_text(0, fi.name); file_item->set_structured_text_bidi_override(0, TextServer::STRUCTURED_TEXT_FILE); file_item->set_icon(0, _get_tree_item_icon(!fi.import_broken, fi.type, fi.icon_path)); + if (da->is_link(file_metadata)) { + file_item->set_icon_overlay(0, get_editor_theme_icon(SNAME("LinkOverlay"))); + file_item->set_tooltip_text(0, vformat(TTR("Link to: %s"), da->read_link(file_metadata))); + } file_item->set_icon_max_width(0, icon_size); Color parent_bg_color = subdirectory_item->get_custom_bg_color(0); if (has_custom_color) { @@ -637,8 +649,7 @@ void FileSystemDock::_notification(int p_what) { } if (do_redraw) { - _update_file_list(true); - _update_tree(get_uncollapsed_paths()); + update_all(); } if (EditorThemeManager::is_generated_theme_outdated()) { @@ -685,7 +696,15 @@ void FileSystemDock::_tree_multi_selected(Object *p_item, int p_column, bool p_s } Vector<String> FileSystemDock::get_selected_paths() const { - return _tree_get_selected(false); + if (display_mode == DISPLAY_MODE_TREE_ONLY) { + return _tree_get_selected(false); + } else { + Vector<String> selected = _file_list_get_selected(); + if (selected.is_empty()) { + selected.push_back(get_current_directory()); + } + return selected; + } } String FileSystemDock::get_current_path() const { @@ -953,7 +972,8 @@ void FileSystemDock::_update_file_list(bool p_keep_selection) { files->set_max_columns(1); files->set_max_text_lines(1); files->set_fixed_column_width(0); - files->set_fixed_icon_size(Size2()); + const int icon_size = get_theme_constant(SNAME("class_icon_size"), EditorStringName(Editor)); + files->set_fixed_icon_size(Size2(icon_size, icon_size)); } Ref<Texture2D> folder_icon = (use_thumbnails) ? folder_thumbnail : get_theme_icon(SNAME("folder"), SNAME("FileDialog")); @@ -1169,6 +1189,47 @@ void FileSystemDock::_update_file_list(bool p_keep_selection) { } } +HashSet<String> FileSystemDock::_get_valid_conversions_for_file_paths(const Vector<String> &p_paths) { + HashSet<String> all_valid_conversion_to_targets; + for (const String &fpath : p_paths) { + if (fpath.is_empty() || fpath == "res://" || !FileAccess::exists(fpath) || FileAccess::exists(fpath + ".import")) { + return HashSet<String>(); + } + + Vector<Ref<EditorResourceConversionPlugin>> conversions = EditorNode::get_singleton()->find_resource_conversion_plugin_for_type_name(EditorFileSystem::get_singleton()->get_file_type(fpath)); + + if (conversions.is_empty()) { + // This resource can't convert to anything, so return an empty list. + return HashSet<String>(); + } + + // Get a list of all potentional conversion-to targets. + HashSet<String> current_valid_conversion_to_targets; + for (const Ref<EditorResourceConversionPlugin> &E : conversions) { + const String what = E->converts_to(); + current_valid_conversion_to_targets.insert(what); + } + + if (all_valid_conversion_to_targets.is_empty()) { + // If we have no existing valid conversions, this is the first one, so copy them directly. + all_valid_conversion_to_targets = current_valid_conversion_to_targets; + } else { + // Check existing conversion targets and remove any which are not in the current list. + for (const String &S : all_valid_conversion_to_targets) { + if (!current_valid_conversion_to_targets.has(S)) { + all_valid_conversion_to_targets.erase(S); + } + } + // We have no more remaining valid conversions, so break the loop. + if (all_valid_conversion_to_targets.is_empty()) { + break; + } + } + } + + return all_valid_conversion_to_targets; +} + void FileSystemDock::_select_file(const String &p_path, bool p_select_in_favorites) { String fpath = p_path; if (fpath.ends_with("/")) { @@ -1207,7 +1268,7 @@ void FileSystemDock::_select_file(const String &p_path, bool p_select_in_favorit } if (is_imported) { - SceneImportSettingsDialog::get_singleton()->open_settings(p_path, resource_type == "AnimationLibrary"); + SceneImportSettingsDialog::get_singleton()->open_settings(p_path, resource_type); } else if (resource_type == "PackedScene") { EditorNode::get_singleton()->open_request(fpath); } else { @@ -1281,13 +1342,7 @@ void FileSystemDock::_fs_changed() { scanning_vb->hide(); split_box->show(); - if (tree->is_visible()) { - _update_tree(get_uncollapsed_paths()); - } - - if (file_list_vb->is_visible()) { - _update_file_list(true); - } + update_all(); if (!select_after_scan.is_empty()) { _navigate_to_path(select_after_scan); @@ -1889,6 +1944,54 @@ void FileSystemDock::_overwrite_dialog_action(bool p_overwrite) { _move_operation_confirm(to_move_path, to_move_or_copy, p_overwrite ? OVERWRITE_REPLACE : OVERWRITE_RENAME); } +void FileSystemDock::_convert_dialog_action() { + Vector<Ref<Resource>> selected_resources; + for (const String &S : to_convert) { + Ref<Resource> res = ResourceLoader::load(S); + ERR_FAIL_COND(res.is_null()); + selected_resources.push_back(res); + } + + Vector<Ref<Resource>> converted_resources; + HashSet<Ref<Resource>> resources_to_erase_history_for; + for (Ref<Resource> res : selected_resources) { + Vector<Ref<EditorResourceConversionPlugin>> conversions = EditorNode::get_singleton()->find_resource_conversion_plugin_for_resource(res); + for (const Ref<EditorResourceConversionPlugin> &conversion : conversions) { + int conversion_id = 0; + for (const String &target : cached_valid_conversion_targets) { + if (conversion_id == selected_conversion_id && conversion->converts_to() == target) { + Ref<Resource> converted_res = conversion->convert(res); + ERR_FAIL_COND(res.is_null()); + converted_resources.push_back(converted_res); + resources_to_erase_history_for.insert(res); + break; + } + conversion_id++; + } + } + } + + // Clear history for the objects being replaced. + EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); + for (Ref<Resource> res : resources_to_erase_history_for) { + undo_redo->clear_history(true, undo_redo->get_history_id_for_object(res.ptr())); + } + + // Updates all the resources existing as node properties. + EditorNode::get_singleton()->replace_resources_in_scenes(selected_resources, converted_resources); + + // Overwrite the old resources. + for (int i = 0; i < converted_resources.size(); i++) { + Ref<Resource> original_resource = selected_resources.get(i); + Ref<Resource> new_resource = converted_resources.get(i); + + // Overwrite the path. + new_resource->set_path(original_resource->get_path(), true); + + ResourceSaver::save(new_resource); + } +} + Vector<String> FileSystemDock::_check_existing() { Vector<String> conflicting_items; for (const FileOrFolder &item : to_move) { @@ -2049,6 +2152,15 @@ Vector<String> FileSystemDock::_tree_get_selected(bool remove_self_inclusion, bo return selected_strings; } +Vector<String> FileSystemDock::_file_list_get_selected() const { + Vector<String> selected; + + for (int idx : files->get_selected_items()) { + selected.push_back(files->get_item_metadata(idx)); + } + return selected; +} + Vector<String> FileSystemDock::_remove_self_included_paths(Vector<String> selected_strings) { // Remove paths or files that are included into another. if (selected_strings.size() > 1) { @@ -2098,6 +2210,16 @@ void FileSystemDock::_file_list_rmb_option(int p_option) { _file_option(p_option, selected); } +void FileSystemDock::_generic_rmb_option_selected(int p_option) { + // Used for submenu commands where we don't know whether we're + // calling from the file_list_rmb menu or the _tree_rmb option. + if (files->has_focus()) { + _file_list_rmb_option(p_option); + } else { + _tree_rmb_option(p_option); + } +} + void FileSystemDock::_file_option(int p_option, const Vector<String> &p_selected) { // The first one should be the active item. @@ -2538,6 +2660,33 @@ void FileSystemDock::_file_option(int p_option, const Vector<String> &p_selected String dir = ProjectSettings::get_singleton()->globalize_path(fpath); ScriptEditor::get_singleton()->open_text_file_create_dialog(dir); } break; + + default: { + if (p_option >= EditorContextMenuPlugin::BASE_ID) { + if (!EditorContextMenuPluginManager::get_singleton()->activate_custom_option(EditorContextMenuPlugin::CONTEXT_SLOT_FILESYSTEM, p_option, p_selected)) { + EditorContextMenuPluginManager::get_singleton()->activate_custom_option(EditorContextMenuPlugin::CONTEXT_SLOT_FILESYSTEM_CREATE, p_option, p_selected); + } + } else if (p_option >= CONVERT_BASE_ID) { + selected_conversion_id = p_option - CONVERT_BASE_ID; + ERR_FAIL_INDEX(selected_conversion_id, (int)cached_valid_conversion_targets.size()); + + to_convert.clear(); + for (const String &S : p_selected) { + to_convert.push_back(S); + } + + int conversion_id = 0; + for (const String &E : cached_valid_conversion_targets) { + if (conversion_id == selected_conversion_id) { + conversion_dialog->set_text(vformat(TTR("Do you wish to convert these files to %s? (This operation cannot be undone!)"), E)); + conversion_dialog->popup_centered(); + break; + } + conversion_id++; + } + } + break; + } } } @@ -2654,6 +2803,16 @@ void FileSystemDock::fix_dependencies(const String &p_for_file) { deps_editor->edit(p_for_file); } +void FileSystemDock::update_all() { + if (tree->is_visible()) { + _update_tree(get_uncollapsed_paths()); + } + + if (file_list_vb->is_visible()) { + _update_file_list(true); + } +} + void FileSystemDock::focus_on_path() { current_path_line_edit->grab_focus(); current_path_line_edit->select_all(); @@ -3075,9 +3234,7 @@ void FileSystemDock::_folder_color_index_pressed(int p_index, PopupMenu *p_menu) } _update_folder_colors_setting(); - - _update_tree(get_uncollapsed_paths()); - _update_file_list(true); + update_all(); emit_signal(SNAME("folder_color_changed")); } @@ -3150,7 +3307,7 @@ void FileSystemDock::_file_and_folders_fill_popup(PopupMenu *p_popup, const Vect if (p_paths.size() == 1 && p_display_path_dependent_options) { PopupMenu *new_menu = memnew(PopupMenu); - new_menu->connect(SceneStringName(id_pressed), callable_mp(this, &FileSystemDock::_tree_rmb_option)); + new_menu->connect(SceneStringName(id_pressed), callable_mp(this, &FileSystemDock::_generic_rmb_option_selected)); p_popup->add_submenu_node_item(TTR("Create New"), new_menu, FILE_NEW); p_popup->set_item_icon(p_popup->get_item_index(FILE_NEW), get_editor_theme_icon(SNAME("Add"))); @@ -3160,6 +3317,8 @@ void FileSystemDock::_file_and_folders_fill_popup(PopupMenu *p_popup, const Vect new_menu->add_icon_item(get_editor_theme_icon(SNAME("Script")), TTR("Script..."), FILE_NEW_SCRIPT); new_menu->add_icon_item(get_editor_theme_icon(SNAME("Object")), TTR("Resource..."), FILE_NEW_RESOURCE); new_menu->add_icon_item(get_editor_theme_icon(SNAME("TextFile")), TTR("TextFile..."), FILE_NEW_TEXTFILE); + + EditorContextMenuPluginManager::get_singleton()->add_options_from_plugins(new_menu, EditorContextMenuPlugin::CONTEXT_SLOT_FILESYSTEM_CREATE, p_paths); p_popup->add_separator(); } @@ -3220,6 +3379,41 @@ void FileSystemDock::_file_and_folders_fill_popup(PopupMenu *p_popup, const Vect p_popup->add_icon_item(get_editor_theme_icon(SNAME("NonFavorite")), TTR("Remove from Favorites"), FILE_REMOVE_FAVORITE); } + if (p_paths.size() > 1 || p_paths[0] != "res://") { + cached_valid_conversion_targets = _get_valid_conversions_for_file_paths(p_paths); + + int relative_id = 0; + if (!cached_valid_conversion_targets.is_empty()) { + p_popup->add_separator(); + + // If we have more than one type we can convert into, collapse it into a submenu. + const int CONVERSION_SUBMENU_THRESHOLD = 1; + + PopupMenu *container_menu = p_popup; + String conversion_string_template = "Convert to %s"; + + if (cached_valid_conversion_targets.size() > CONVERSION_SUBMENU_THRESHOLD) { + container_menu = memnew(PopupMenu); + container_menu->connect("id_pressed", callable_mp(this, &FileSystemDock::_generic_rmb_option_selected)); + + p_popup->add_submenu_node_item(TTR("Convert to..."), container_menu, FILE_NEW); + conversion_string_template = "%s"; + } + + for (const String &E : cached_valid_conversion_targets) { + Ref<Texture2D> icon; + if (has_theme_icon(E, SNAME("EditorIcons"))) { + icon = get_editor_theme_icon(E); + } else { + icon = get_editor_theme_icon(SNAME("Object")); + } + + container_menu->add_icon_item(icon, vformat(TTR(conversion_string_template), E), CONVERT_BASE_ID + relative_id); + relative_id++; + } + } + } + { List<String> resource_extensions; ResourceFormatImporter::get_singleton()->get_recognized_extensions_for_type("Resource", &resource_extensions); @@ -3299,6 +3493,7 @@ void FileSystemDock::_file_and_folders_fill_popup(PopupMenu *p_popup, const Vect current_path = fpath; } + EditorContextMenuPluginManager::get_singleton()->add_options_from_plugins(p_popup, EditorContextMenuPlugin::CONTEXT_SLOT_FILESYSTEM, p_paths); } void FileSystemDock::_tree_rmb_select(const Vector2 &p_pos, MouseButton p_button) { @@ -3392,6 +3587,11 @@ void FileSystemDock::_file_list_empty_clicked(const Vector2 &p_pos, MouseButton current_path = current_path_line_edit->get_text(); + // Favorites isn't a directory so don't show menu. + if (current_path == "Favorites") { + return; + } + file_list_popup->clear(); file_list_popup->reset_size(); @@ -3528,7 +3728,16 @@ void FileSystemDock::_tree_gui_input(Ref<InputEvent> p_event) { } else if (ED_IS_SHORTCUT("editor/open_search", p_event)) { focus_on_filter(); } else { - return; + Callable custom_callback = EditorContextMenuPluginManager::get_singleton()->match_custom_shortcut(EditorContextMenuPlugin::CONTEXT_SLOT_FILESYSTEM, p_event); + if (!custom_callback.is_valid()) { + custom_callback = EditorContextMenuPluginManager::get_singleton()->match_custom_shortcut(EditorContextMenuPlugin::CONTEXT_SLOT_FILESYSTEM_CREATE, p_event); + } + + if (custom_callback.is_valid()) { + EditorContextMenuPluginManager::get_singleton()->invoke_callback(custom_callback, _tree_get_selected(false)); + } else { + return; + } } accept_event(); @@ -3596,7 +3805,16 @@ void FileSystemDock::_file_list_gui_input(Ref<InputEvent> p_event) { } else if (ED_IS_SHORTCUT("editor/open_search", p_event)) { focus_on_filter(); } else { - return; + Callable custom_callback = EditorContextMenuPluginManager::get_singleton()->match_custom_shortcut(EditorContextMenuPlugin::CONTEXT_SLOT_FILESYSTEM, p_event); + if (!custom_callback.is_valid()) { + custom_callback = EditorContextMenuPluginManager::get_singleton()->match_custom_shortcut(EditorContextMenuPlugin::CONTEXT_SLOT_FILESYSTEM_CREATE, p_event); + } + + if (custom_callback.is_valid()) { + EditorContextMenuPluginManager::get_singleton()->invoke_callback(custom_callback, files->get_selected_items()); + } else { + return; + } } accept_event(); @@ -3724,8 +3942,7 @@ void FileSystemDock::set_file_sort(FileSortOption p_file_sort) { file_sort = p_file_sort; // Update everything needed. - _update_tree(get_uncollapsed_paths()); - _update_file_list(true); + update_all(); } void FileSystemDock::_file_sort_popup(int p_id) { @@ -4115,7 +4332,6 @@ FileSystemDock::FileSystemDock() { make_dir_dialog = memnew(DirectoryCreateDialog); add_child(make_dir_dialog); - make_dir_dialog->connect("dir_created", callable_mp(this, &FileSystemDock::_rescan).unbind(1)); make_scene_dialog = memnew(SceneCreateDialog); add_child(make_scene_dialog); @@ -4133,6 +4349,11 @@ FileSystemDock::FileSystemDock() { new_resource_dialog->set_base_type("Resource"); new_resource_dialog->connect("create", callable_mp(this, &FileSystemDock::_resource_created)); + conversion_dialog = memnew(ConfirmationDialog); + add_child(conversion_dialog); + conversion_dialog->set_ok_button_text(TTR("Convert")); + conversion_dialog->connect(SceneStringName(confirmed), callable_mp(this, &FileSystemDock::_convert_dialog_action)); + uncollapsed_paths_before_search = Vector<String>(); tree_update_id = 0; |