summaryrefslogtreecommitdiffstats
path: root/editor/filesystem_dock.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'editor/filesystem_dock.cpp')
-rw-r--r--editor/filesystem_dock.cpp265
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;