mirror of
https://github.com/godotengine/godot.git
synced 2024-11-23 12:43:43 +00:00
Add filter & sort to editor file dialog
Closes https://github.com/godotengine/godot-proposals/issues/2721 On `EditorFileDialog`: * Add filter box that only shows folders and files in current directory that match * Add sort button to sort files and directories * Add a shortcut for CTRL+F for selecting the filter box Also moved common code between `EditorFileDialog` and `FileSystemDock` to it's own file. Co-authored-by: fox <12120644+foxydevloper@users.noreply.github.com>
This commit is contained in:
parent
f7c567e2f5
commit
163753949e
61
editor/file_info.cpp
Normal file
61
editor/file_info.cpp
Normal file
@ -0,0 +1,61 @@
|
||||
/**************************************************************************/
|
||||
/* file_info.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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 "editor/file_info.h"
|
||||
|
||||
void sort_file_info_list(List<FileInfo> &r_file_list, FileSortOption p_file_sort_option) {
|
||||
// Sort the file list if needed.
|
||||
switch (p_file_sort_option) {
|
||||
case FileSortOption::FILE_SORT_TYPE:
|
||||
r_file_list.sort_custom<FileInfoTypeComparator>();
|
||||
break;
|
||||
case FileSortOption::FILE_SORT_TYPE_REVERSE:
|
||||
r_file_list.sort_custom<FileInfoTypeComparator>();
|
||||
r_file_list.reverse();
|
||||
break;
|
||||
case FileSortOption::FILE_SORT_MODIFIED_TIME:
|
||||
r_file_list.sort_custom<FileInfoModifiedTimeComparator>();
|
||||
break;
|
||||
case FileSortOption::FILE_SORT_MODIFIED_TIME_REVERSE:
|
||||
r_file_list.sort_custom<FileInfoModifiedTimeComparator>();
|
||||
r_file_list.reverse();
|
||||
break;
|
||||
case FileSortOption::FILE_SORT_NAME_REVERSE:
|
||||
r_file_list.sort();
|
||||
r_file_list.reverse();
|
||||
break;
|
||||
case FileSortOption::FILE_SORT_NAME:
|
||||
r_file_list.sort();
|
||||
break;
|
||||
default:
|
||||
ERR_FAIL_MSG("Invalid file sort option.");
|
||||
break;
|
||||
}
|
||||
}
|
74
editor/file_info.h
Normal file
74
editor/file_info.h
Normal file
@ -0,0 +1,74 @@
|
||||
/**************************************************************************/
|
||||
/* file_info.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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 FILE_INFO_H
|
||||
#define FILE_INFO_H
|
||||
|
||||
#include "core/variant/variant.h"
|
||||
|
||||
enum class FileSortOption {
|
||||
FILE_SORT_NAME = 0,
|
||||
FILE_SORT_NAME_REVERSE = 1,
|
||||
FILE_SORT_TYPE = 2,
|
||||
FILE_SORT_TYPE_REVERSE = 3,
|
||||
FILE_SORT_MODIFIED_TIME = 4,
|
||||
FILE_SORT_MODIFIED_TIME_REVERSE = 5,
|
||||
FILE_SORT_MAX = 6,
|
||||
};
|
||||
|
||||
struct FileInfo {
|
||||
String name;
|
||||
String path;
|
||||
String icon_path;
|
||||
StringName type;
|
||||
Vector<String> sources;
|
||||
bool import_broken = false;
|
||||
uint64_t modified_time = 0;
|
||||
|
||||
bool operator<(const FileInfo &p_fi) const {
|
||||
return FileNoCaseComparator()(name, p_fi.name);
|
||||
}
|
||||
};
|
||||
|
||||
struct FileInfoTypeComparator {
|
||||
bool operator()(const FileInfo &p_a, const FileInfo &p_b) const {
|
||||
return FileNoCaseComparator()(p_a.name.get_extension() + p_a.type + p_a.name.get_basename(), p_b.name.get_extension() + p_b.type + p_b.name.get_basename());
|
||||
}
|
||||
};
|
||||
|
||||
struct FileInfoModifiedTimeComparator {
|
||||
bool operator()(const FileInfo &p_a, const FileInfo &p_b) const {
|
||||
return p_a.modified_time > p_b.modified_time;
|
||||
}
|
||||
};
|
||||
|
||||
void sort_file_info_list(List<FileInfo> &r_file_list, FileSortOption p_file_sort_option);
|
||||
|
||||
#endif // FILE_INFO_H
|
@ -263,7 +263,7 @@ bool FileSystemDock::_create_tree(TreeItem *p_parent, EditorFileSystemDirectory
|
||||
}
|
||||
|
||||
// Create items for all subdirectories.
|
||||
bool reversed = file_sort == FILE_SORT_NAME_REVERSE;
|
||||
bool reversed = file_sort == FileSortOption::FILE_SORT_NAME_REVERSE;
|
||||
for (int i = reversed ? p_dir->get_subdir_count() - 1 : 0;
|
||||
reversed ? i >= 0 : i < p_dir->get_subdir_count();
|
||||
reversed ? i-- : i++) {
|
||||
@ -294,28 +294,28 @@ bool FileSystemDock::_create_tree(TreeItem *p_parent, EditorFileSystemDirectory
|
||||
}
|
||||
}
|
||||
|
||||
FileInfo fi;
|
||||
fi.name = p_dir->get_file(i);
|
||||
fi.type = p_dir->get_file_type(i);
|
||||
fi.icon_path = p_dir->get_file_icon_path(i);
|
||||
fi.import_broken = !p_dir->get_file_import_is_valid(i);
|
||||
fi.modified_time = p_dir->get_file_modified_time(i);
|
||||
FileInfo file_info;
|
||||
file_info.name = p_dir->get_file(i);
|
||||
file_info.type = p_dir->get_file_type(i);
|
||||
file_info.icon_path = p_dir->get_file_icon_path(i);
|
||||
file_info.import_broken = !p_dir->get_file_import_is_valid(i);
|
||||
file_info.modified_time = p_dir->get_file_modified_time(i);
|
||||
|
||||
file_list.push_back(fi);
|
||||
file_list.push_back(file_info);
|
||||
}
|
||||
|
||||
// Sort the file list if needed.
|
||||
_sort_file_info_list(file_list);
|
||||
sort_file_info_list(file_list, file_sort);
|
||||
|
||||
// Build the tree.
|
||||
const int icon_size = get_theme_constant(SNAME("class_icon_size"), EditorStringName(Editor));
|
||||
|
||||
for (const FileInfo &fi : file_list) {
|
||||
for (const FileInfo &file_info : file_list) {
|
||||
TreeItem *file_item = tree->create_item(subdirectory_item);
|
||||
const String file_metadata = lpath.path_join(fi.name);
|
||||
file_item->set_text(0, fi.name);
|
||||
const String file_metadata = lpath.path_join(file_info.name);
|
||||
file_item->set_text(0, file_info.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));
|
||||
file_item->set_icon(0, _get_tree_item_icon(!file_info.import_broken, file_info.type, file_info.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)));
|
||||
@ -860,19 +860,19 @@ void FileSystemDock::_search(EditorFileSystemDirectory *p_path, List<FileInfo> *
|
||||
String file = p_path->get_file(i);
|
||||
|
||||
if (_matches_all_search_tokens(file)) {
|
||||
FileInfo fi;
|
||||
fi.name = file;
|
||||
fi.type = p_path->get_file_type(i);
|
||||
fi.path = p_path->get_file_path(i);
|
||||
fi.import_broken = !p_path->get_file_import_is_valid(i);
|
||||
fi.modified_time = p_path->get_file_modified_time(i);
|
||||
FileInfo file_info;
|
||||
file_info.name = file;
|
||||
file_info.type = p_path->get_file_type(i);
|
||||
file_info.path = p_path->get_file_path(i);
|
||||
file_info.import_broken = !p_path->get_file_import_is_valid(i);
|
||||
file_info.modified_time = p_path->get_file_modified_time(i);
|
||||
|
||||
if (_is_file_type_disabled_by_feature_profile(fi.type)) {
|
||||
if (_is_file_type_disabled_by_feature_profile(file_info.type)) {
|
||||
// This type is disabled, will not appear here.
|
||||
continue;
|
||||
}
|
||||
|
||||
matches->push_back(fi);
|
||||
matches->push_back(file_info);
|
||||
if (matches->size() > p_max_items) {
|
||||
return;
|
||||
}
|
||||
@ -880,45 +880,6 @@ void FileSystemDock::_search(EditorFileSystemDirectory *p_path, List<FileInfo> *
|
||||
}
|
||||
}
|
||||
|
||||
struct FileSystemDock::FileInfoTypeComparator {
|
||||
bool operator()(const FileInfo &p_a, const FileInfo &p_b) const {
|
||||
return FileNoCaseComparator()(p_a.name.get_extension() + p_a.type + p_a.name.get_basename(), p_b.name.get_extension() + p_b.type + p_b.name.get_basename());
|
||||
}
|
||||
};
|
||||
|
||||
struct FileSystemDock::FileInfoModifiedTimeComparator {
|
||||
bool operator()(const FileInfo &p_a, const FileInfo &p_b) const {
|
||||
return p_a.modified_time > p_b.modified_time;
|
||||
}
|
||||
};
|
||||
|
||||
void FileSystemDock::_sort_file_info_list(List<FileSystemDock::FileInfo> &r_file_list) {
|
||||
// Sort the file list if needed.
|
||||
switch (file_sort) {
|
||||
case FILE_SORT_TYPE:
|
||||
r_file_list.sort_custom<FileInfoTypeComparator>();
|
||||
break;
|
||||
case FILE_SORT_TYPE_REVERSE:
|
||||
r_file_list.sort_custom<FileInfoTypeComparator>();
|
||||
r_file_list.reverse();
|
||||
break;
|
||||
case FILE_SORT_MODIFIED_TIME:
|
||||
r_file_list.sort_custom<FileInfoModifiedTimeComparator>();
|
||||
break;
|
||||
case FILE_SORT_MODIFIED_TIME_REVERSE:
|
||||
r_file_list.sort_custom<FileInfoModifiedTimeComparator>();
|
||||
r_file_list.reverse();
|
||||
break;
|
||||
case FILE_SORT_NAME_REVERSE:
|
||||
r_file_list.sort();
|
||||
r_file_list.reverse();
|
||||
break;
|
||||
default: // FILE_SORT_NAME
|
||||
r_file_list.sort();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void FileSystemDock::_update_file_list(bool p_keep_selection) {
|
||||
// Register the previously current and selected items.
|
||||
HashSet<String> previous_selection;
|
||||
@ -1005,22 +966,22 @@ void FileSystemDock::_update_file_list(bool p_keep_selection) {
|
||||
int index;
|
||||
EditorFileSystemDirectory *efd = EditorFileSystem::get_singleton()->find_file(favorite, &index);
|
||||
|
||||
FileInfo fi;
|
||||
fi.name = favorite.get_file();
|
||||
fi.path = favorite;
|
||||
FileInfo file_info;
|
||||
file_info.name = favorite.get_file();
|
||||
file_info.path = favorite;
|
||||
if (efd) {
|
||||
fi.type = efd->get_file_type(index);
|
||||
fi.icon_path = efd->get_file_icon_path(index);
|
||||
fi.import_broken = !efd->get_file_import_is_valid(index);
|
||||
fi.modified_time = efd->get_file_modified_time(index);
|
||||
file_info.type = efd->get_file_type(index);
|
||||
file_info.icon_path = efd->get_file_icon_path(index);
|
||||
file_info.import_broken = !efd->get_file_import_is_valid(index);
|
||||
file_info.modified_time = efd->get_file_modified_time(index);
|
||||
} else {
|
||||
fi.type = "";
|
||||
fi.import_broken = true;
|
||||
fi.modified_time = 0;
|
||||
file_info.type = "";
|
||||
file_info.import_broken = true;
|
||||
file_info.modified_time = 0;
|
||||
}
|
||||
|
||||
if (searched_tokens.is_empty() || _matches_all_search_tokens(fi.name)) {
|
||||
file_list.push_back(fi);
|
||||
if (searched_tokens.is_empty() || _matches_all_search_tokens(file_info.name)) {
|
||||
file_list.push_back(file_info);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1077,7 +1038,7 @@ void FileSystemDock::_update_file_list(bool p_keep_selection) {
|
||||
files->set_item_icon_modulate(-1, editor_is_dark_theme ? inherited_folder_color : inherited_folder_color * ITEM_COLOR_SCALE);
|
||||
}
|
||||
|
||||
bool reversed = file_sort == FILE_SORT_NAME_REVERSE;
|
||||
bool reversed = file_sort == FileSortOption::FILE_SORT_NAME_REVERSE;
|
||||
for (int i = reversed ? efd->get_subdir_count() - 1 : 0;
|
||||
reversed ? i >= 0 : i < efd->get_subdir_count();
|
||||
reversed ? i-- : i++) {
|
||||
@ -1099,21 +1060,21 @@ void FileSystemDock::_update_file_list(bool p_keep_selection) {
|
||||
|
||||
// Display the folder content.
|
||||
for (int i = 0; i < efd->get_file_count(); i++) {
|
||||
FileInfo fi;
|
||||
fi.name = efd->get_file(i);
|
||||
fi.path = directory.path_join(fi.name);
|
||||
fi.type = efd->get_file_type(i);
|
||||
fi.icon_path = efd->get_file_icon_path(i);
|
||||
fi.import_broken = !efd->get_file_import_is_valid(i);
|
||||
fi.modified_time = efd->get_file_modified_time(i);
|
||||
FileInfo file_info;
|
||||
file_info.name = efd->get_file(i);
|
||||
file_info.path = directory.path_join(file_info.name);
|
||||
file_info.type = efd->get_file_type(i);
|
||||
file_info.icon_path = efd->get_file_icon_path(i);
|
||||
file_info.import_broken = !efd->get_file_import_is_valid(i);
|
||||
file_info.modified_time = efd->get_file_modified_time(i);
|
||||
|
||||
file_list.push_back(fi);
|
||||
file_list.push_back(file_info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the file list if needed.
|
||||
_sort_file_info_list(file_list);
|
||||
sort_file_info_list(file_list, file_sort);
|
||||
|
||||
// Fills the ItemList control node from the FileInfos.
|
||||
String main_scene = GLOBAL_GET("application/run/main_scene");
|
||||
@ -3935,7 +3896,7 @@ void FileSystemDock::_project_settings_changed() {
|
||||
}
|
||||
|
||||
void FileSystemDock::set_file_sort(FileSortOption p_file_sort) {
|
||||
for (int i = 0; i != FILE_SORT_MAX; i++) {
|
||||
for (int i = 0; i != (int)FileSortOption::FILE_SORT_MAX; i++) {
|
||||
tree_button_sort->get_popup()->set_item_checked(i, (i == (int)p_file_sort));
|
||||
file_list_button_sort->get_popup()->set_item_checked(i, (i == (int)p_file_sort));
|
||||
}
|
||||
@ -3965,13 +3926,13 @@ MenuButton *FileSystemDock::_create_file_menu_button() {
|
||||
|
||||
PopupMenu *p = button->get_popup();
|
||||
p->connect(SceneStringName(id_pressed), callable_mp(this, &FileSystemDock::_file_sort_popup));
|
||||
p->add_radio_check_item(TTR("Sort by Name (Ascending)"), FILE_SORT_NAME);
|
||||
p->add_radio_check_item(TTR("Sort by Name (Descending)"), FILE_SORT_NAME_REVERSE);
|
||||
p->add_radio_check_item(TTR("Sort by Type (Ascending)"), FILE_SORT_TYPE);
|
||||
p->add_radio_check_item(TTR("Sort by Type (Descending)"), FILE_SORT_TYPE_REVERSE);
|
||||
p->add_radio_check_item(TTR("Sort by Last Modified"), FILE_SORT_MODIFIED_TIME);
|
||||
p->add_radio_check_item(TTR("Sort by First Modified"), FILE_SORT_MODIFIED_TIME_REVERSE);
|
||||
p->set_item_checked(file_sort, true);
|
||||
p->add_radio_check_item(TTR("Sort by Name (Ascending)"), (int)FileSortOption::FILE_SORT_NAME);
|
||||
p->add_radio_check_item(TTR("Sort by Name (Descending)"), (int)FileSortOption::FILE_SORT_NAME_REVERSE);
|
||||
p->add_radio_check_item(TTR("Sort by Type (Ascending)"), (int)FileSortOption::FILE_SORT_TYPE);
|
||||
p->add_radio_check_item(TTR("Sort by Type (Descending)"), (int)FileSortOption::FILE_SORT_TYPE_REVERSE);
|
||||
p->add_radio_check_item(TTR("Sort by Last Modified"), (int)FileSortOption::FILE_SORT_MODIFIED_TIME);
|
||||
p->add_radio_check_item(TTR("Sort by First Modified"), (int)FileSortOption::FILE_SORT_MODIFIED_TIME_REVERSE);
|
||||
p->set_item_checked((int)file_sort, true);
|
||||
return button;
|
||||
}
|
||||
|
||||
@ -4041,7 +4002,7 @@ void FileSystemDock::save_layout_to_config(Ref<ConfigFile> p_layout, const Strin
|
||||
p_layout->set_value(p_section, "dock_filesystem_h_split_offset", get_h_split_offset());
|
||||
p_layout->set_value(p_section, "dock_filesystem_v_split_offset", get_v_split_offset());
|
||||
p_layout->set_value(p_section, "dock_filesystem_display_mode", get_display_mode());
|
||||
p_layout->set_value(p_section, "dock_filesystem_file_sort", get_file_sort());
|
||||
p_layout->set_value(p_section, "dock_filesystem_file_sort", (int)get_file_sort());
|
||||
p_layout->set_value(p_section, "dock_filesystem_file_list_display_mode", get_file_list_display_mode());
|
||||
PackedStringArray selected_files = get_selected_paths();
|
||||
p_layout->set_value(p_section, "dock_filesystem_selected_paths", selected_files);
|
||||
|
@ -33,6 +33,7 @@
|
||||
|
||||
#include "editor/dependency_editor.h"
|
||||
#include "editor/editor_file_system.h"
|
||||
#include "editor/file_info.h"
|
||||
#include "editor/plugins/script_editor_plugin.h"
|
||||
#include "editor/script_create_dialog.h"
|
||||
#include "scene/gui/box_container.h"
|
||||
@ -93,16 +94,6 @@ public:
|
||||
DISPLAY_MODE_HSPLIT,
|
||||
};
|
||||
|
||||
enum FileSortOption {
|
||||
FILE_SORT_NAME = 0,
|
||||
FILE_SORT_NAME_REVERSE,
|
||||
FILE_SORT_TYPE,
|
||||
FILE_SORT_TYPE_REVERSE,
|
||||
FILE_SORT_MODIFIED_TIME,
|
||||
FILE_SORT_MODIFIED_TIME_REVERSE,
|
||||
FILE_SORT_MAX,
|
||||
};
|
||||
|
||||
enum Overwrite {
|
||||
OVERWRITE_UNDECIDED,
|
||||
OVERWRITE_REPLACE,
|
||||
@ -146,7 +137,7 @@ private:
|
||||
HashMap<String, Color> folder_colors;
|
||||
Dictionary assigned_folder_colors;
|
||||
|
||||
FileSortOption file_sort = FILE_SORT_NAME;
|
||||
FileSortOption file_sort = FileSortOption::FILE_SORT_NAME;
|
||||
|
||||
VBoxContainer *scanning_vb = nullptr;
|
||||
ProgressBar *scanning_progress = nullptr;
|
||||
@ -336,25 +327,6 @@ private:
|
||||
void _tree_empty_click(const Vector2 &p_pos, MouseButton p_button);
|
||||
void _tree_empty_selected();
|
||||
|
||||
struct FileInfo {
|
||||
String name;
|
||||
String path;
|
||||
String icon_path;
|
||||
StringName type;
|
||||
Vector<String> sources;
|
||||
bool import_broken = false;
|
||||
uint64_t modified_time = 0;
|
||||
|
||||
bool operator<(const FileInfo &fi) const {
|
||||
return FileNoCaseComparator()(name, fi.name);
|
||||
}
|
||||
};
|
||||
|
||||
struct FileInfoTypeComparator;
|
||||
struct FileInfoModifiedTimeComparator;
|
||||
|
||||
void _sort_file_info_list(List<FileSystemDock::FileInfo> &r_file_list);
|
||||
|
||||
void _search(EditorFileSystemDirectory *p_path, List<FileInfo> *matches, int p_max_items);
|
||||
|
||||
void _set_current_path_line_edit_text(const String &p_path);
|
||||
|
@ -31,7 +31,6 @@
|
||||
#include "editor_file_dialog.h"
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/io/file_access.h"
|
||||
#include "core/os/keyboard.h"
|
||||
#include "core/os/os.h"
|
||||
#include "editor/dependency_editor.h"
|
||||
@ -183,6 +182,9 @@ void EditorFileDialog::_update_theme_item_cache() {
|
||||
theme_cache.favorites_down = get_editor_theme_icon(SNAME("MoveDown"));
|
||||
theme_cache.create_folder = get_editor_theme_icon(SNAME("FolderCreate"));
|
||||
|
||||
theme_cache.filter_box = get_editor_theme_icon(SNAME("Search"));
|
||||
theme_cache.file_sort_button = get_editor_theme_icon(SNAME("Sort"));
|
||||
|
||||
theme_cache.folder = get_editor_theme_icon(SNAME("Folder"));
|
||||
theme_cache.folder_icon_color = get_theme_color(SNAME("folder_icon_color"), SNAME("FileDialog"));
|
||||
|
||||
@ -326,6 +328,10 @@ void EditorFileDialog::shortcut_input(const Ref<InputEvent> &p_event) {
|
||||
dir->select_all();
|
||||
handled = true;
|
||||
}
|
||||
if (ED_IS_SHORTCUT("file_dialog/focus_filter", p_event)) {
|
||||
_focus_filter_box();
|
||||
handled = true;
|
||||
}
|
||||
if (ED_IS_SHORTCUT("file_dialog/move_favorite_up", p_event)) {
|
||||
_favorite_move_up();
|
||||
handled = true;
|
||||
@ -369,6 +375,8 @@ void EditorFileDialog::update_dir() {
|
||||
}
|
||||
dir->set_text(dir_access->get_current_dir(false));
|
||||
|
||||
filter_box->clear();
|
||||
|
||||
// Disable "Open" button only when selecting file(s) mode.
|
||||
get_ok_button()->set_disabled(_is_open_should_be_disabled());
|
||||
switch (mode) {
|
||||
@ -422,7 +430,13 @@ void EditorFileDialog::_post_popup() {
|
||||
item_list->grab_focus();
|
||||
}
|
||||
|
||||
if (mode == FILE_MODE_OPEN_DIR) {
|
||||
bool is_open_directory_mode = mode == FILE_MODE_OPEN_DIR;
|
||||
PopupMenu *p = file_sort_button->get_popup();
|
||||
p->set_item_disabled(2, is_open_directory_mode);
|
||||
p->set_item_disabled(3, is_open_directory_mode);
|
||||
p->set_item_disabled(4, is_open_directory_mode);
|
||||
p->set_item_disabled(5, is_open_directory_mode);
|
||||
if (is_open_directory_mode) {
|
||||
file_box->set_visible(false);
|
||||
} else {
|
||||
file_box->set_visible(true);
|
||||
@ -956,7 +970,7 @@ void EditorFileDialog::update_file_list() {
|
||||
|
||||
dir_access->list_dir_begin();
|
||||
|
||||
List<String> files;
|
||||
List<FileInfo> file_infos;
|
||||
List<String> dirs;
|
||||
|
||||
String item = dir_access->get_next();
|
||||
@ -967,27 +981,44 @@ void EditorFileDialog::update_file_list() {
|
||||
continue;
|
||||
}
|
||||
|
||||
bool matches_search = true;
|
||||
if (search_string.length() > 0) {
|
||||
matches_search = item.find(search_string) != -1;
|
||||
}
|
||||
|
||||
FileInfo file_info;
|
||||
file_info.name = item;
|
||||
file_info.path = cdir.path_join(file_info.name);
|
||||
file_info.type = item.get_extension();
|
||||
file_info.modified_time = FileAccess::get_modified_time(file_info.path);
|
||||
|
||||
if (matches_search) {
|
||||
if (show_hidden_files) {
|
||||
if (!dir_access->current_is_dir()) {
|
||||
files.push_back(item);
|
||||
file_infos.push_back(file_info);
|
||||
} else {
|
||||
dirs.push_back(item);
|
||||
dirs.push_back(file_info.name);
|
||||
}
|
||||
} else if (!dir_access->current_is_hidden()) {
|
||||
String full_path = cdir == "res://" ? item : dir_access->get_current_dir() + "/" + item;
|
||||
String full_path = cdir == "res://" ? file_info.name : dir_access->get_current_dir() + "/" + file_info.name;
|
||||
if (dir_access->current_is_dir()) {
|
||||
if (Engine::get_singleton()->is_project_manager_hint() || !EditorFileSystem::_should_skip_directory(full_path)) {
|
||||
dirs.push_back(item);
|
||||
dirs.push_back(file_info.name);
|
||||
}
|
||||
} else {
|
||||
files.push_back(item);
|
||||
file_infos.push_back(file_info);
|
||||
}
|
||||
}
|
||||
}
|
||||
item = dir_access->get_next();
|
||||
}
|
||||
|
||||
dirs.sort_custom<FileNoCaseComparator>();
|
||||
files.sort_custom<FileNoCaseComparator>();
|
||||
bool reverse_directories = file_sort == FileSortOption::FILE_SORT_NAME_REVERSE;
|
||||
if (reverse_directories) {
|
||||
dirs.reverse();
|
||||
}
|
||||
sort_file_info_list(file_infos, file_sort);
|
||||
|
||||
while (!dirs.is_empty()) {
|
||||
const String &dir_name = dirs.front()->get();
|
||||
@ -1037,25 +1068,26 @@ void EditorFileDialog::update_file_list() {
|
||||
}
|
||||
}
|
||||
|
||||
while (!files.is_empty()) {
|
||||
while (!file_infos.is_empty()) {
|
||||
bool match = patterns.is_empty();
|
||||
|
||||
FileInfo file_info = file_infos.front()->get();
|
||||
for (const String &E : patterns) {
|
||||
if (files.front()->get().matchn(E)) {
|
||||
if (file_info.name.matchn(E)) {
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (match) {
|
||||
item_list->add_item(files.front()->get());
|
||||
item_list->add_item(file_info.name);
|
||||
|
||||
if (get_icon_func) {
|
||||
Ref<Texture2D> icon = get_icon_func(cdir.path_join(files.front()->get()));
|
||||
Ref<Texture2D> icon = get_icon_func(file_info.path);
|
||||
if (display_mode == DISPLAY_THUMBNAILS) {
|
||||
Ref<Texture2D> thumbnail;
|
||||
if (get_thumbnail_func) {
|
||||
thumbnail = get_thumbnail_func(cdir.path_join(files.front()->get()));
|
||||
thumbnail = get_thumbnail_func(file_info.path);
|
||||
}
|
||||
if (thumbnail.is_null()) {
|
||||
thumbnail = file_thumbnail;
|
||||
@ -1069,22 +1101,21 @@ void EditorFileDialog::update_file_list() {
|
||||
}
|
||||
|
||||
Dictionary d;
|
||||
d["name"] = files.front()->get();
|
||||
d["name"] = file_info.name;
|
||||
d["dir"] = false;
|
||||
String fullpath = cdir.path_join(files.front()->get());
|
||||
d["path"] = fullpath;
|
||||
d["path"] = file_info.path;
|
||||
item_list->set_item_metadata(-1, d);
|
||||
|
||||
if (display_mode == DISPLAY_THUMBNAILS && previews_enabled) {
|
||||
EditorResourcePreview::get_singleton()->queue_resource_preview(fullpath, this, "_thumbnail_result", fullpath);
|
||||
EditorResourcePreview::get_singleton()->queue_resource_preview(file_info.path, this, "_thumbnail_result", file_info.path);
|
||||
}
|
||||
|
||||
if (file->get_text() == files.front()->get()) {
|
||||
if (file->get_text() == file_info.name) {
|
||||
item_list->set_current(item_list->get_item_count() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
files.pop_front();
|
||||
file_infos.pop_front();
|
||||
}
|
||||
|
||||
if (favorites->get_current() >= 0) {
|
||||
@ -1353,6 +1384,24 @@ void EditorFileDialog::_make_dir() {
|
||||
makedirname->grab_focus();
|
||||
}
|
||||
|
||||
void EditorFileDialog::_focus_filter_box() {
|
||||
filter_box->grab_focus();
|
||||
filter_box->select_all();
|
||||
}
|
||||
|
||||
void EditorFileDialog::_filter_changed(const String &p_text) {
|
||||
search_string = p_text;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
void EditorFileDialog::_file_sort_popup(int p_id) {
|
||||
for (int i = 0; i != static_cast<int>(FileSortOption::FILE_SORT_MAX); i++) {
|
||||
file_sort_button->get_popup()->set_item_checked(i, (i == p_id));
|
||||
}
|
||||
file_sort = static_cast<FileSortOption>(p_id);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
void EditorFileDialog::_delete_items() {
|
||||
// Collect the selected folders and files to delete and check them in the deletion dependency dialog.
|
||||
Vector<String> folders;
|
||||
@ -1444,6 +1493,10 @@ void EditorFileDialog::_update_icons() {
|
||||
show_hidden->set_icon(theme_cache.toggle_hidden);
|
||||
makedir->set_icon(theme_cache.create_folder);
|
||||
|
||||
filter_box->set_right_icon(theme_cache.filter_box);
|
||||
file_sort_button->set_icon(theme_cache.file_sort_button);
|
||||
filter_box->set_clear_button_enabled(true);
|
||||
|
||||
fav_up->set_icon(theme_cache.favorites_up);
|
||||
fav_down->set_icon(theme_cache.favorites_down);
|
||||
}
|
||||
@ -2093,6 +2146,7 @@ EditorFileDialog::EditorFileDialog() {
|
||||
// Allow both Cmd + L and Cmd + Shift + G to match Safari's and Finder's shortcuts respectively.
|
||||
ED_SHORTCUT_OVERRIDE_ARRAY("file_dialog/focus_path", "macos",
|
||||
{ int32_t(KeyModifierMask::META | Key::L), int32_t(KeyModifierMask::META | KeyModifierMask::SHIFT | Key::G) });
|
||||
ED_SHORTCUT("file_dialog/focus_filter", TTR("Focus Filter"), KeyModifierMask::CMD_OR_CTRL | Key::F);
|
||||
ED_SHORTCUT("file_dialog/move_favorite_up", TTR("Move Favorite Up"), KeyModifierMask::CMD_OR_CTRL | Key::UP);
|
||||
ED_SHORTCUT("file_dialog/move_favorite_down", TTR("Move Favorite Down"), KeyModifierMask::CMD_OR_CTRL | Key::DOWN);
|
||||
|
||||
@ -2256,11 +2310,37 @@ EditorFileDialog::EditorFileDialog() {
|
||||
VBoxContainer *list_vb = memnew(VBoxContainer);
|
||||
list_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
|
||||
HBoxContainer *lower_hb = memnew(HBoxContainer);
|
||||
lower_hb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
|
||||
l = memnew(Label(TTR("Directories & Files:")));
|
||||
l->set_theme_type_variation("HeaderSmall");
|
||||
list_vb->add_child(l);
|
||||
lower_hb->add_child(l);
|
||||
|
||||
list_vb->add_child(lower_hb);
|
||||
preview_hb->add_child(list_vb);
|
||||
|
||||
filter_box = memnew(LineEdit);
|
||||
filter_box->set_h_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
filter_box->set_placeholder(TTR("Filter"));
|
||||
filter_box->connect(SceneStringName(text_changed), callable_mp(this, &EditorFileDialog::_filter_changed));
|
||||
lower_hb->add_child(filter_box);
|
||||
|
||||
file_sort_button = memnew(MenuButton);
|
||||
file_sort_button->set_flat(true);
|
||||
file_sort_button->set_tooltip_text(TTR("Sort files"));
|
||||
|
||||
PopupMenu *p = file_sort_button->get_popup();
|
||||
p->connect(SceneStringName(id_pressed), callable_mp(this, &EditorFileDialog::_file_sort_popup));
|
||||
p->add_radio_check_item(TTR("Sort by Name (Ascending)"), static_cast<int>(FileSortOption::FILE_SORT_NAME));
|
||||
p->add_radio_check_item(TTR("Sort by Name (Descending)"), static_cast<int>(FileSortOption::FILE_SORT_NAME_REVERSE));
|
||||
p->add_radio_check_item(TTR("Sort by Type (Ascending)"), static_cast<int>(FileSortOption::FILE_SORT_TYPE));
|
||||
p->add_radio_check_item(TTR("Sort by Type (Descending)"), static_cast<int>(FileSortOption::FILE_SORT_TYPE_REVERSE));
|
||||
p->add_radio_check_item(TTR("Sort by Last Modified"), static_cast<int>(FileSortOption::FILE_SORT_MODIFIED_TIME));
|
||||
p->add_radio_check_item(TTR("Sort by First Modified"), static_cast<int>(FileSortOption::FILE_SORT_MODIFIED_TIME_REVERSE));
|
||||
p->set_item_checked(0, true);
|
||||
lower_hb->add_child(file_sort_button);
|
||||
|
||||
// Item (files and folders) list with context menu.
|
||||
|
||||
item_list = memnew(ItemList);
|
||||
|
@ -32,13 +32,15 @@
|
||||
#define EDITOR_FILE_DIALOG_H
|
||||
|
||||
#include "core/io/dir_access.h"
|
||||
#include "editor/file_info.h"
|
||||
#include "scene/gui/dialogs.h"
|
||||
#include "scene/property_list_helper.h"
|
||||
|
||||
class GridContainer;
|
||||
class DependencyRemoveDialog;
|
||||
class GridContainer;
|
||||
class HSplitContainer;
|
||||
class ItemList;
|
||||
class MenuButton;
|
||||
class OptionButton;
|
||||
class PopupMenu;
|
||||
class TextureRect;
|
||||
@ -127,6 +129,11 @@ private:
|
||||
Button *favorite = nullptr;
|
||||
Button *show_hidden = nullptr;
|
||||
|
||||
String search_string;
|
||||
LineEdit *filter_box = nullptr;
|
||||
FileSortOption file_sort = FileSortOption::FILE_SORT_NAME;
|
||||
MenuButton *file_sort_button = nullptr;
|
||||
|
||||
Button *fav_up = nullptr;
|
||||
Button *fav_down = nullptr;
|
||||
|
||||
@ -165,6 +172,9 @@ private:
|
||||
Ref<Texture2D> favorites_up;
|
||||
Ref<Texture2D> favorites_down;
|
||||
|
||||
Ref<Texture2D> filter_box;
|
||||
Ref<Texture2D> file_sort_button;
|
||||
|
||||
Ref<Texture2D> folder;
|
||||
Color folder_icon_color;
|
||||
|
||||
@ -227,6 +237,10 @@ private:
|
||||
void _make_dir();
|
||||
void _make_dir_confirm();
|
||||
|
||||
void _focus_filter_box();
|
||||
void _filter_changed(const String &p_text);
|
||||
void _file_sort_popup(int p_id);
|
||||
|
||||
void _delete_items();
|
||||
void _delete_files_global();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user