1
0
mirror of https://github.com/godotengine/godot.git synced 2025-11-20 14:45:44 +00:00

Change FileDialog's Tree to ItemList

This commit is contained in:
kobewi
2025-04-22 11:04:16 +02:00
parent 1cf573f44d
commit 7fe61c7e84
2 changed files with 118 additions and 162 deletions

View File

@@ -36,10 +36,10 @@
#include "scene/gui/box_container.h" #include "scene/gui/box_container.h"
#include "scene/gui/check_box.h" #include "scene/gui/check_box.h"
#include "scene/gui/grid_container.h" #include "scene/gui/grid_container.h"
#include "scene/gui/item_list.h"
#include "scene/gui/label.h" #include "scene/gui/label.h"
#include "scene/gui/line_edit.h" #include "scene/gui/line_edit.h"
#include "scene/gui/option_button.h" #include "scene/gui/option_button.h"
#include "scene/gui/tree.h"
#include "scene/theme/theme_db.h" #include "scene/theme/theme_db.h"
void FileDialog::popup_file_dialog() { void FileDialog::popup_file_dialog() {
@@ -340,19 +340,15 @@ void FileDialog::shortcut_input(const Ref<InputEvent> &p_event) {
} }
void FileDialog::set_enable_multiple_selection(bool p_enable) { void FileDialog::set_enable_multiple_selection(bool p_enable) {
tree->set_select_mode(p_enable ? Tree::SELECT_MULTI : Tree::SELECT_SINGLE); file_list->set_select_mode(p_enable ? ItemList::SELECT_MULTI : ItemList::SELECT_SINGLE);
} }
Vector<String> FileDialog::get_selected_files() const { Vector<String> FileDialog::get_selected_files() const {
const String current_dir = dir_access->get_current_dir();
Vector<String> list; Vector<String> list;
for (int idx : file_list->get_selected_items()) {
TreeItem *item = tree->get_root(); list.push_back(current_dir.path_join(file_list->get_item_text(idx)));
item = tree->get_next_selected(item);
while (item) {
list.push_back(dir_access->get_current_dir().path_join(item->get_text(0)));
item = tree->get_next_selected(item);
} }
return list; return list;
} }
@@ -410,7 +406,7 @@ void FileDialog::_post_popup() {
if (mode == FILE_MODE_SAVE_FILE) { if (mode == FILE_MODE_SAVE_FILE) {
filename_edit->grab_focus(); filename_edit->grab_focus();
} else { } else {
tree->grab_focus(); file_list->grab_focus();
} }
set_process_shortcut_input(true); set_process_shortcut_input(true);
@@ -441,20 +437,11 @@ void FileDialog::_push_history() {
void FileDialog::_action_pressed() { void FileDialog::_action_pressed() {
if (mode == FILE_MODE_OPEN_FILES) { if (mode == FILE_MODE_OPEN_FILES) {
TreeItem *ti = tree->get_next_selected(nullptr); const Vector<String> files = get_selected_files();
String fbase = dir_access->get_current_dir(); if (!files.is_empty()) {
Vector<String> files;
while (ti) {
files.push_back(fbase.path_join(ti->get_text(0)));
ti = tree->get_next_selected(ti);
}
if (files.size()) {
emit_signal(SNAME("files_selected"), files); emit_signal(SNAME("files_selected"), files);
hide(); hide();
} }
return; return;
} }
@@ -468,9 +455,9 @@ void FileDialog::_action_pressed() {
String path = dir_access->get_current_dir(); String path = dir_access->get_current_dir();
path = path.replace_char('\\', '/'); path = path.replace_char('\\', '/');
TreeItem *item = tree->get_selected(); int selected = _get_selected_file_idx();
if (item) { if (selected > -1) {
Dictionary d = item->get_metadata(0); Dictionary d = file_list->get_item_metadata(selected);
if (d["dir"] && d["name"] != "..") { if (d["dir"] && d["name"] != "..") {
path = path.path_join(d["name"]); path = path.path_join(d["name"]);
} }
@@ -554,24 +541,19 @@ bool FileDialog::_is_open_should_be_disabled() {
return false; return false;
} }
TreeItem *ti = tree->get_next_selected(tree->get_root()); Vector<int> items = file_list->get_selected_items();
while (ti) { if (items.is_empty()) {
TreeItem *prev_ti = ti;
ti = tree->get_next_selected(tree->get_root());
if (ti == prev_ti) {
break;
}
}
// We have something that we can't select?
if (!ti) {
return mode != FILE_MODE_OPEN_DIR; // In "Open folder" mode, having nothing selected picks the current folder. return mode != FILE_MODE_OPEN_DIR; // In "Open folder" mode, having nothing selected picks the current folder.
} }
Dictionary d = ti->get_metadata(0); for (const int idx : items) {
Dictionary d = file_list->get_item_metadata(idx);
// Opening a file, but selected a folder? Forbidden. if (((mode == FILE_MODE_OPEN_FILE || mode == FILE_MODE_OPEN_FILES) && d["dir"]) || (mode == FILE_MODE_OPEN_DIR && !d["dir"])) {
return ((mode == FILE_MODE_OPEN_FILE || mode == FILE_MODE_OPEN_FILES) && d["dir"]) || // Flipped case, also forbidden. return true;
(mode == FILE_MODE_OPEN_DIR && !d["dir"]); }
}
return false;
} }
void FileDialog::_go_up() { void FileDialog::_go_up() {
@@ -605,10 +587,9 @@ void FileDialog::_go_forward() {
void FileDialog::deselect_all() { void FileDialog::deselect_all() {
// Clear currently selected items in file manager. // Clear currently selected items in file manager.
tree->deselect_all(); file_list->deselect_all();
// And change get_ok title. // And change get_ok title.
if (!tree->is_anything_selected()) {
get_ok_button()->set_disabled(_is_open_should_be_disabled()); get_ok_button()->set_disabled(_is_open_should_be_disabled());
switch (mode) { switch (mode) {
@@ -626,19 +607,23 @@ void FileDialog::deselect_all() {
set_ok_button_text(ETR("Save")); set_ok_button_text(ETR("Save"));
break; break;
} }
}
int FileDialog::_get_selected_file_idx() {
const PackedInt32Array selected = file_list->get_selected_items();
return selected.is_empty() ? -1 : selected[0];
}
void FileDialog::_file_list_multi_selected(int p_item, bool p_selected) {
if (p_selected) {
_file_list_selected(p_item);
} else {
get_ok_button()->set_disabled(_is_open_should_be_disabled());
} }
} }
void FileDialog::_tree_multi_selected(Object *p_object, int p_cell, bool p_selected) { void FileDialog::_file_list_selected(int p_item) {
_tree_selected(); Dictionary d = file_list->get_item_metadata(p_item);
}
void FileDialog::_tree_selected() {
TreeItem *ti = tree->get_selected();
if (!ti) {
return;
}
Dictionary d = ti->get_metadata(0);
if (!d["dir"]) { if (!d["dir"]) {
filename_edit->set_text(d["name"]); filename_edit->set_text(d["name"]);
@@ -657,13 +642,8 @@ void FileDialog::_tree_selected() {
get_ok_button()->set_disabled(_is_open_should_be_disabled()); get_ok_button()->set_disabled(_is_open_should_be_disabled());
} }
void FileDialog::_tree_item_activated() { void FileDialog::_file_list_item_activated(int p_item) {
TreeItem *ti = tree->get_selected(); Dictionary d = file_list->get_item_metadata(p_item);
if (!ti) {
return;
}
Dictionary d = ti->get_metadata(0);
if (d["dir"]) { if (d["dir"]) {
_change_dir(d["name"]); _change_dir(d["name"]);
@@ -698,10 +678,10 @@ void FileDialog::update_file_name() {
void FileDialog::_item_menu_id_pressed(int p_option) { void FileDialog::_item_menu_id_pressed(int p_option) {
switch (p_option) { switch (p_option) {
case ITEM_MENU_SHOW_IN_EXPLORER: { case ITEM_MENU_SHOW_IN_EXPLORER: {
TreeItem *ti = tree->get_selected();
String path; String path;
if (ti) { int selected = _get_selected_file_idx();
Dictionary d = ti->get_metadata(0); if (selected > -1) {
Dictionary d = file_list->get_item_metadata(selected);
path = ProjectSettings::get_singleton()->globalize_path(dir_access->get_current_dir().path_join(d["name"])); path = ProjectSettings::get_singleton()->globalize_path(dir_access->get_current_dir().path_join(d["name"]));
} else { } else {
path = ProjectSettings::get_singleton()->globalize_path(dir_access->get_current_dir()); path = ProjectSettings::get_singleton()->globalize_path(dir_access->get_current_dir());
@@ -711,11 +691,11 @@ void FileDialog::_item_menu_id_pressed(int p_option) {
} break; } break;
case ITEM_MENU_SHOW_BUNDLE_CONTENT: { case ITEM_MENU_SHOW_BUNDLE_CONTENT: {
TreeItem *ti = tree->get_selected(); int selected = _get_selected_file_idx();
if (!ti) { if (selected == -1) {
return; return;
} }
Dictionary d = ti->get_metadata(0); Dictionary d = file_list->get_item_metadata(selected);
_change_dir(d["name"]); _change_dir(d["name"]);
if (mode == FILE_MODE_OPEN_FILE || mode == FILE_MODE_OPEN_FILES || mode == FILE_MODE_OPEN_DIR || mode == FILE_MODE_OPEN_ANY) { if (mode == FILE_MODE_OPEN_FILE || mode == FILE_MODE_OPEN_FILES || mode == FILE_MODE_OPEN_DIR || mode == FILE_MODE_OPEN_ANY) {
filename_edit->set_text(""); filename_edit->set_text("");
@@ -732,42 +712,38 @@ void FileDialog::_empty_clicked(const Vector2 &p_pos, MouseButton p_button) {
// Opening the system file manager is not supported on the Android and web editors. // Opening the system file manager is not supported on the Android and web editors.
item_menu->add_item(ETR("Open in File Manager"), ITEM_MENU_SHOW_IN_EXPLORER); item_menu->add_item(ETR("Open in File Manager"), ITEM_MENU_SHOW_IN_EXPLORER);
item_menu->set_position(tree->get_screen_position() + p_pos); item_menu->set_position(file_list->get_screen_position() + p_pos);
item_menu->reset_size(); item_menu->reset_size();
item_menu->popup(); item_menu->popup();
#endif #endif
} else if (p_button == MouseButton::LEFT) {
deselect_all();
} }
} }
void FileDialog::_rmb_select(const Vector2 &p_pos, MouseButton p_button) { void FileDialog::_item_clicked(int p_item, const Vector2 &p_pos, MouseButton p_button) {
if (p_button == MouseButton::RIGHT) { if (p_button == MouseButton::RIGHT) {
item_menu->clear(); item_menu->clear();
#if !defined(ANDROID_ENABLED) && !defined(WEB_ENABLED) #if !defined(ANDROID_ENABLED) && !defined(WEB_ENABLED)
// Opening the system file manager is not supported on the Android and web editors. // Opening the system file manager is not supported on the Android and web editors.
TreeItem *ti = tree->get_selected(); Dictionary d = file_list->get_item_metadata(p_item);
if (!ti) {
return;
}
Dictionary d = ti->get_metadata(0);
if (d["bundle"]) { if (d["bundle"]) {
item_menu->add_item(ETR("Show Package Contents"), ITEM_MENU_SHOW_BUNDLE_CONTENT); item_menu->add_item(ETR("Show Package Contents"), ITEM_MENU_SHOW_BUNDLE_CONTENT);
} }
item_menu->add_item(ETR("Open in File Manager"), ITEM_MENU_SHOW_IN_EXPLORER); item_menu->add_item(ETR("Open in File Manager"), ITEM_MENU_SHOW_IN_EXPLORER);
item_menu->set_position(tree->get_screen_position() + p_pos); item_menu->set_position(file_list->get_screen_position() + p_pos);
item_menu->reset_size(); item_menu->reset_size();
item_menu->popup(); item_menu->popup();
#endif #endif
} else {
_tree_selected();
} }
} }
void FileDialog::update_file_list() { void FileDialog::update_file_list() {
tree->clear(); file_list->clear();
// Scroll back to the top after opening a directory // Scroll back to the top after opening a directory
tree->get_vscroll_bar()->set_value(0); file_list->get_v_scroll_bar()->set_value(0);
dir_access->list_dir_begin(); dir_access->list_dir_begin();
@@ -778,9 +754,8 @@ void FileDialog::update_file_list() {
message->show(); message->show();
} }
TreeItem *root = tree->create_item(); LocalVector<String> files;
List<String> files; LocalVector<String> dirs;
List<String> dirs;
bool is_hidden; bool is_hidden;
String item = dir_access->get_next(); String item = dir_access->get_next();
@@ -834,9 +809,7 @@ void FileDialog::update_file_list() {
} }
} }
while (!dirs.is_empty()) { for (const String &dir_name : dirs) {
const String &dir_name = dirs.front()->get();
bool bundle = dir_access->is_bundle(dir_name); bool bundle = dir_access->is_bundle(dir_name);
bool found = true; bool found = true;
if (bundle) { if (bundle) {
@@ -851,72 +824,56 @@ void FileDialog::update_file_list() {
} }
if (found && (filename_filter_lower.is_empty() || dir_name.to_lower().contains(filename_filter_lower))) { if (found && (filename_filter_lower.is_empty() || dir_name.to_lower().contains(filename_filter_lower))) {
TreeItem *ti = tree->create_item(root); file_list->add_item(dir_name, theme_cache.folder);
file_list->set_item_icon_modulate(-1, theme_cache.folder_icon_color);
ti->set_text(0, dir_name);
ti->set_icon(0, theme_cache.folder);
ti->set_icon_modulate(0, theme_cache.folder_icon_color);
Dictionary d; Dictionary d;
d["name"] = dir_name; d["name"] = dir_name;
d["dir"] = !bundle; d["dir"] = !bundle;
d["bundle"] = bundle; d["bundle"] = bundle;
file_list->set_item_metadata(-1, d);
ti->set_metadata(0, d);
} }
dirs.pop_front();
} }
String base_dir = dir_access->get_current_dir(); String base_dir = dir_access->get_current_dir();
while (!files.is_empty()) { for (const String &filename : files) {
bool match = patterns.is_empty(); bool match = patterns.is_empty();
String match_str; String match_str;
for (const String &E : patterns) { for (const String &E : patterns) {
if (files.front()->get().matchn(E)) { if (filename.matchn(E)) {
match_str = E; match_str = E;
match = true; match = true;
break; break;
} }
} }
if (match && (filename_filter_lower.is_empty() || files.front()->get().to_lower().contains(filename_filter_lower))) { if (match && (filename_filter_lower.is_empty() || filename.to_lower().contains(filename_filter_lower))) {
TreeItem *ti = tree->create_item(root); const Ref<Texture2D> icon = get_icon_func ? get_icon_func(base_dir.path_join(filename)) : theme_cache.file;
ti->set_text(0, files.front()->get()); file_list->add_item(filename, icon);
file_list->set_item_icon_modulate(-1, theme_cache.file_icon_color);
if (get_icon_func) {
Ref<Texture2D> icon = get_icon_func(base_dir.path_join(files.front()->get()));
ti->set_icon(0, icon);
} else {
ti->set_icon(0, theme_cache.file);
}
ti->set_icon_modulate(0, theme_cache.file_icon_color);
if (mode == FILE_MODE_OPEN_DIR) { if (mode == FILE_MODE_OPEN_DIR) {
ti->set_custom_color(0, theme_cache.file_disabled_color); file_list->set_item_disabled(-1, true);
ti->set_selectable(0, false);
} }
Dictionary d; Dictionary d;
d["name"] = files.front()->get(); d["name"] = filename;
d["dir"] = false; d["dir"] = false;
d["bundle"] = false; d["bundle"] = false;
ti->set_metadata(0, d); file_list->set_item_metadata(-1, d);
if (filename_edit->get_text() == files.front()->get() || match_str == files.front()->get()) { if (filename_edit->get_text() == filename || match_str == filename) {
ti->select(0); file_list->select(file_list->get_item_count() - 1);
} }
} }
files.pop_front();
} }
if (mode != FILE_MODE_SAVE_FILE && mode != FILE_MODE_OPEN_DIR) { if (mode != FILE_MODE_SAVE_FILE && mode != FILE_MODE_OPEN_DIR) {
// Select the first file from list if nothing is selected. // Select the first file from list if nothing is selected.
if (tree->get_root() && tree->get_root()->get_first_child() && tree->get_selected() == nullptr) { int selected = _get_selected_file_idx();
tree->get_root()->get_first_child()->select(0); if (selected == -1) {
_tree_selected(); _file_list_select_first();
} }
} }
} }
@@ -929,20 +886,20 @@ void FileDialog::_filter_selected(int) {
void FileDialog::_filename_filter_changed() { void FileDialog::_filename_filter_changed() {
update_filename_filter(); update_filename_filter();
update_file_list(); update_file_list();
callable_mp(this, &FileDialog::_tree_select_first).call_deferred(); callable_mp(this, &FileDialog::_file_list_select_first).call_deferred();
} }
void FileDialog::_tree_select_first() { void FileDialog::_file_list_select_first() {
if (tree->get_root() && tree->get_root()->get_first_child()) { if (file_list->get_item_count() > 0) {
tree->get_root()->get_first_child()->select(0); file_list->select(0);
_tree_selected(); _file_list_selected(0);
} }
} }
void FileDialog::_filename_filter_selected() { void FileDialog::_filename_filter_selected() {
TreeItem *item = tree->get_selected(); int selected = _get_selected_file_idx();
if (item) { if (selected > -1) {
filename_edit->set_text(item->get_text(0)); filename_edit->set_text(file_list->get_item_text(selected));
filename_edit->emit_signal(SceneStringName(text_submitted), filename_edit->get_text()); filename_edit->emit_signal(SceneStringName(text_submitted), filename_edit->get_text());
} }
} }
@@ -1217,9 +1174,9 @@ void FileDialog::set_file_mode(FileMode p_mode) {
} }
if (mode == FILE_MODE_OPEN_FILES) { if (mode == FILE_MODE_OPEN_FILES) {
tree->set_select_mode(Tree::SELECT_MULTI); file_list->set_select_mode(ItemList::SELECT_MULTI);
} else { } else {
tree->set_select_mode(Tree::SELECT_SINGLE); file_list->set_select_mode(ItemList::SELECT_SINGLE);
} }
get_ok_button()->set_disabled(_is_open_should_be_disabled()); get_ok_button()->set_disabled(_is_open_should_be_disabled());
@@ -1635,7 +1592,7 @@ void FileDialog::set_show_filename_filter(bool p_show) {
filename_filter->grab_focus(); filename_filter->grab_focus();
} else { } else {
if (filename_filter->has_focus()) { if (filename_filter->has_focus()) {
tree->call_deferred("grab_focus"); callable_mp((Control *)file_list, &Control::grab_focus).call_deferred();
} }
} }
show_filename_filter = p_show; show_filename_filter = p_show;
@@ -1772,19 +1729,17 @@ FileDialog::FileDialog() {
main_vbox->add_child(label); main_vbox->add_child(label);
} }
tree = memnew(Tree); file_list = memnew(ItemList);
tree->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED); file_list->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
tree->set_v_size_flags(Control::SIZE_EXPAND_FILL); file_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
tree->set_accessibility_name(ETR("Directories and Files")); file_list->set_accessibility_name(ETR("Directories and Files"));
tree->set_hide_root(true); file_list->set_allow_rmb_select(true);
tree->set_allow_rmb_select(true); main_vbox->add_child(file_list);
main_vbox->add_child(tree); file_list->connect("multi_selected", callable_mp(this, &FileDialog::_file_list_multi_selected));
tree->connect("multi_selected", callable_mp(this, &FileDialog::_tree_multi_selected), CONNECT_DEFERRED); file_list->connect("item_selected", callable_mp(this, &FileDialog::_file_list_selected));
tree->connect("cell_selected", callable_mp(this, &FileDialog::_tree_selected), CONNECT_DEFERRED); file_list->connect("item_activated", callable_mp(this, &FileDialog::_file_list_item_activated));
tree->connect("item_activated", callable_mp(this, &FileDialog::_tree_item_activated)); file_list->connect("item_clicked", callable_mp(this, &FileDialog::_item_clicked));
tree->connect("nothing_selected", callable_mp(this, &FileDialog::deselect_all)); file_list->connect("empty_clicked", callable_mp(this, &FileDialog::_empty_clicked));
tree->connect("item_mouse_selected", callable_mp(this, &FileDialog::_rmb_select));
tree->connect("empty_clicked", callable_mp(this, &FileDialog::_empty_clicked));
message = memnew(Label); message = memnew(Label);
message->set_focus_mode(Control::FOCUS_ACCESSIBILITY); message->set_focus_mode(Control::FOCUS_ACCESSIBILITY);
@@ -1792,7 +1747,7 @@ FileDialog::FileDialog() {
message->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT); message->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
message->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER); message->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
message->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER); message->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER);
tree->add_child(message); file_list->add_child(message);
filename_filter_box = memnew(HBoxContainer); filename_filter_box = memnew(HBoxContainer);
filename_filter_box->set_visible(false); filename_filter_box->set_visible(false);

View File

@@ -36,11 +36,11 @@
class DirAccess; class DirAccess;
class GridContainer; class GridContainer;
class HBoxContainer; class HBoxContainer;
class ItemList;
class LineEdit; class LineEdit;
class OptionButton; class OptionButton;
class PopupMenu; class PopupMenu;
class VBoxContainer; class VBoxContainer;
class Tree;
class FileDialog : public ConfirmationDialog { class FileDialog : public ConfirmationDialog {
GDCLASS(FileDialog, ConfirmationDialog); GDCLASS(FileDialog, ConfirmationDialog);
@@ -127,7 +127,7 @@ private:
Button *show_filename_filter_button = nullptr; Button *show_filename_filter_button = nullptr;
Button *make_dir_button = nullptr; Button *make_dir_button = nullptr;
Tree *tree = nullptr; ItemList *file_list = nullptr;
Label *message = nullptr; Label *message = nullptr;
PopupMenu *item_menu = nullptr; PopupMenu *item_menu = nullptr;
@@ -176,15 +176,16 @@ private:
void _item_menu_id_pressed(int p_option); void _item_menu_id_pressed(int p_option);
void _empty_clicked(const Vector2 &p_pos, MouseButton p_button); void _empty_clicked(const Vector2 &p_pos, MouseButton p_button);
void _rmb_select(const Vector2 &p_pos, MouseButton p_button = MouseButton::RIGHT); void _item_clicked(int p_item, const Vector2 &p_pos, MouseButton p_button);
void _focus_file_text(); void _focus_file_text();
void _tree_multi_selected(Object *p_object, int p_cell, bool p_selected); int _get_selected_file_idx();
void _tree_selected(); void _file_list_multi_selected(int p_item, bool p_selected);
void _file_list_selected(int p_item);
void _file_list_item_activated(int p_item);
void _select_drive(int p_idx); void _select_drive(int p_idx);
void _tree_item_activated();
void _dir_submitted(String p_dir); void _dir_submitted(String p_dir);
void _file_submitted(const String &p_file); void _file_submitted(const String &p_file);
void _action_pressed(); void _action_pressed();
@@ -193,7 +194,7 @@ private:
void _filter_selected(int); void _filter_selected(int);
void _filename_filter_changed(); void _filename_filter_changed();
void _filename_filter_selected(); void _filename_filter_selected();
void _tree_select_first(); void _file_list_select_first();
void _make_dir(); void _make_dir();
void _make_dir_confirm(); void _make_dir_confirm();
void _go_up(); void _go_up();