Skip to content

Commit 45dfd4d

Browse files
committed
Merge pull request #108147 from KoBeWi/set_get_is_back
Expose FileDialog callbacks for getting custom icons
2 parents 1df6a40 + f88b519 commit 45dfd4d

File tree

6 files changed

+138
-13
lines changed

6 files changed

+138
-13
lines changed

doc/classes/FileDialog.xml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,34 @@
131131
[b]Note:[/b] [FileDialog] will update its internal [ItemList] of favorites when its visibility changes. Be sure to call this method earlier if you want your changes to have effect.
132132
</description>
133133
</method>
134+
<method name="set_get_icon_callback" qualifiers="static">
135+
<return type="void" />
136+
<param index="0" name="callback" type="Callable" />
137+
<description>
138+
Sets the callback used by the [FileDialog] nodes to get a file icon, when [constant DISPLAY_LIST] mode is used. The callback should take a single [String] argument (file path), and return a [Texture2D]. If an invalid texture is returned, the [theme_item file] icon will be used instead.
139+
</description>
140+
</method>
141+
<method name="set_get_thumbnail_callback" qualifiers="static">
142+
<return type="void" />
143+
<param index="0" name="callback" type="Callable" />
144+
<description>
145+
Sets the callback used by the [FileDialog] nodes to get a file icon, when [constant DISPLAY_THUMBNAILS] mode is used. The callback should take a single [String] argument (file path), and return a [Texture2D]. If an invalid texture is returned, the [theme_item file_thumbnail] icon will be used instead.
146+
Thumbnails are usually more complex and may take a while to load. To avoid stalling the application, you can use [ImageTexture] to asynchronously create the thumbnail.
147+
[codeblock]
148+
func _ready():
149+
FileDialog.set_get_thumbnail_callback(thumbnail_method)
150+
151+
func thumbnail_method(path):
152+
var image_texture = ImageTexture.new()
153+
make_thumbnail_async(path, image_texture)
154+
return image_texture
155+
156+
func make_thumbnail_async(path, image_texture):
157+
var thumbnail_texture = await generate_thumbnail(path) # Some method that generates a thumbnail.
158+
image_texture.set_image(thumbnail_texture.get_image())
159+
[/codeblock]
160+
</description>
161+
</method>
134162
<method name="set_option_default">
135163
<return type="void" />
136164
<param index="0" name="option" type="int" />

editor/editor_node.cpp

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -687,6 +687,10 @@ void EditorNode::_update_theme(bool p_skip_creation) {
687687
_update_renderer_color();
688688
}
689689

690+
Ref<Texture2D> thumbnail_icon = gui_base->get_theme_icon(SNAME("file_thumbnail"), SNAME("FileDialog"));
691+
default_thumbnail.instantiate();
692+
default_thumbnail->set_image(thumbnail_icon->get_image());
693+
690694
editor_dock_manager->update_tab_styles();
691695
editor_dock_manager->update_docks_menu();
692696
editor_dock_manager->set_tab_icon_max_width(theme->get_constant(SNAME("class_icon_size"), EditorStringName(Editor)));
@@ -5625,6 +5629,19 @@ Ref<Texture2D> EditorNode::_file_dialog_get_icon(const String &p_path) {
56255629
return singleton->icon_type_cache["Object"];
56265630
}
56275631

5632+
Ref<Texture2D> EditorNode::_file_dialog_get_thumbnail(const String &p_path) {
5633+
Ref<ImageTexture> texture = singleton->default_thumbnail->duplicate();
5634+
EditorResourcePreview::get_singleton()->queue_resource_preview(p_path, callable_mp_static(EditorNode::_file_dialog_thumbnail_callback).bind(texture));
5635+
return texture;
5636+
}
5637+
5638+
void EditorNode::_file_dialog_thumbnail_callback(const String &p_path, const Ref<Texture2D> &p_preview, const Ref<Texture2D> &p_small_preview, Ref<ImageTexture> p_texture) {
5639+
ERR_FAIL_COND(p_texture.is_null());
5640+
if (p_preview.is_valid()) {
5641+
p_texture->set_image(p_preview->get_image());
5642+
}
5643+
}
5644+
56285645
void EditorNode::_build_icon_type_cache() {
56295646
List<StringName> tl;
56305647
theme->get_icon_list(EditorStringName(EditorIcons), &tl);
@@ -7817,7 +7834,8 @@ EditorNode::EditorNode() {
78177834
EditorContextMenuPluginManager::create();
78187835

78197836
// Used for previews.
7820-
FileDialog::get_icon_func = _file_dialog_get_icon;
7837+
FileDialog::set_get_icon_callback(callable_mp_static(_file_dialog_get_icon));
7838+
FileDialog::set_get_thumbnail_callback(callable_mp_static(_file_dialog_get_thumbnail));
78217839
FileDialog::register_func = _file_dialog_register;
78227840
FileDialog::unregister_func = _file_dialog_unregister;
78237841

@@ -8919,7 +8937,6 @@ EditorNode::~EditorNode() {
89198937
GDExtensionEditorPlugins::editor_node_add_plugin = nullptr;
89208938
GDExtensionEditorPlugins::editor_node_remove_plugin = nullptr;
89218939

8922-
FileDialog::get_icon_func = nullptr;
89238940
FileDialog::register_func = nullptr;
89248941
FileDialog::unregister_func = nullptr;
89258942

editor/editor_node.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ class ConfirmationDialog;
4646
class Control;
4747
class FileDialog;
4848
class HBoxContainer;
49+
class ImageTexture;
4950
class MenuBar;
5051
class MenuButton;
5152
class OptionButton;
@@ -512,6 +513,10 @@ class EditorNode : public Node {
512513
}
513514

514515
static Ref<Texture2D> _file_dialog_get_icon(const String &p_path);
516+
static Ref<Texture2D> _file_dialog_get_thumbnail(const String &p_path);
517+
static void _file_dialog_thumbnail_callback(const String &p_path, const Ref<Texture2D> &p_preview, const Ref<Texture2D> &p_small_preview, Ref<ImageTexture> p_texture);
518+
Ref<ImageTexture> default_thumbnail;
519+
515520
static void _file_dialog_register(FileDialog *p_dialog);
516521
static void _file_dialog_unregister(FileDialog *p_dialog);
517522
static void _editor_file_dialog_register(EditorFileDialog *p_dialog);

scene/gui/file_dialog.cpp

Lines changed: 67 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -577,6 +577,24 @@ bool FileDialog::_is_open_should_be_disabled() {
577577
return false;
578578
}
579579

580+
void FileDialog::_thumbnail_callback(const Ref<Texture2D> &p_texture, const String &p_path) {
581+
if (display_mode == DISPLAY_LIST || p_texture.is_null()) {
582+
return;
583+
}
584+
585+
if (!p_path.begins_with(full_dir)) {
586+
return;
587+
}
588+
589+
const String file_name = p_path.get_file();
590+
for (int i = 0; i < file_list->get_item_count(); i++) {
591+
if (file_list->get_item_text(i) == file_name) {
592+
file_list->set_item_icon(i, p_texture);
593+
break;
594+
}
595+
}
596+
}
597+
580598
void FileDialog::_go_up() {
581599
_change_dir("..");
582600
_push_history();
@@ -778,7 +796,7 @@ void FileDialog::update_file_list() {
778796
file_list->set_max_columns(1);
779797
file_list->set_max_text_lines(1);
780798
file_list->set_fixed_column_width(0);
781-
file_list->set_fixed_icon_size(Size2());
799+
file_list->set_fixed_icon_size(theme_cache.file->get_size());
782800
}
783801

784802
dir_access->list_dir_begin();
@@ -948,13 +966,44 @@ void FileDialog::update_file_list() {
948966

949967
for (const FileInfo &info : filtered_files) {
950968
file_list->add_item(info.name);
951-
if (get_icon_func) {
952-
Ref<Texture2D> icon = get_icon_func(base_dir.path_join(info.name));
969+
const String path = base_dir.path_join(info.name);
970+
971+
if (display_mode == DISPLAY_LIST) {
972+
Ref<Texture2D> icon;
973+
if (get_icon_callback.is_valid()) {
974+
const Variant &v = path;
975+
const Variant *argptrs[1] = { &v };
976+
Variant vicon;
977+
978+
Callable::CallError ce;
979+
get_icon_callback.callp(argptrs, 1, vicon, ce);
980+
if (unlikely(ce.error != Callable::CallError::CALL_OK)) {
981+
ERR_PRINT(vformat("Error calling FileDialog's icon callback: %s.", Variant::get_callable_error_text(get_icon_callback, argptrs, 1, ce)));
982+
}
983+
icon = vicon;
984+
}
985+
if (icon.is_null()) {
986+
icon = theme_cache.file;
987+
}
988+
file_list->set_item_icon(-1, icon);
989+
} else { // DISPLAY_THUMBNAILS
990+
Ref<Texture2D> icon;
991+
if (get_thumbnail_callback.is_valid()) {
992+
const Variant &v = path;
993+
const Variant *argptrs[1] = { &v };
994+
Variant vicon;
995+
996+
Callable::CallError ce;
997+
get_thumbnail_callback.callp(argptrs, 1, vicon, ce);
998+
if (unlikely(ce.error != Callable::CallError::CALL_OK)) {
999+
ERR_PRINT(vformat("Error calling FileDialog's thumbnail callback: %s.", Variant::get_callable_error_text(get_thumbnail_callback, argptrs, 1, ce)));
1000+
}
1001+
icon = vicon;
1002+
}
1003+
if (icon.is_null()) {
1004+
icon = theme_cache.file;
1005+
}
9531006
file_list->set_item_icon(-1, icon);
954-
} else if (display_mode == DISPLAY_THUMBNAILS) {
955-
file_list->set_item_icon(-1, theme_cache.file_thumbnail);
956-
} else {
957-
file_list->set_item_icon(-1, theme_cache.file);
9581007
}
9591008
file_list->set_item_icon_modulate(-1, theme_cache.file_icon_color);
9601009

@@ -1983,6 +2032,8 @@ void FileDialog::_bind_methods() {
19832032
ClassDB::bind_static_method("FileDialog", D_METHOD("get_favorite_list"), &FileDialog::get_favorite_list);
19842033
ClassDB::bind_static_method("FileDialog", D_METHOD("set_recent_list", "recents"), &FileDialog::set_recent_list);
19852034
ClassDB::bind_static_method("FileDialog", D_METHOD("get_recent_list"), &FileDialog::get_recent_list);
2035+
ClassDB::bind_static_method("FileDialog", D_METHOD("set_get_icon_callback", "callback"), &FileDialog::set_get_icon_callback);
2036+
ClassDB::bind_static_method("FileDialog", D_METHOD("set_get_thumbnail_callback", "callback"), &FileDialog::set_get_thumbnail_callback);
19862037

19872038
ClassDB::bind_method(D_METHOD("invalidate"), &FileDialog::invalidate);
19882039

@@ -2118,6 +2169,14 @@ void FileDialog::set_default_show_hidden_files(bool p_show) {
21182169
default_show_hidden_files = p_show;
21192170
}
21202171

2172+
void FileDialog::set_get_icon_callback(const Callable &p_callback) {
2173+
get_icon_callback = p_callback;
2174+
}
2175+
2176+
void FileDialog::set_get_thumbnail_callback(const Callable &p_callback) {
2177+
get_thumbnail_callback = p_callback;
2178+
}
2179+
21212180
void FileDialog::set_use_native_dialog(bool p_native) {
21222181
use_native_dialog = p_native;
21232182

@@ -2143,6 +2202,7 @@ FileDialog::FileDialog() {
21432202
set_hide_on_ok(false);
21442203
set_size(Size2(640, 360));
21452204
set_default_ok_text(ETR("Save")); // Default mode text.
2205+
thumbnail_callback = callable_mp(this, &FileDialog::_thumbnail_callback);
21462206

21472207
for (int i = 0; i < CUSTOMIZATION_MAX; i++) {
21482208
customization_flags[i] = true;

scene/gui/file_dialog.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,10 +146,11 @@ class FileDialog : public ConfirmationDialog {
146146
CUSTOMIZATION_MAX
147147
};
148148

149-
typedef Ref<Texture2D> (*GetIconFunc)(const String &);
150149
typedef void (*RegisterFunc)(FileDialog *);
151150

152-
inline static GetIconFunc get_icon_func = nullptr;
151+
inline static Callable get_icon_callback;
152+
inline static Callable get_thumbnail_callback;
153+
153154
inline static RegisterFunc register_func = nullptr;
154155
inline static RegisterFunc unregister_func = nullptr;
155156

@@ -189,6 +190,7 @@ class FileDialog : public ConfirmationDialog {
189190
String root_prefix;
190191
String full_dir;
191192

193+
Callable thumbnail_callback;
192194
bool is_invalidating = false;
193195

194196
VBoxContainer *main_vbox = nullptr;
@@ -338,6 +340,7 @@ class FileDialog : public ConfirmationDialog {
338340
void _native_dialog_cb_with_options(bool p_ok, const Vector<String> &p_files, int p_filter, const Dictionary &p_selected_options);
339341

340342
bool _is_open_should_be_disabled();
343+
void _thumbnail_callback(const Ref<Texture2D> &p_texture, const String &p_path);
341344

342345
TypedArray<Dictionary> _get_options() const;
343346
void _update_option_controls();
@@ -430,6 +433,9 @@ class FileDialog : public ConfirmationDialog {
430433

431434
static void set_default_show_hidden_files(bool p_show);
432435

436+
static void set_get_icon_callback(const Callable &p_callback);
437+
static void set_get_thumbnail_callback(const Callable &p_callback);
438+
433439
void invalidate();
434440

435441
void deselect_all();

scene/gui/item_list.cpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -208,11 +208,20 @@ void ItemList::set_item_icon(int p_idx, const Ref<Texture2D> &p_icon) {
208208
}
209209
ERR_FAIL_INDEX(p_idx, items.size());
210210

211-
if (items[p_idx].icon == p_icon) {
211+
Item &item = items.write[p_idx];
212+
if (item.icon == p_icon) {
212213
return;
213214
}
214215

215-
items.write[p_idx].icon = p_icon;
216+
const Callable redraw = callable_mp((CanvasItem *)this, &CanvasItem::queue_redraw);
217+
if (item.icon.is_valid()) {
218+
item.icon->disconnect_changed(redraw);
219+
}
220+
item.icon = p_icon;
221+
if (p_icon.is_valid()) {
222+
p_icon->connect_changed(redraw);
223+
}
224+
216225
queue_redraw();
217226
shape_changed = true;
218227
}

0 commit comments

Comments
 (0)