2014-02-09 22:10:30 -03:00
/**************************************************************************/
/* file_dialog.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. */
/**************************************************************************/
2018-01-05 00:50:27 +01:00
2014-02-09 22:10:30 -03:00
# include "file_dialog.h"
2018-11-11 11:30:53 +01:00
2023-10-13 12:37:46 +03:00
# include "core/config/project_settings.h"
2025-04-22 00:23:16 +02:00
# include "core/io/dir_access.h"
2025-10-05 17:05:56 +09:00
# include "core/io/file_access.h"
2018-09-11 18:13:45 +02:00
# include "core/os/keyboard.h"
2025-04-22 00:23:16 +02:00
# include "scene/gui/box_container.h"
2023-10-13 12:37:46 +03:00
# include "scene/gui/check_box.h"
2025-04-22 15:32:34 +02:00
# include "scene/gui/flow_container.h"
2023-10-13 12:37:46 +03:00
# include "scene/gui/grid_container.h"
2025-04-22 11:04:16 +02:00
# include "scene/gui/item_list.h"
2014-02-09 22:10:30 -03:00
# include "scene/gui/label.h"
2025-04-22 00:23:16 +02:00
# include "scene/gui/line_edit.h"
2025-04-24 19:58:17 +02:00
# include "scene/gui/menu_button.h"
2023-10-13 12:37:46 +03:00
# include "scene/gui/option_button.h"
2025-04-24 19:58:17 +02:00
# include "scene/gui/separator.h"
2025-04-23 00:11:36 +02:00
# include "scene/gui/split_container.h"
2023-09-08 21:00:10 +02:00
# include "scene/theme/theme_db.h"
2015-03-23 11:31:03 -03:00
2020-07-11 18:45:19 +02:00
void FileDialog : : popup_file_dialog ( ) {
popup_centered_clamped ( Size2i ( 700 , 500 ) , 0.8f ) ;
2022-01-03 17:08:03 +08:00
_focus_file_text ( ) ;
}
void FileDialog : : _focus_file_text ( ) {
2025-04-22 00:23:16 +02:00
int lp = filename_edit - > get_text ( ) . rfind_char ( ' . ' ) ;
2022-01-03 17:08:03 +08:00
if ( lp ! = - 1 ) {
2025-04-22 00:23:16 +02:00
filename_edit - > select ( 0 , lp ) ;
if ( filename_edit - > is_inside_tree ( ) & & ! is_part_of_edited_scene ( ) ) {
filename_edit - > grab_focus ( ) ;
2022-01-03 17:08:03 +08:00
}
}
2020-07-11 18:45:19 +02:00
}
2024-03-21 10:42:38 +02:00
void FileDialog : : _native_popup ( ) {
// Show native dialog directly.
String root ;
2024-11-24 23:49:46 +02:00
if ( ! root_prefix . is_empty ( ) ) {
root = ProjectSettings : : get_singleton ( ) - > globalize_path ( root_prefix ) ;
} else if ( access = = ACCESS_RESOURCES ) {
2024-03-21 10:42:38 +02:00
root = ProjectSettings : : get_singleton ( ) - > get_resource_path ( ) ;
} else if ( access = = ACCESS_USERDATA ) {
root = OS : : get_singleton ( ) - > get_user_data_dir ( ) ;
}
2024-09-30 12:19:48 +03:00
// Attach native file dialog to first persistent parent window.
Window * w = ( is_transient ( ) | | is_transient_to_focused ( ) ) ? get_parent_visible_window ( ) : nullptr ;
while ( w & & w - > get_flag ( FLAG_POPUP ) & & w - > get_parent_visible_window ( ) ) {
w = w - > get_parent_visible_window ( ) ;
}
DisplayServer : : WindowID wid = w ? w - > get_window_id ( ) : DisplayServer : : INVALID_WINDOW_ID ;
2024-10-21 14:13:44 +05:30
if ( DisplayServer : : get_singleton ( ) - > has_feature ( DisplayServer : : FEATURE_NATIVE_DIALOG_FILE_EXTRA ) ) {
2025-06-14 23:06:55 +08:00
DisplayServer : : get_singleton ( ) - > file_dialog_with_options_show ( get_displayed_title ( ) , ProjectSettings : : get_singleton ( ) - > globalize_path ( full_dir ) , root , filename_edit - > get_text ( ) . get_file ( ) , show_hidden_files , DisplayServer : : FileDialogMode ( mode ) , processed_filters , _get_options ( ) , callable_mp ( this , & FileDialog : : _native_dialog_cb_with_options ) , wid ) ;
2024-10-21 14:13:44 +05:30
} else {
2025-06-14 23:06:55 +08:00
DisplayServer : : get_singleton ( ) - > file_dialog_show ( get_displayed_title ( ) , ProjectSettings : : get_singleton ( ) - > globalize_path ( full_dir ) , filename_edit - > get_text ( ) . get_file ( ) , show_hidden_files , DisplayServer : : FileDialogMode ( mode ) , processed_filters , callable_mp ( this , & FileDialog : : _native_dialog_cb ) , wid ) ;
2024-10-21 14:13:44 +05:30
}
}
bool FileDialog : : _can_use_native_popup ( ) {
if ( access = = ACCESS_RESOURCES | | access = = ACCESS_USERDATA | | options . size ( ) > 0 ) {
return DisplayServer : : get_singleton ( ) - > has_feature ( DisplayServer : : FEATURE_NATIVE_DIALOG_FILE_EXTRA ) ;
}
return DisplayServer : : get_singleton ( ) - > has_feature ( DisplayServer : : FEATURE_NATIVE_DIALOG_FILE ) ;
2024-03-21 10:42:38 +02:00
}
2021-03-30 15:42:50 +03:00
void FileDialog : : popup ( const Rect2i & p_rect ) {
2023-10-13 12:37:46 +03:00
_update_option_controls ( ) ;
2023-10-03 23:02:12 +03:00
# ifdef TOOLS_ENABLED
if ( is_part_of_edited_scene ( ) ) {
ConfirmationDialog : : popup ( p_rect ) ;
2024-09-12 10:49:50 +03:00
return ;
2023-10-03 23:02:12 +03:00
}
# endif
2024-10-21 14:13:44 +05:30
if ( _can_use_native_popup ( ) & & ( use_native_dialog | | OS : : get_singleton ( ) - > is_sandboxed ( ) ) ) {
2024-03-21 10:42:38 +02:00
_native_popup ( ) ;
2021-03-30 15:42:50 +03:00
} else {
ConfirmationDialog : : popup ( p_rect ) ;
}
}
2023-09-30 00:40:49 +03:00
void FileDialog : : set_visible ( bool p_visible ) {
2024-03-21 10:42:38 +02:00
if ( p_visible ) {
_update_option_controls ( ) ;
}
2023-10-13 12:37:46 +03:00
2023-10-03 23:02:12 +03:00
# ifdef TOOLS_ENABLED
if ( is_part_of_edited_scene ( ) ) {
ConfirmationDialog : : set_visible ( p_visible ) ;
return ;
}
# endif
2024-10-21 14:13:44 +05:30
if ( _can_use_native_popup ( ) & & ( use_native_dialog | | OS : : get_singleton ( ) - > is_sandboxed ( ) ) ) {
2024-06-09 17:54:18 +03:00
if ( p_visible ) {
_native_popup ( ) ;
}
2023-09-30 00:40:49 +03:00
} else {
ConfirmationDialog : : set_visible ( p_visible ) ;
}
}
2024-10-21 14:13:44 +05:30
void FileDialog : : _native_dialog_cb ( bool p_ok , const Vector < String > & p_files , int p_filter ) {
_native_dialog_cb_with_options ( p_ok , p_files , p_filter , Dictionary ( ) ) ;
}
void FileDialog : : _native_dialog_cb_with_options ( bool p_ok , const Vector < String > & p_files , int p_filter , const Dictionary & p_selected_options ) {
2024-03-21 10:42:38 +02:00
if ( ! p_ok ) {
2025-04-22 00:23:16 +02:00
filename_edit - > set_text ( " " ) ;
2024-03-21 10:42:38 +02:00
emit_signal ( SNAME ( " canceled " ) ) ;
return ;
}
if ( p_files . is_empty ( ) ) {
return ;
}
Vector < String > files = p_files ;
if ( access ! = ACCESS_FILESYSTEM ) {
for ( String & file_name : files ) {
file_name = ProjectSettings : : get_singleton ( ) - > localize_path ( file_name ) ;
}
}
2024-08-23 09:53:16 +03:00
selected_options = p_selected_options ;
2024-03-21 10:42:38 +02:00
String f = files [ 0 ] ;
2024-11-24 23:49:46 +02:00
filter - > select ( p_filter ) ;
2025-04-22 00:23:16 +02:00
directory_edit - > set_text ( f . get_base_dir ( ) ) ;
filename_edit - > set_text ( f . get_file ( ) ) ;
2024-11-24 23:49:46 +02:00
_change_dir ( f . get_base_dir ( ) ) ;
2024-03-21 10:42:38 +02:00
if ( mode = = FILE_MODE_OPEN_FILES ) {
emit_signal ( SNAME ( " files_selected " ) , files ) ;
} else {
if ( mode = = FILE_MODE_SAVE_FILE ) {
2025-02-13 09:26:07 +02:00
bool valid = false ;
if ( p_filter = = filter - > get_item_count ( ) - 1 ) {
valid = true ; // Match none.
} else if ( filters . size ( ) > 1 & & p_filter = = 0 ) {
// Match all filters.
for ( int i = 0 ; i < filters . size ( ) ; i + + ) {
2024-11-16 17:16:07 +01:00
String flt = filters [ i ] . get_slicec ( ' ; ' , 0 ) ;
2025-02-13 09:26:07 +02:00
for ( int j = 0 ; j < flt . get_slice_count ( " , " ) ; j + + ) {
2024-11-16 17:16:07 +01:00
String str = flt . get_slicec ( ' , ' , j ) . strip_edges ( ) ;
2025-02-13 09:26:07 +02:00
if ( f . matchn ( str ) ) {
valid = true ;
break ;
}
}
if ( valid ) {
break ;
}
}
} else {
2024-11-23 20:33:58 +02:00
int idx = p_filter ;
if ( filters . size ( ) > 1 ) {
idx - - ;
2024-03-21 10:42:38 +02:00
}
2024-11-23 20:33:58 +02:00
if ( idx > = 0 & & idx < filters . size ( ) ) {
2024-11-16 17:16:07 +01:00
String flt = filters [ idx ] . get_slicec ( ' ; ' , 0 ) ;
2024-11-23 20:33:58 +02:00
int filter_slice_count = flt . get_slice_count ( " , " ) ;
for ( int j = 0 ; j < filter_slice_count ; j + + ) {
2024-11-16 17:16:07 +01:00
String str = flt . get_slicec ( ' , ' , j ) . strip_edges ( ) ;
2025-02-13 09:26:07 +02:00
if ( f . matchn ( str ) ) {
2024-11-23 20:33:58 +02:00
valid = true ;
break ;
}
}
2024-03-21 10:42:38 +02:00
2024-11-23 20:33:58 +02:00
if ( ! valid & & filter_slice_count > 0 ) {
2024-11-16 17:16:07 +01:00
String str = flt . get_slicec ( ' , ' , 0 ) . strip_edges ( ) ;
2025-02-26 11:41:11 +01:00
f + = str . substr ( 1 ) ;
2025-04-22 00:23:16 +02:00
filename_edit - > set_text ( f . get_file ( ) ) ;
2025-02-13 09:26:07 +02:00
valid = true ;
2024-11-23 20:33:58 +02:00
}
2025-02-13 09:26:07 +02:00
} else {
valid = true ;
2021-03-30 15:42:50 +03:00
}
}
2025-02-13 09:26:07 +02:00
// Add first extension of filter if no valid extension is found.
if ( ! valid ) {
int idx = p_filter ;
2024-11-16 17:16:07 +01:00
String flt = filters [ idx ] . get_slicec ( ' ; ' , 0 ) ;
String ext = flt . get_slicec ( ' , ' , 0 ) . strip_edges ( ) . get_extension ( ) ;
2025-02-13 09:26:07 +02:00
f + = " . " + ext ;
}
2024-03-21 10:42:38 +02:00
emit_signal ( SNAME ( " file_selected " ) , f ) ;
} else if ( ( mode = = FILE_MODE_OPEN_ANY | | mode = = FILE_MODE_OPEN_FILE ) & & dir_access - > file_exists ( f ) ) {
emit_signal ( SNAME ( " file_selected " ) , f ) ;
} else if ( mode = = FILE_MODE_OPEN_ANY | | mode = = FILE_MODE_OPEN_DIR ) {
emit_signal ( SNAME ( " dir_selected " ) , f ) ;
2021-03-30 15:42:50 +03:00
}
}
}
2023-09-11 16:24:54 +02:00
void FileDialog : : _validate_property ( PropertyInfo & p_property ) const {
if ( p_property . name = = " dialog_text " ) {
// File dialogs have a custom layout, and dialog nodes can't have both a text and a layout.
p_property . usage = PROPERTY_USAGE_NONE ;
}
}
2020-03-06 14:00:16 -03:00
void FileDialog : : _notification ( int p_what ) {
2022-02-15 18:06:48 +01:00
switch ( p_what ) {
2024-04-06 15:45:45 -03:00
case NOTIFICATION_READY : {
# ifdef TOOLS_ENABLED
if ( is_part_of_edited_scene ( ) ) {
return ;
}
# endif
// Replace the built-in dialog with the native one if it started visible.
2024-10-21 14:13:44 +05:30
if ( is_visible ( ) & & _can_use_native_popup ( ) & & ( use_native_dialog | | OS : : get_singleton ( ) - > is_sandboxed ( ) ) ) {
2024-04-06 15:45:45 -03:00
ConfirmationDialog : : set_visible ( false ) ;
_native_popup ( ) ;
}
} break ;
2022-02-15 18:06:48 +01:00
case NOTIFICATION_VISIBILITY_CHANGED : {
2025-04-23 00:11:36 +02:00
if ( is_visible ( ) ) {
_update_favorite_list ( ) ;
_update_recent_list ( ) ;
invalidate ( ) ; // Put it here to preview in the editor.
2022-02-15 18:06:48 +01:00
}
} break ;
2022-09-01 13:38:08 +03:00
case NOTIFICATION_THEME_CHANGED : {
2025-05-21 16:41:18 +02:00
favorite_list - > set_custom_minimum_size ( Vector2 ( 128 * get_theme_default_base_scale ( ) , 0 ) ) ;
recent_list - > set_custom_minimum_size ( Vector2 ( 128 * get_theme_default_base_scale ( ) , 0 ) ) ;
2025-04-22 00:23:16 +02:00
if ( main_vbox - > is_layout_rtl ( ) ) {
_setup_button ( dir_prev , theme_cache . forward_folder ) ;
_setup_button ( dir_next , theme_cache . back_folder ) ;
2022-02-15 18:06:48 +01:00
} else {
2025-04-22 00:23:16 +02:00
_setup_button ( dir_prev , theme_cache . back_folder ) ;
_setup_button ( dir_next , theme_cache . forward_folder ) ;
2022-02-15 18:06:48 +01:00
}
2025-04-22 00:23:16 +02:00
_setup_button ( dir_up , theme_cache . parent_folder ) ;
_setup_button ( refresh_button , theme_cache . reload ) ;
2025-04-23 00:11:36 +02:00
_setup_button ( favorite_button , theme_cache . favorite ) ;
2025-04-22 00:23:16 +02:00
_setup_button ( make_dir_button , theme_cache . create_folder ) ;
2025-04-28 14:57:50 +02:00
_setup_button ( show_hidden , theme_cache . toggle_hidden ) ;
_setup_button ( thumbnail_mode_button , theme_cache . thumbnail_mode ) ;
_setup_button ( list_mode_button , theme_cache . list_mode ) ;
2025-04-22 00:23:16 +02:00
_setup_button ( show_filename_filter_button , theme_cache . toggle_filename_filter ) ;
2025-04-24 19:58:17 +02:00
_setup_button ( file_sort_button , theme_cache . sort ) ;
2025-04-23 00:11:36 +02:00
_setup_button ( fav_up_button , theme_cache . favorite_up ) ;
_setup_button ( fav_down_button , theme_cache . favorite_down ) ;
2023-01-22 14:26:32 -03:00
invalidate ( ) ;
2022-08-29 11:04:31 +02:00
} break ;
case NOTIFICATION_TRANSLATION_CHANGED : {
update_filters ( ) ;
2022-02-15 18:06:48 +01:00
} break ;
2022-01-19 17:04:06 +01:00
}
2015-12-29 00:36:48 +01:00
}
2022-01-11 15:59:52 +02:00
void FileDialog : : shortcut_input ( const Ref < InputEvent > & p_event ) {
2025-10-09 22:31:55 +02:00
if ( p_event . is_null ( ) | | p_event - > is_released ( ) | | p_event - > is_echo ( ) ) {
return ;
}
2015-12-29 00:36:48 +01:00
2025-10-09 22:31:55 +02:00
for ( const KeyValue < ItemMenu , Ref < Shortcut > > & action : action_shortcuts ) {
if ( action . value - > matches_event ( p_event ) ) {
_item_menu_id_pressed ( action . key ) ;
set_input_as_handled ( ) ;
break ;
2015-12-29 00:36:48 +01:00
}
}
2014-02-09 22:10:30 -03:00
}
void FileDialog : : set_enable_multiple_selection ( bool p_enable ) {
2025-04-22 11:04:16 +02:00
file_list - > set_select_mode ( p_enable ? ItemList : : SELECT_MULTI : ItemList : : SELECT_SINGLE ) ;
2022-09-28 15:59:08 +02:00
}
2014-02-09 22:10:30 -03:00
Vector < String > FileDialog : : get_selected_files ( ) const {
2025-04-22 11:04:16 +02:00
const String current_dir = dir_access - > get_current_dir ( ) ;
2014-02-09 22:10:30 -03:00
Vector < String > list ;
2025-04-22 11:04:16 +02:00
for ( int idx : file_list - > get_selected_items ( ) ) {
list . push_back ( current_dir . path_join ( file_list - > get_item_text ( idx ) ) ) ;
2022-09-28 15:59:08 +02:00
}
2014-02-09 22:10:30 -03:00
return list ;
2022-09-28 15:59:08 +02:00
}
2014-02-09 22:10:30 -03:00
void FileDialog : : update_dir ( ) {
2024-11-24 23:49:46 +02:00
full_dir = dir_access - > get_current_dir ( ) ;
2022-03-13 01:35:20 +01:00
if ( root_prefix . is_empty ( ) ) {
2025-04-22 00:23:16 +02:00
directory_edit - > set_text ( dir_access - > get_current_dir ( false ) ) ;
2022-03-13 01:35:20 +01:00
} else {
2025-04-22 00:23:16 +02:00
directory_edit - > set_text ( dir_access - > get_current_dir ( false ) . trim_prefix ( root_prefix ) . trim_prefix ( " / " ) ) ;
2022-03-13 01:35:20 +01:00
}
2020-02-10 09:19:29 +01:00
2017-10-06 21:59:43 +07:00
if ( drives - > is_visible ( ) ) {
2022-01-24 13:12:46 +02:00
if ( dir_access - > get_current_dir ( ) . is_network_share_path ( ) ) {
_update_drives ( false ) ;
2023-12-15 20:56:06 -03:00
drives - > add_item ( ETR ( " Network " ) ) ;
2022-03-12 01:06:45 +01:00
drives - > set_item_disabled ( - 1 , true ) ;
2022-01-24 13:12:46 +02:00
drives - > select ( drives - > get_item_count ( ) - 1 ) ;
} else {
drives - > select ( dir_access - > get_current_drive ( ) ) ;
}
2017-10-06 21:59:43 +07:00
}
2017-11-27 18:58:28 +03:00
// Deselect any item, to make "Select Current Folder" button text by default.
2020-12-21 10:26:41 +00:00
deselect_all ( ) ;
2014-02-09 22:10:30 -03:00
}
2021-06-16 09:43:34 -07:00
void FileDialog : : _dir_submitted ( String p_dir ) {
2024-12-19 09:47:46 +02:00
String new_dir = p_dir ;
# ifdef WINDOWS_ENABLED
if ( root_prefix . is_empty ( ) & & drives - > is_visible ( ) & & ! new_dir . is_network_share_path ( ) & & new_dir . is_absolute_path ( ) & & new_dir . find ( " :/ " ) = = - 1 & & new_dir . find ( " : \\ " ) = = - 1 ) {
// Non network path without X:/ prefix on Windows, add drive letter.
new_dir = drives - > get_item_text ( drives - > get_selected ( ) ) . path_join ( new_dir ) ;
}
# endif
if ( ! root_prefix . is_empty ( ) ) {
new_dir = root_prefix . path_join ( new_dir ) ;
}
_change_dir ( new_dir ) ;
2025-04-22 00:23:16 +02:00
filename_edit - > set_text ( " " ) ;
2021-03-19 14:01:03 +02:00
_push_history ( ) ;
2014-02-09 22:10:30 -03:00
}
2021-06-16 09:43:34 -07:00
void FileDialog : : _file_submitted ( const String & p_file ) {
2016-03-09 00:00:52 +01:00
_action_pressed ( ) ;
2014-02-09 22:10:30 -03:00
}
void FileDialog : : _save_confirm_pressed ( ) {
2025-04-23 00:11:36 +02:00
_save_to_recent ( ) ;
2025-04-22 00:23:16 +02:00
String f = dir_access - > get_current_dir ( ) . path_join ( filename_edit - > get_text ( ) ) ;
2021-07-17 18:22:52 -03:00
emit_signal ( SNAME ( " file_selected " ) , f ) ;
2016-03-09 00:00:52 +01:00
hide ( ) ;
2014-02-09 22:10:30 -03:00
}
void FileDialog : : _post_popup ( ) {
ConfirmationDialog : : _post_popup ( ) ;
2020-05-14 16:41:43 +02:00
if ( mode = = FILE_MODE_SAVE_FILE ) {
2025-09-03 20:39:18 -03:00
filename_edit - > grab_focus ( true ) ;
2020-05-14 16:41:43 +02:00
} else {
2025-09-03 20:39:18 -03:00
file_list - > grab_focus ( true ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-09 22:10:30 -03:00
2017-11-27 18:58:28 +03:00
// For open dir mode, deselect all items on file dialog open.
2020-03-06 14:00:16 -03:00
if ( mode = = FILE_MODE_OPEN_DIR ) {
2020-12-21 10:26:41 +00:00
deselect_all ( ) ;
2019-10-26 17:29:22 +02:00
file_box - > set_visible ( false ) ;
} else {
file_box - > set_visible ( true ) ;
}
2021-03-19 14:01:03 +02:00
local_history . clear ( ) ;
local_history_pos = - 1 ;
_push_history ( ) ;
}
void FileDialog : : _push_history ( ) {
local_history . resize ( local_history_pos + 1 ) ;
String new_path = dir_access - > get_current_dir ( ) ;
2025-03-20 00:07:31 +08:00
if ( local_history . is_empty ( ) | | new_path ! = local_history [ local_history_pos ] ) {
2021-03-19 14:01:03 +02:00
local_history . push_back ( new_path ) ;
local_history_pos + + ;
dir_prev - > set_disabled ( local_history_pos = = 0 ) ;
dir_next - > set_disabled ( true ) ;
}
2014-02-09 22:10:30 -03:00
}
void FileDialog : : _action_pressed ( ) {
2020-03-06 14:00:16 -03:00
if ( mode = = FILE_MODE_OPEN_FILES ) {
2025-04-22 11:04:16 +02:00
const Vector < String > files = get_selected_files ( ) ;
if ( ! files . is_empty ( ) ) {
2025-04-23 00:11:36 +02:00
_save_to_recent ( ) ;
2021-07-17 18:22:52 -03:00
emit_signal ( SNAME ( " files_selected " ) , files ) ;
2014-02-09 22:10:30 -03:00
hide ( ) ;
}
return ;
}
2025-04-22 00:23:16 +02:00
String file_text = filename_edit - > get_text ( ) ;
2022-08-29 19:34:01 -05:00
String f = file_text . is_absolute_path ( ) ? file_text : dir_access - > get_current_dir ( ) . path_join ( file_text ) ;
2016-03-09 00:00:52 +01:00
2024-11-04 11:50:42 +02:00
if ( ( mode = = FILE_MODE_OPEN_ANY | | mode = = FILE_MODE_OPEN_FILE ) & & ( dir_access - > file_exists ( f ) | | dir_access - > is_bundle ( f ) ) ) {
2025-04-23 00:11:36 +02:00
_save_to_recent ( ) ;
2021-07-17 18:22:52 -03:00
emit_signal ( SNAME ( " file_selected " ) , f ) ;
2014-02-09 22:10:30 -03:00
hide ( ) ;
2020-03-06 14:00:16 -03:00
} else if ( mode = = FILE_MODE_OPEN_ANY | | mode = = FILE_MODE_OPEN_DIR ) {
2014-02-09 22:10:30 -03:00
String path = dir_access - > get_current_dir ( ) ;
2016-09-19 11:27:53 +08:00
2024-05-28 12:15:00 +02:00
path = path . replace_char ( ' \\ ' , ' / ' ) ;
2025-04-22 11:04:16 +02:00
int selected = _get_selected_file_idx ( ) ;
if ( selected > - 1 ) {
Dictionary d = file_list - > get_item_metadata ( selected ) ;
2017-11-24 21:12:18 +03:00
if ( d [ " dir " ] & & d [ " name " ] ! = " .. " ) {
2022-08-29 19:34:01 -05:00
path = path . path_join ( d [ " name " ] ) ;
2014-02-09 22:10:30 -03:00
}
2016-09-19 11:27:53 +08:00
}
2025-04-23 00:11:36 +02:00
_save_to_recent ( ) ;
2021-07-17 18:22:52 -03:00
emit_signal ( SNAME ( " dir_selected " ) , path ) ;
2014-02-09 22:10:30 -03:00
hide ( ) ;
}
2020-03-06 14:00:16 -03:00
if ( mode = = FILE_MODE_SAVE_FILE ) {
2014-04-10 00:18:27 -03:00
bool valid = false ;
if ( filter - > get_selected ( ) = = filter - > get_item_count ( ) - 1 ) {
2025-02-13 09:26:07 +02:00
valid = true ; // Match none.
2014-04-10 00:18:27 -03:00
} else if ( filters . size ( ) > 1 & & filter - > get_selected ( ) = = 0 ) {
2025-02-13 09:26:07 +02:00
// Match all filters.
2014-04-10 00:18:27 -03:00
for ( int i = 0 ; i < filters . size ( ) ; i + + ) {
2024-11-16 17:16:07 +01:00
String flt = filters [ i ] . get_slicec ( ' ; ' , 0 ) ;
2014-04-10 00:18:27 -03:00
for ( int j = 0 ; j < flt . get_slice_count ( " , " ) ; j + + ) {
2024-11-16 17:16:07 +01:00
String str = flt . get_slicec ( ' , ' , j ) . strip_edges ( ) ;
2023-12-05 14:11:09 +00:00
if ( f . matchn ( str ) ) {
2014-04-10 00:18:27 -03:00
valid = true ;
break ;
}
}
2020-05-14 16:41:43 +02:00
if ( valid ) {
2014-04-10 00:18:27 -03:00
break ;
2020-05-14 16:41:43 +02:00
}
2014-04-10 00:18:27 -03:00
}
} else {
int idx = filter - > get_selected ( ) ;
2020-05-14 16:41:43 +02:00
if ( filters . size ( ) > 1 ) {
2014-04-10 00:18:27 -03:00
idx - - ;
2020-05-14 16:41:43 +02:00
}
2014-04-10 00:18:27 -03:00
if ( idx > = 0 & & idx < filters . size ( ) ) {
2024-11-16 17:16:07 +01:00
String flt = filters [ idx ] . get_slicec ( ' ; ' , 0 ) ;
2024-11-23 20:33:58 +02:00
int filter_slice_count = flt . get_slice_count ( " , " ) ;
for ( int j = 0 ; j < filter_slice_count ; j + + ) {
2024-11-16 17:16:07 +01:00
String str = ( flt . get_slicec ( ' , ' , j ) . strip_edges ( ) ) ;
2023-12-05 14:11:09 +00:00
if ( f . matchn ( str ) ) {
2014-04-10 00:18:27 -03:00
valid = true ;
break ;
}
}
2015-01-14 18:02:51 -05:00
2024-11-23 20:33:58 +02:00
if ( ! valid & & filter_slice_count > 0 ) {
2024-11-16 17:16:07 +01:00
String str = flt . get_slicec ( ' , ' , 0 ) . strip_edges ( ) ;
2025-02-26 11:41:11 +01:00
f + = str . substr ( 1 ) ;
2025-04-22 00:23:16 +02:00
filename_edit - > set_text ( f . get_file ( ) ) ;
2015-01-14 18:02:51 -05:00
valid = true ;
}
2014-04-10 00:18:27 -03:00
} else {
valid = true ;
}
}
2022-12-09 15:32:14 +07:00
String file_name = file_text . strip_edges ( ) . get_file ( ) ;
if ( ! valid | | file_name . is_empty ( ) ) {
2020-03-06 14:00:16 -03:00
exterr - > popup_centered ( Size2 ( 250 , 80 ) ) ;
2014-04-10 00:18:27 -03:00
return ;
}
2025-10-02 13:23:02 +02:00
if ( customization_flags [ CUSTOMIZATION_OVERWRITE_WARNING ] & & ( dir_access - > file_exists ( f ) | | dir_access - > is_bundle ( f ) ) ) {
2023-12-15 20:56:06 -03:00
confirm_save - > set_text ( vformat ( atr ( ETR ( " File \" %s \" already exists. \n Do you want to overwrite it? " ) ) , f ) ) ;
2022-12-09 15:32:14 +07:00
confirm_save - > popup_centered ( Size2 ( 250 , 80 ) ) ;
2014-02-09 22:10:30 -03:00
} else {
2025-04-23 00:11:36 +02:00
_save_to_recent ( ) ;
2021-07-17 18:22:52 -03:00
emit_signal ( SNAME ( " file_selected " ) , f ) ;
2014-02-09 22:10:30 -03:00
hide ( ) ;
}
}
}
void FileDialog : : _cancel_pressed ( ) {
2025-04-22 00:23:16 +02:00
filename_edit - > set_text ( " " ) ;
2014-02-09 22:10:30 -03:00
invalidate ( ) ;
hide ( ) ;
}
2017-11-24 21:12:18 +03:00
bool FileDialog : : _is_open_should_be_disabled ( ) {
2020-05-14 16:41:43 +02:00
if ( mode = = FILE_MODE_OPEN_ANY | | mode = = FILE_MODE_SAVE_FILE ) {
2017-11-24 21:12:18 +03:00
return false ;
2020-05-14 16:41:43 +02:00
}
2017-11-24 21:12:18 +03:00
2025-04-22 11:04:16 +02:00
Vector < int > items = file_list - > get_selected_items ( ) ;
if ( items . is_empty ( ) ) {
2020-03-06 14:00:16 -03:00
return mode ! = FILE_MODE_OPEN_DIR ; // In "Open folder" mode, having nothing selected picks the current folder.
2020-05-14 16:41:43 +02:00
}
2017-11-24 21:12:18 +03:00
2025-04-22 11:04:16 +02:00
for ( const int idx : items ) {
Dictionary d = file_list - > get_item_metadata ( idx ) ;
2017-11-24 21:12:18 +03:00
2025-04-22 11:04:16 +02:00
if ( ( ( mode = = FILE_MODE_OPEN_FILE | | mode = = FILE_MODE_OPEN_FILES ) & & d [ " dir " ] ) | | ( mode = = FILE_MODE_OPEN_DIR & & ! d [ " dir " ] ) ) {
return true ;
}
}
return false ;
2017-11-24 21:12:18 +03:00
}
2025-06-30 19:07:12 +02:00
void FileDialog : : _thumbnail_callback ( const Ref < Texture2D > & p_texture , const String & p_path ) {
if ( display_mode = = DISPLAY_LIST | | p_texture . is_null ( ) ) {
return ;
}
if ( ! p_path . begins_with ( full_dir ) ) {
return ;
}
const String file_name = p_path . get_file ( ) ;
for ( int i = 0 ; i < file_list - > get_item_count ( ) ; i + + ) {
if ( file_list - > get_item_text ( i ) = = file_name ) {
file_list - > set_item_icon ( i , p_texture ) ;
break ;
}
}
}
2017-11-27 18:58:28 +03:00
void FileDialog : : _go_up ( ) {
2022-03-13 01:35:20 +01:00
_change_dir ( " .. " ) ;
2021-03-19 14:01:03 +02:00
_push_history ( ) ;
}
void FileDialog : : _go_back ( ) {
if ( local_history_pos < = 0 ) {
return ;
}
local_history_pos - - ;
2022-03-13 01:35:20 +01:00
_change_dir ( local_history [ local_history_pos ] ) ;
2021-03-19 14:01:03 +02:00
dir_prev - > set_disabled ( local_history_pos = = 0 ) ;
dir_next - > set_disabled ( local_history_pos = = local_history . size ( ) - 1 ) ;
}
void FileDialog : : _go_forward ( ) {
2023-01-03 10:23:49 +02:00
if ( local_history_pos > = local_history . size ( ) - 1 ) {
2021-03-19 14:01:03 +02:00
return ;
}
local_history_pos + + ;
2022-03-13 01:35:20 +01:00
_change_dir ( local_history [ local_history_pos ] ) ;
2021-03-19 14:01:03 +02:00
dir_prev - > set_disabled ( local_history_pos = = 0 ) ;
dir_next - > set_disabled ( local_history_pos = = local_history . size ( ) - 1 ) ;
2017-11-27 18:58:28 +03:00
}
2020-12-21 10:26:41 +00:00
void FileDialog : : deselect_all ( ) {
2017-11-27 18:58:28 +03:00
// Clear currently selected items in file manager.
2025-04-22 11:04:16 +02:00
file_list - > deselect_all ( ) ;
2017-11-27 18:58:28 +03:00
// And change get_ok title.
2025-04-22 11:04:16 +02:00
get_ok_button ( ) - > set_disabled ( _is_open_should_be_disabled ( ) ) ;
2017-11-27 18:58:28 +03:00
2025-04-22 11:04:16 +02:00
switch ( mode ) {
case FILE_MODE_OPEN_FILE :
case FILE_MODE_OPEN_FILES :
2025-04-19 00:57:00 +02:00
set_default_ok_text ( ETR ( " Open " ) ) ;
2025-04-22 11:04:16 +02:00
break ;
case FILE_MODE_OPEN_DIR :
2025-04-19 00:57:00 +02:00
set_default_ok_text ( ETR ( " Select Current Folder " ) ) ;
2025-04-22 11:04:16 +02:00
break ;
case FILE_MODE_OPEN_ANY :
2025-04-19 00:57:00 +02:00
set_default_ok_text ( ETR ( " Open " ) ) ;
2025-04-22 11:04:16 +02:00
break ;
case FILE_MODE_SAVE_FILE :
2025-04-19 00:57:00 +02:00
set_default_ok_text ( ETR ( " Save " ) ) ;
2025-04-22 11:04:16 +02:00
break ;
2017-11-27 18:58:28 +03:00
}
}
2018-02-15 00:44:11 -02:00
2025-04-22 11:04:16 +02:00
int FileDialog : : _get_selected_file_idx ( ) {
const PackedInt32Array selected = file_list - > get_selected_items ( ) ;
return selected . is_empty ( ) ? - 1 : selected [ 0 ] ;
2018-08-20 17:45:16 +02:00
}
2025-10-01 14:49:52 +02:00
String FileDialog : : _get_item_path ( int p_idx ) const {
const Dictionary meta = file_list - > get_item_metadata ( p_idx ) ;
return ProjectSettings : : get_singleton ( ) - > globalize_path ( dir_access - > get_current_dir ( ) . path_join ( meta [ " name " ] ) ) ;
}
2025-04-22 11:04:16 +02:00
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 ( ) ) ;
2020-05-14 16:41:43 +02:00
}
2025-04-22 11:04:16 +02:00
}
void FileDialog : : _file_list_selected ( int p_item ) {
Dictionary d = file_list - > get_item_metadata ( p_item ) ;
2016-03-09 00:00:52 +01:00
2014-02-09 22:10:30 -03:00
if ( ! d [ " dir " ] ) {
2025-04-22 00:23:16 +02:00
filename_edit - > set_text ( d [ " name " ] ) ;
2025-01-27 10:08:32 +02:00
if ( mode = = FILE_MODE_SAVE_FILE ) {
2025-04-19 00:57:00 +02:00
set_default_ok_text ( ETR ( " Save " ) ) ;
2025-01-27 10:08:32 +02:00
} else {
2025-04-19 00:57:00 +02:00
set_default_ok_text ( ETR ( " Open " ) ) ;
2025-01-27 10:08:32 +02:00
}
2025-04-22 00:23:16 +02:00
} else if ( mode = = FILE_MODE_OPEN_DIR | | mode = = FILE_MODE_OPEN_ANY | | ! dir_access - > file_exists ( filename_edit - > get_text ( ) ) ) {
filename_edit - > set_text ( " " ) ;
2025-02-04 08:49:42 +02:00
if ( mode = = FILE_MODE_OPEN_DIR | | mode = = FILE_MODE_OPEN_ANY ) {
2025-04-19 00:57:00 +02:00
set_default_ok_text ( ETR ( " Select This Folder " ) ) ;
2025-02-04 08:49:42 +02:00
}
2014-02-09 22:10:30 -03:00
}
2017-11-24 21:12:18 +03:00
2020-12-14 18:37:30 +00:00
get_ok_button ( ) - > set_disabled ( _is_open_should_be_disabled ( ) ) ;
2014-02-09 22:10:30 -03:00
}
2025-04-22 11:04:16 +02:00
void FileDialog : : _file_list_item_activated ( int p_item ) {
Dictionary d = file_list - > get_item_metadata ( p_item ) ;
2014-02-09 22:10:30 -03:00
if ( d [ " dir " ] ) {
2022-03-13 01:35:20 +01:00
_change_dir ( d [ " name " ] ) ;
2020-05-14 16:41:43 +02:00
if ( mode = = FILE_MODE_OPEN_FILE | | mode = = FILE_MODE_OPEN_FILES | | mode = = FILE_MODE_OPEN_DIR | | mode = = FILE_MODE_OPEN_ANY ) {
2025-04-22 00:23:16 +02:00
filename_edit - > set_text ( " " ) ;
2020-05-14 16:41:43 +02:00
}
2021-03-19 14:01:03 +02:00
_push_history ( ) ;
2014-02-09 22:10:30 -03:00
} else {
_action_pressed ( ) ;
}
}
2019-06-28 10:29:50 +03:00
void FileDialog : : update_file_name ( ) {
int idx = filter - > get_selected ( ) - 1 ;
if ( ( idx = = - 1 & & filter - > get_item_count ( ) = = 2 ) | | ( filter - > get_item_count ( ) > 2 & & idx > = 0 & & idx < filter - > get_item_count ( ) - 2 ) ) {
2020-05-14 16:41:43 +02:00
if ( idx = = - 1 ) {
2019-06-28 10:29:50 +03:00
idx + = 1 ;
2020-05-14 16:41:43 +02:00
}
2019-06-28 10:29:50 +03:00
String filter_str = filters [ idx ] ;
2025-04-22 00:23:16 +02:00
String file_str = filename_edit - > get_text ( ) ;
2019-06-28 10:29:50 +03:00
String base_name = file_str . get_basename ( ) ;
2020-04-28 19:33:46 +05:30
Vector < String > filter_substr = filter_str . split ( " ; " ) ;
if ( filter_substr . size ( ) > = 2 ) {
file_str = base_name + " . " + filter_substr [ 0 ] . strip_edges ( ) . get_extension ( ) . to_lower ( ) ;
} else {
file_str = base_name + " . " + filter_str . strip_edges ( ) . get_extension ( ) . to_lower ( ) ;
}
2025-04-22 00:23:16 +02:00
filename_edit - > set_text ( file_str ) ;
2019-06-28 10:29:50 +03:00
}
}
2024-11-04 11:50:42 +02:00
void FileDialog : : _item_menu_id_pressed ( int p_option ) {
2025-10-01 14:49:52 +02:00
int selected = _get_selected_file_idx ( ) ;
2024-11-04 11:50:42 +02:00
switch ( p_option ) {
2025-10-01 14:49:52 +02:00
case ITEM_MENU_COPY_PATH : {
if ( selected > - 1 ) {
DisplayServer : : get_singleton ( ) - > clipboard_set ( _get_item_path ( selected ) ) ;
}
} break ;
case ITEM_MENU_DELETE : {
if ( selected > - 1 ) {
delete_dialog - > popup_centered ( Size2 ( 250 , 80 ) ) ;
}
} break ;
case ITEM_MENU_REFRESH : {
invalidate ( ) ;
} break ;
case ITEM_MENU_NEW_FOLDER : {
_make_dir ( ) ;
} break ;
2024-11-04 11:50:42 +02:00
case ITEM_MENU_SHOW_IN_EXPLORER : {
String path ;
2025-04-22 11:04:16 +02:00
if ( selected > - 1 ) {
2025-10-01 14:49:52 +02:00
path = _get_item_path ( selected ) ;
2024-11-04 11:50:42 +02:00
} else {
path = ProjectSettings : : get_singleton ( ) - > globalize_path ( dir_access - > get_current_dir ( ) ) ;
}
OS : : get_singleton ( ) - > shell_show_in_file_manager ( path , true ) ;
} break ;
case ITEM_MENU_SHOW_BUNDLE_CONTENT : {
2025-04-22 11:04:16 +02:00
if ( selected = = - 1 ) {
2024-11-04 11:50:42 +02:00
return ;
}
2025-10-01 14:49:52 +02:00
Dictionary meta = file_list - > get_item_metadata ( selected ) ;
_change_dir ( meta [ " name " ] ) ;
2024-11-04 11:50:42 +02:00
if ( mode = = FILE_MODE_OPEN_FILE | | mode = = FILE_MODE_OPEN_FILES | | mode = = FILE_MODE_OPEN_DIR | | mode = = FILE_MODE_OPEN_ANY ) {
2025-04-22 00:23:16 +02:00
filename_edit - > set_text ( " " ) ;
2024-11-04 11:50:42 +02:00
}
_push_history ( ) ;
} break ;
2025-10-09 22:31:55 +02:00
case ITEM_MENU_GO_UP : {
_dir_submitted ( " .. " ) ;
} break ;
case ITEM_MENU_TOGGLE_HIDDEN : {
set_show_hidden_files ( ! show_hidden_files ) ;
} break ;
case ITEM_MENU_FIND : {
show_filename_filter_button - > set_pressed ( ! show_filename_filter_button - > is_pressed ( ) ) ;
} break ;
case ITEM_MENU_FOCUS_PATH : {
directory_edit - > grab_focus ( ) ;
directory_edit - > select_all ( ) ;
} break ;
2024-11-04 11:50:42 +02:00
}
}
void FileDialog : : _empty_clicked ( const Vector2 & p_pos , MouseButton p_button ) {
if ( p_button = = MouseButton : : RIGHT ) {
2025-10-01 14:49:52 +02:00
_popup_menu ( p_pos , - 1 ) ;
2025-04-22 11:04:16 +02:00
} else if ( p_button = = MouseButton : : LEFT ) {
deselect_all ( ) ;
2024-11-04 11:50:42 +02:00
}
}
2025-04-22 11:04:16 +02:00
void FileDialog : : _item_clicked ( int p_item , const Vector2 & p_pos , MouseButton p_button ) {
2024-11-04 11:50:42 +02:00
if ( p_button = = MouseButton : : RIGHT ) {
2025-10-01 14:49:52 +02:00
_popup_menu ( p_pos , p_item ) ;
}
}
void FileDialog : : _popup_menu ( const Vector2 & p_pos , int p_for_item ) {
item_menu - > clear ( ) ;
if ( p_for_item > - 1 ) {
item_menu - > add_item ( ETR ( " Copy Path " ) , ITEM_MENU_COPY_PATH ) ;
if ( customization_flags [ CUSTOMIZATION_DELETE ] ) {
item_menu - > add_item ( ETR ( " Delete " ) , ITEM_MENU_DELETE ) ;
2025-10-09 22:31:55 +02:00
item_menu - > set_item_shortcut ( - 1 , action_shortcuts [ ITEM_MENU_DELETE ] ) ;
2024-11-04 11:50:42 +02:00
}
2025-10-01 14:49:52 +02:00
} else {
if ( can_create_folders ) {
item_menu - > add_item ( ETR ( " New Folder... " ) , ITEM_MENU_NEW_FOLDER ) ;
}
item_menu - > add_item ( ETR ( " Refresh " ) , ITEM_MENU_REFRESH ) ;
2025-10-09 22:31:55 +02:00
item_menu - > set_item_shortcut ( - 1 , action_shortcuts [ ITEM_MENU_REFRESH ] ) ;
2025-10-01 14:49:52 +02:00
}
# if !defined(ANDROID_ENABLED) && !defined(WEB_ENABLED)
// Opening the system file manager is not supported on the Android and web editors.
item_menu - > add_separator ( ) ;
2024-11-04 11:50:42 +02:00
2025-10-01 14:49:52 +02:00
Dictionary meta ;
if ( p_for_item > - 1 ) {
meta = file_list - > get_item_metadata ( p_for_item ) ;
}
item_menu - > add_item ( ( p_for_item = = - 1 | | meta [ " dir " ] ) ? ETR ( " Open in File Manager " ) : ETR ( " Show in File Manager " ) , ITEM_MENU_SHOW_IN_EXPLORER ) ;
if ( meta [ " bundle " ] ) {
item_menu - > add_item ( ETR ( " Show Package Contents " ) , ITEM_MENU_SHOW_BUNDLE_CONTENT ) ;
}
2024-11-04 11:50:42 +02:00
# endif
2025-10-01 14:49:52 +02:00
if ( item_menu - > get_item_count ( ) = = 0 ) {
return ;
2024-11-04 11:50:42 +02:00
}
2025-10-01 14:49:52 +02:00
item_menu - > set_position ( file_list - > get_screen_position ( ) + p_pos ) ;
item_menu - > reset_size ( ) ;
item_menu - > popup ( ) ;
2024-11-04 11:50:42 +02:00
}
2014-02-09 22:10:30 -03:00
void FileDialog : : update_file_list ( ) {
2025-04-22 11:04:16 +02:00
file_list - > clear ( ) ;
2019-10-24 15:31:31 +02:00
// Scroll back to the top after opening a directory
2025-04-22 11:04:16 +02:00
file_list - > get_v_scroll_bar ( ) - > set_value ( 0 ) ;
2019-10-24 15:31:31 +02:00
2025-04-28 14:57:50 +02:00
if ( display_mode = = DISPLAY_THUMBNAILS ) {
file_list - > set_max_columns ( 0 ) ;
file_list - > set_icon_mode ( ItemList : : ICON_MODE_TOP ) ;
file_list - > set_fixed_column_width ( theme_cache . thumbnail_size * 3 / 2 ) ;
file_list - > set_max_text_lines ( 2 ) ;
file_list - > set_text_overrun_behavior ( TextServer : : OVERRUN_TRIM_ELLIPSIS ) ;
file_list - > set_fixed_icon_size ( Size2 ( theme_cache . thumbnail_size , theme_cache . thumbnail_size ) ) ;
} else {
file_list - > set_icon_mode ( ItemList : : ICON_MODE_LEFT ) ;
file_list - > set_max_columns ( 1 ) ;
file_list - > set_max_text_lines ( 1 ) ;
file_list - > set_fixed_column_width ( 0 ) ;
2025-06-30 19:07:12 +02:00
file_list - > set_fixed_icon_size ( theme_cache . file - > get_size ( ) ) ;
2025-04-28 14:57:50 +02:00
}
2016-03-09 00:00:52 +01:00
2025-04-28 14:57:50 +02:00
dir_access - > list_dir_begin ( ) ;
2021-11-30 15:19:26 +01:00
if ( dir_access - > is_readable ( dir_access - > get_current_dir ( ) . utf8 ( ) . get_data ( ) ) ) {
message - > hide ( ) ;
} else {
2023-12-15 20:56:06 -03:00
message - > set_text ( ETR ( " You don't have permission to access contents of this folder. " ) ) ;
2021-11-30 15:19:26 +01:00
message - > show ( ) ;
}
2025-04-22 11:04:16 +02:00
LocalVector < String > files ;
LocalVector < String > dirs ;
2016-03-09 00:00:52 +01:00
2019-06-10 00:20:24 -03:00
bool is_hidden ;
2021-11-30 15:19:26 +01:00
String item = dir_access - > get_next ( ) ;
2021-03-19 14:01:03 +02:00
2021-11-30 15:19:26 +01:00
while ( ! item . is_empty ( ) ) {
2020-05-14 16:41:43 +02:00
if ( item = = " . " | | item = = " .. " ) {
2021-11-30 15:19:26 +01:00
item = dir_access - > get_next ( ) ;
2017-11-21 02:58:07 +03:00
continue ;
2020-05-14 16:41:43 +02:00
}
2017-11-21 02:58:07 +03:00
2019-06-10 00:20:24 -03:00
is_hidden = dir_access - > current_is_hidden ( ) ;
2015-03-21 18:33:32 +01:00
2019-06-10 00:20:24 -03:00
if ( show_hidden_files | | ! is_hidden ) {
2020-05-14 16:41:43 +02:00
if ( ! dir_access - > current_is_dir ( ) ) {
2015-03-21 18:33:32 +01:00
files . push_back ( item ) ;
2020-05-14 16:41:43 +02:00
} else {
2015-03-21 18:33:32 +01:00
dirs . push_back ( item ) ;
2020-05-14 16:41:43 +02:00
}
2015-03-21 18:33:32 +01:00
}
2021-11-30 15:19:26 +01:00
item = dir_access - > get_next ( ) ;
2014-02-09 22:10:30 -03:00
}
2016-03-09 00:00:52 +01:00
2024-02-22 18:34:46 +01:00
String filename_filter_lower = file_name_filter . to_lower ( ) ;
2014-02-09 22:10:30 -03:00
List < String > patterns ;
2025-04-24 19:58:17 +02:00
// Build filter.
2014-02-09 22:10:30 -03:00
if ( filter - > get_selected ( ) = = filter - > get_item_count ( ) - 1 ) {
2025-04-24 19:58:17 +02:00
// Match all.
2014-02-09 22:10:30 -03:00
} else if ( filters . size ( ) > 1 & & filter - > get_selected ( ) = = 0 ) {
2025-04-24 19:58:17 +02:00
// Match all filters.
2014-02-09 22:10:30 -03:00
for ( int i = 0 ; i < filters . size ( ) ; i + + ) {
2024-11-16 17:16:07 +01:00
String f = filters [ i ] . get_slicec ( ' ; ' , 0 ) ;
2014-02-09 22:10:30 -03:00
for ( int j = 0 ; j < f . get_slice_count ( " , " ) ; j + + ) {
2024-11-16 17:16:07 +01:00
patterns . push_back ( f . get_slicec ( ' , ' , j ) . strip_edges ( ) ) ;
2014-02-09 22:10:30 -03:00
}
}
} else {
int idx = filter - > get_selected ( ) ;
2020-05-14 16:41:43 +02:00
if ( filters . size ( ) > 1 ) {
2014-02-09 22:10:30 -03:00
idx - - ;
2020-05-14 16:41:43 +02:00
}
2016-03-09 00:00:52 +01:00
2014-02-09 22:10:30 -03:00
if ( idx > = 0 & & idx < filters . size ( ) ) {
2024-11-16 17:16:07 +01:00
String f = filters [ idx ] . get_slicec ( ' ; ' , 0 ) ;
2014-02-09 22:10:30 -03:00
for ( int j = 0 ; j < f . get_slice_count ( " , " ) ; j + + ) {
2024-11-16 17:16:07 +01:00
patterns . push_back ( f . get_slicec ( ' , ' , j ) . strip_edges ( ) ) ;
2016-03-09 00:00:52 +01:00
}
2014-02-09 22:10:30 -03:00
}
}
2025-04-24 19:58:17 +02:00
LocalVector < DirInfo > filtered_dirs ;
filtered_dirs . reserve ( dirs . size ( ) ) ;
const String base_dir = dir_access - > get_current_dir ( ) ;
2025-04-22 11:04:16 +02:00
for ( const String & dir_name : dirs ) {
2024-11-04 11:50:42 +02:00
bool bundle = dir_access - > is_bundle ( dir_name ) ;
bool found = true ;
if ( bundle ) {
bool match = patterns . is_empty ( ) ;
for ( const String & E : patterns ) {
if ( dir_name . matchn ( E ) ) {
match = true ;
break ;
}
}
found = match ;
}
if ( found & & ( filename_filter_lower . is_empty ( ) | | dir_name . to_lower ( ) . contains ( filename_filter_lower ) ) ) {
2025-04-24 19:58:17 +02:00
DirInfo di ;
di . name = dir_name ;
di . bundle = bundle ;
if ( file_sort = = FileSortOption : : MODIFIED_TIME | | file_sort = = FileSortOption : : MODIFIED_TIME_REVERSE ) {
di . modified_time = FileAccess : : get_modified_time ( base_dir . path_join ( dir_name ) ) ;
}
filtered_dirs . push_back ( di ) ;
2024-11-04 11:50:42 +02:00
}
}
2025-04-24 19:58:17 +02:00
if ( file_sort = = FileSortOption : : MODIFIED_TIME | | file_sort = = FileSortOption : : MODIFIED_TIME_REVERSE ) {
filtered_dirs . sort_custom < DirInfo : : TimeComparator > ( ) ;
} else {
filtered_dirs . sort_custom < DirInfo : : NameComparator > ( ) ;
}
if ( file_sort = = FileSortOption : : NAME_REVERSE | | file_sort = = FileSortOption : : MODIFIED_TIME_REVERSE ) {
filtered_dirs . reverse ( ) ;
}
for ( const DirInfo & info : filtered_dirs ) {
2025-04-28 14:57:50 +02:00
if ( display_mode = = DISPLAY_THUMBNAILS ) {
2025-07-20 20:44:33 +03:00
file_list - > add_item ( info . name , info . bundle ? theme_cache . file_thumbnail : theme_cache . folder_thumbnail ) ;
2025-04-28 14:57:50 +02:00
} else {
2025-07-20 20:44:33 +03:00
file_list - > add_item ( info . name , info . bundle ? theme_cache . file : theme_cache . folder ) ;
2025-04-28 14:57:50 +02:00
}
2025-04-24 19:58:17 +02:00
file_list - > set_item_icon_modulate ( - 1 , theme_cache . folder_icon_color ) ;
Dictionary d ;
d [ " name " ] = info . name ;
d [ " dir " ] = ! info . bundle ;
d [ " bundle " ] = info . bundle ;
file_list - > set_item_metadata ( - 1 , d ) ;
}
LocalVector < FileInfo > filtered_files ;
filtered_files . reserve ( files . size ( ) ) ;
2016-03-09 00:00:52 +01:00
2025-04-22 11:04:16 +02:00
for ( const String & filename : files ) {
2020-12-15 12:04:21 +00:00
bool match = patterns . is_empty ( ) ;
2016-11-15 22:36:41 +09:00
String match_str ;
2016-03-09 00:00:52 +01:00
2021-07-24 15:46:25 +02:00
for ( const String & E : patterns ) {
2025-04-22 11:04:16 +02:00
if ( filename . matchn ( E ) ) {
2021-07-15 23:45:57 -04:00
match_str = E ;
2014-02-09 22:10:30 -03:00
match = true ;
break ;
}
}
2016-03-09 00:00:52 +01:00
2025-04-22 11:04:16 +02:00
if ( match & & ( filename_filter_lower . is_empty ( ) | | filename . to_lower ( ) . contains ( filename_filter_lower ) ) ) {
2025-04-24 19:58:17 +02:00
FileInfo fi ;
fi . name = filename ;
fi . match_string = match_str ;
// Only assign sorting fields when needed.
if ( file_sort = = FileSortOption : : TYPE | | file_sort = = FileSortOption : : TYPE_REVERSE ) {
fi . type_sort = filename . get_extension ( ) + filename . get_basename ( ) ;
} else if ( file_sort = = FileSortOption : : MODIFIED_TIME | | file_sort = = FileSortOption : : MODIFIED_TIME_REVERSE ) {
fi . modified_time = FileAccess : : get_modified_time ( base_dir . path_join ( filename ) ) ;
2020-05-14 16:41:43 +02:00
}
2025-04-24 19:58:17 +02:00
filtered_files . push_back ( fi ) ;
}
}
switch ( file_sort ) {
case FileSortOption : : NAME :
case FileSortOption : : NAME_REVERSE :
filtered_files . sort_custom < FileInfo : : NameComparator > ( ) ;
break ;
case FileSortOption : : TYPE :
case FileSortOption : : TYPE_REVERSE :
filtered_files . sort_custom < FileInfo : : TypeComparator > ( ) ;
break ;
case FileSortOption : : MODIFIED_TIME :
case FileSortOption : : MODIFIED_TIME_REVERSE :
filtered_files . sort_custom < FileInfo : : TimeComparator > ( ) ;
break ;
default :
ERR_PRINT ( vformat ( " Invalid FileDialog sort option: %d " , int ( file_sort ) ) ) ;
}
if ( file_sort = = FileSortOption : : NAME_REVERSE | | file_sort = = FileSortOption : : TYPE_REVERSE | | file_sort = = FileSortOption : : MODIFIED_TIME_REVERSE ) {
filtered_files . reverse ( ) ;
}
for ( const FileInfo & info : filtered_files ) {
2025-04-28 14:57:50 +02:00
file_list - > add_item ( info . name ) ;
2025-06-30 19:07:12 +02:00
const String path = base_dir . path_join ( info . name ) ;
if ( display_mode = = DISPLAY_LIST ) {
Ref < Texture2D > icon ;
if ( get_icon_callback . is_valid ( ) ) {
const Variant & v = path ;
const Variant * argptrs [ 1 ] = { & v } ;
Variant vicon ;
Callable : : CallError ce ;
get_icon_callback . callp ( argptrs , 1 , vicon , ce ) ;
if ( unlikely ( ce . error ! = Callable : : CallError : : CALL_OK ) ) {
ERR_PRINT ( vformat ( " Error calling FileDialog's icon callback: %s. " , Variant : : get_callable_error_text ( get_icon_callback , argptrs , 1 , ce ) ) ) ;
}
icon = vicon ;
}
if ( icon . is_null ( ) ) {
icon = theme_cache . file ;
}
file_list - > set_item_icon ( - 1 , icon ) ;
} else { // DISPLAY_THUMBNAILS
Ref < Texture2D > icon ;
if ( get_thumbnail_callback . is_valid ( ) ) {
const Variant & v = path ;
const Variant * argptrs [ 1 ] = { & v } ;
Variant vicon ;
Callable : : CallError ce ;
get_thumbnail_callback . callp ( argptrs , 1 , vicon , ce ) ;
if ( unlikely ( ce . error ! = Callable : : CallError : : CALL_OK ) ) {
ERR_PRINT ( vformat ( " Error calling FileDialog's thumbnail callback: %s. " , Variant : : get_callable_error_text ( get_thumbnail_callback , argptrs , 1 , ce ) ) ) ;
}
icon = vicon ;
}
if ( icon . is_null ( ) ) {
icon = theme_cache . file ;
}
2025-04-28 14:57:50 +02:00
file_list - > set_item_icon ( - 1 , icon ) ;
}
2025-04-24 19:58:17 +02:00
file_list - > set_item_icon_modulate ( - 1 , theme_cache . file_icon_color ) ;
if ( mode = = FILE_MODE_OPEN_DIR ) {
file_list - > set_item_disabled ( - 1 , true ) ;
}
Dictionary d ;
d [ " name " ] = info . name ;
d [ " dir " ] = false ;
d [ " bundle " ] = false ;
file_list - > set_item_metadata ( - 1 , d ) ;
if ( filename_edit - > get_text ( ) = = info . name | | info . match_string = = info . name ) {
file_list - > select ( file_list - > get_item_count ( ) - 1 ) ;
2014-02-09 22:10:30 -03:00
}
}
2016-03-09 00:00:52 +01:00
2023-08-27 17:25:17 +08:00
if ( mode ! = FILE_MODE_SAVE_FILE & & mode ! = FILE_MODE_OPEN_DIR ) {
2022-11-13 02:24:17 +01:00
// Select the first file from list if nothing is selected.
2025-04-22 11:04:16 +02:00
int selected = _get_selected_file_idx ( ) ;
if ( selected = = - 1 ) {
_file_list_select_first ( ) ;
2022-11-13 02:24:17 +01:00
}
2020-05-14 16:41:43 +02:00
}
2025-04-23 00:11:36 +02:00
favorite_list - > deselect_all ( ) ;
favorite_button - > set_pressed ( false ) ;
const int fav_count = favorite_list - > get_item_count ( ) ;
for ( int i = 0 ; i < fav_count ; i + + ) {
const String fav_dir = favorite_list - > get_item_metadata ( i ) ;
if ( fav_dir ! = base_dir & & fav_dir ! = base_dir + " / " ) {
continue ;
}
favorite_list - > select ( i ) ;
favorite_button - > set_pressed ( true ) ;
break ;
}
_update_fav_buttons ( ) ;
2014-02-09 22:10:30 -03:00
}
void FileDialog : : _filter_selected ( int ) {
2019-06-28 10:29:50 +03:00
update_file_name ( ) ;
2014-02-09 22:10:30 -03:00
update_file_list ( ) ;
}
2024-02-22 18:34:46 +01:00
void FileDialog : : _filename_filter_changed ( ) {
update_filename_filter ( ) ;
update_file_list ( ) ;
2025-04-22 11:04:16 +02:00
callable_mp ( this , & FileDialog : : _file_list_select_first ) . call_deferred ( ) ;
2024-02-22 18:34:46 +01:00
}
2025-04-22 11:04:16 +02:00
void FileDialog : : _file_list_select_first ( ) {
if ( file_list - > get_item_count ( ) > 0 ) {
file_list - > select ( 0 ) ;
_file_list_selected ( 0 ) ;
2024-02-22 18:34:46 +01:00
}
}
2025-10-01 14:49:52 +02:00
void FileDialog : : _delete_confirm ( ) {
OS : : get_singleton ( ) - > move_to_trash ( _get_item_path ( _get_selected_file_idx ( ) ) ) ;
invalidate ( ) ;
}
2024-02-22 18:34:46 +01:00
void FileDialog : : _filename_filter_selected ( ) {
2025-04-22 11:04:16 +02:00
int selected = _get_selected_file_idx ( ) ;
if ( selected > - 1 ) {
filename_edit - > set_text ( file_list - > get_item_text ( selected ) ) ;
2025-04-22 00:23:16 +02:00
filename_edit - > emit_signal ( SceneStringName ( text_submitted ) , filename_edit - > get_text ( ) ) ;
2024-02-22 18:34:46 +01:00
}
}
2014-02-09 22:10:30 -03:00
void FileDialog : : update_filters ( ) {
filter - > clear ( ) ;
2024-11-15 08:43:07 +02:00
processed_filters . clear ( ) ;
2016-03-09 00:00:52 +01:00
2014-02-09 22:10:30 -03:00
if ( filters . size ( ) > 1 ) {
String all_filters ;
2024-11-17 12:25:23 +02:00
String all_mime ;
2024-11-15 08:43:07 +02:00
String all_filters_full ;
2024-11-17 12:25:23 +02:00
String all_mime_full ;
2014-02-09 22:10:30 -03:00
const int max_filters = 5 ;
2016-03-09 00:00:52 +01:00
2024-11-17 12:25:23 +02:00
// "All Recognized" display name.
2014-02-09 22:10:30 -03:00
for ( int i = 0 ; i < MIN ( max_filters , filters . size ( ) ) ; i + + ) {
2024-11-15 08:43:07 +02:00
String flt = filters [ i ] . get_slicec ( ' ; ' , 0 ) . strip_edges ( ) ;
2024-11-17 12:25:23 +02:00
if ( ! all_filters . is_empty ( ) & & ! flt . is_empty ( ) ) {
2019-12-06 22:40:59 -03:00
all_filters + = " , " ;
2020-05-14 16:41:43 +02:00
}
2014-02-09 22:10:30 -03:00
all_filters + = flt ;
2024-11-17 12:25:23 +02:00
String mime = filters [ i ] . get_slicec ( ' ; ' , 2 ) . strip_edges ( ) ;
if ( ! all_mime . is_empty ( ) & & ! mime . is_empty ( ) ) {
all_mime + = " , " ;
}
all_mime + = mime ;
2014-02-09 22:10:30 -03:00
}
2024-11-17 12:25:23 +02:00
// "All Recognized" filter.
2024-11-15 08:43:07 +02:00
for ( int i = 0 ; i < filters . size ( ) ; i + + ) {
String flt = filters [ i ] . get_slicec ( ' ; ' , 0 ) . strip_edges ( ) ;
2024-11-17 12:25:23 +02:00
if ( ! all_filters_full . is_empty ( ) & & ! flt . is_empty ( ) ) {
2024-11-15 08:43:07 +02:00
all_filters_full + = " , " ;
}
all_filters_full + = flt ;
2024-11-17 12:25:23 +02:00
String mime = filters [ i ] . get_slicec ( ' ; ' , 2 ) . strip_edges ( ) ;
if ( ! all_mime_full . is_empty ( ) & & ! mime . is_empty ( ) ) {
all_mime_full + = " , " ;
}
all_mime_full + = mime ;
2024-11-15 08:43:07 +02:00
}
2014-02-09 22:10:30 -03:00
2024-11-17 12:25:23 +02:00
String native_all_name ;
2025-10-11 18:50:09 -03:00
native_all_name + = all_filters ;
2024-11-17 12:25:23 +02:00
if ( DisplayServer : : get_singleton ( ) - > has_feature ( DisplayServer : : FEATURE_NATIVE_DIALOG_FILE_MIME ) ) {
2025-10-11 18:50:09 -03:00
if ( ! native_all_name . is_empty ( ) ) {
native_all_name + = " , " ;
}
native_all_name + = all_mime ;
2024-11-17 12:25:23 +02:00
}
2020-05-14 16:41:43 +02:00
if ( max_filters < filters . size ( ) ) {
2014-02-09 22:10:30 -03:00
all_filters + = " , ... " ;
2024-11-17 12:25:23 +02:00
native_all_name + = " , ... " ;
2020-05-14 16:41:43 +02:00
}
2016-03-09 00:00:52 +01:00
2024-11-17 12:25:23 +02:00
filter - > add_item ( atr ( ETR ( " All Recognized " ) ) + " ( " + all_filters + " ) " ) ;
processed_filters . push_back ( all_filters_full + " ; " + atr ( ETR ( " All Recognized " ) ) + " ( " + native_all_name + " ) " + " ; " + all_mime_full ) ;
2014-02-09 22:10:30 -03:00
}
for ( int i = 0 ; i < filters . size ( ) ; i + + ) {
2024-11-15 08:43:07 +02:00
String flt = filters [ i ] . get_slicec ( ' ; ' , 0 ) . strip_edges ( ) ;
2024-11-17 12:25:23 +02:00
String desc = filters [ i ] . get_slicec ( ' ; ' , 1 ) . strip_edges ( ) ;
String mime = filters [ i ] . get_slicec ( ' ; ' , 2 ) . strip_edges ( ) ;
String native_name ;
2025-10-11 18:50:09 -03:00
native_name + = flt ;
2024-11-17 12:25:23 +02:00
if ( DisplayServer : : get_singleton ( ) - > has_feature ( DisplayServer : : FEATURE_NATIVE_DIALOG_FILE_MIME ) ) {
2025-10-11 18:50:09 -03:00
if ( ! native_name . is_empty ( ) & & ! mime . is_empty ( ) ) {
native_name + = " , " ;
}
native_name + = mime ;
2024-11-17 12:25:23 +02:00
}
if ( ! desc . is_empty ( ) ) {
filter - > add_item ( atr ( desc ) + " ( " + flt + " ) " ) ;
processed_filters . push_back ( flt + " ; " + atr ( desc ) + " ( " + native_name + " ); " + mime ) ;
2020-05-14 16:41:43 +02:00
} else {
2024-11-17 12:25:23 +02:00
filter - > add_item ( " ( " + flt + " ) " ) ;
processed_filters . push_back ( flt + " ;( " + native_name + " ); " + mime ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-09 22:10:30 -03:00
}
2016-03-09 00:00:52 +01:00
2024-11-24 20:09:05 +02:00
String f = atr ( ETR ( " All Files " ) ) + " (*.*) " ;
2024-11-15 08:43:07 +02:00
filter - > add_item ( f ) ;
2024-11-17 12:25:23 +02:00
processed_filters . push_back ( " *.*; " + f + " ;application/octet-stream " ) ;
2014-02-09 22:10:30 -03:00
}
2025-05-21 16:41:18 +02:00
void FileDialog : : update_customization ( ) {
_update_make_dir_visible ( ) ;
show_hidden - > set_visible ( customization_flags [ CUSTOMIZATION_HIDDEN_FILES ] ) ;
layout_container - > set_visible ( customization_flags [ CUSTOMIZATION_LAYOUT ] ) ;
layout_separator - > set_visible ( customization_flags [ CUSTOMIZATION_FILE_FILTER ] | | customization_flags [ CUSTOMIZATION_FILE_SORT ] ) ;
show_filename_filter_button - > set_visible ( customization_flags [ CUSTOMIZATION_FILE_FILTER ] ) ;
file_sort_button - > set_visible ( customization_flags [ CUSTOMIZATION_FILE_SORT ] ) ;
show_hidden_separator - > set_visible ( customization_flags [ CUSTOMIZATION_HIDDEN_FILES ] & & ( customization_flags [ CUSTOMIZATION_LAYOUT ] | | customization_flags [ CUSTOMIZATION_FILE_FILTER ] | | customization_flags [ CUSTOMIZATION_FILE_SORT ] ) ) ;
favorite_button - > set_visible ( customization_flags [ CUSTOMIZATION_FAVORITES ] ) ;
favorite_vbox - > set_visible ( customization_flags [ CUSTOMIZATION_FAVORITES ] ) ;
recent_vbox - > set_visible ( customization_flags [ CUSTOMIZATION_RECENT ] ) ;
}
2024-02-22 18:34:46 +01:00
void FileDialog : : clear_filename_filter ( ) {
set_filename_filter ( " " ) ;
update_filename_filter_gui ( ) ;
invalidate ( ) ;
}
void FileDialog : : update_filename_filter_gui ( ) {
filename_filter_box - > set_visible ( show_filename_filter ) ;
if ( ! show_filename_filter ) {
file_name_filter . clear ( ) ;
}
if ( filename_filter - > get_text ( ) = = file_name_filter ) {
return ;
}
filename_filter - > set_text ( file_name_filter ) ;
}
void FileDialog : : update_filename_filter ( ) {
if ( filename_filter - > get_text ( ) = = file_name_filter ) {
return ;
}
set_filename_filter ( filename_filter - > get_text ( ) ) ;
}
2014-02-09 22:10:30 -03:00
void FileDialog : : clear_filters ( ) {
filters . clear ( ) ;
update_filters ( ) ;
invalidate ( ) ;
}
2020-05-14 14:29:06 +02:00
2022-07-04 16:26:26 -05:00
void FileDialog : : add_filter ( const String & p_filter , const String & p_description ) {
2022-01-08 16:27:15 +01:00
ERR_FAIL_COND_MSG ( p_filter . begins_with ( " . " ) , " Filter must be \" filename.extension \" , can't start with dot. " ) ;
2022-07-04 16:26:26 -05:00
if ( p_description . is_empty ( ) ) {
filters . push_back ( p_filter ) ;
} else {
filters . push_back ( vformat ( " %s ; %s " , p_filter , p_description ) ) ;
}
2014-02-09 22:10:30 -03:00
update_filters ( ) ;
invalidate ( ) ;
}
2016-07-17 01:58:28 +02:00
void FileDialog : : set_filters ( const Vector < String > & p_filters ) {
2022-03-16 15:50:48 +08:00
if ( filters = = p_filters ) {
return ;
}
2016-07-17 01:58:28 +02:00
filters = p_filters ;
update_filters ( ) ;
invalidate ( ) ;
}
2024-02-22 18:34:46 +01:00
void FileDialog : : set_filename_filter ( const String & p_filename_filter ) {
if ( file_name_filter = = p_filename_filter ) {
return ;
}
file_name_filter = p_filename_filter ;
update_filename_filter_gui ( ) ;
emit_signal ( SNAME ( " filename_filter_changed " ) , filter ) ;
invalidate ( ) ;
}
2016-07-17 01:58:28 +02:00
Vector < String > FileDialog : : get_filters ( ) const {
return filters ;
}
2024-02-22 18:34:46 +01:00
String FileDialog : : get_filename_filter ( ) const {
return file_name_filter ;
}
2014-02-09 22:10:30 -03:00
String FileDialog : : get_current_dir ( ) const {
2024-11-24 23:49:46 +02:00
return full_dir ;
2014-02-09 22:10:30 -03:00
}
2020-05-14 14:29:06 +02:00
2014-02-09 22:10:30 -03:00
String FileDialog : : get_current_file ( ) const {
2025-04-22 00:23:16 +02:00
return filename_edit - > get_text ( ) ;
2014-02-09 22:10:30 -03:00
}
2020-05-14 14:29:06 +02:00
2014-02-09 22:10:30 -03:00
String FileDialog : : get_current_path ( ) const {
2025-04-22 00:23:16 +02:00
return full_dir . path_join ( filename_edit - > get_text ( ) ) ;
2014-02-09 22:10:30 -03:00
}
2020-05-14 14:29:06 +02:00
2014-02-09 22:10:30 -03:00
void FileDialog : : set_current_dir ( const String & p_dir ) {
2022-03-13 01:35:20 +01:00
_change_dir ( p_dir ) ;
2022-03-16 15:50:48 +08:00
2021-03-19 14:01:03 +02:00
_push_history ( ) ;
2014-02-09 22:10:30 -03:00
}
2020-05-14 14:29:06 +02:00
2014-02-09 22:10:30 -03:00
void FileDialog : : set_current_file ( const String & p_file ) {
2025-04-22 00:23:16 +02:00
if ( filename_edit - > get_text ( ) = = p_file ) {
2022-03-16 15:50:48 +08:00
return ;
}
2025-04-22 00:23:16 +02:00
filename_edit - > set_text ( p_file ) ;
2014-02-09 22:10:30 -03:00
update_dir ( ) ;
invalidate ( ) ;
2022-01-03 17:08:03 +08:00
_focus_file_text ( ) ;
2014-02-09 22:10:30 -03:00
}
2020-05-14 14:29:06 +02:00
2014-02-09 22:10:30 -03:00
void FileDialog : : set_current_path ( const String & p_path ) {
2020-05-14 16:41:43 +02:00
if ( ! p_path . size ( ) ) {
2014-02-09 22:10:30 -03:00
return ;
2020-05-14 16:41:43 +02:00
}
2024-11-16 18:52:15 +01:00
int pos = MAX ( p_path . rfind_char ( ' / ' ) , p_path . rfind_char ( ' \\ ' ) ) ;
2014-02-09 22:10:30 -03:00
if ( pos = = - 1 ) {
set_current_file ( p_path ) ;
} else {
2022-09-29 12:53:28 +03:00
String path_dir = p_path . substr ( 0 , pos ) ;
2025-02-26 11:41:11 +01:00
String path_file = p_path . substr ( pos + 1 ) ;
2022-09-29 12:53:28 +03:00
set_current_dir ( path_dir ) ;
set_current_file ( path_file ) ;
2014-02-09 22:10:30 -03:00
}
}
2022-03-13 01:35:20 +01:00
void FileDialog : : set_root_subfolder ( const String & p_root ) {
root_subfolder = p_root ;
ERR_FAIL_COND_MSG ( ! dir_access - > dir_exists ( p_root ) , " root_subfolder must be an existing sub-directory. " ) ;
local_history . clear ( ) ;
local_history_pos = - 1 ;
dir_access - > change_dir ( root_subfolder ) ;
if ( root_subfolder . is_empty ( ) ) {
root_prefix = " " ;
} else {
root_prefix = dir_access - > get_current_dir ( ) ;
}
invalidate ( ) ;
update_dir ( ) ;
}
String FileDialog : : get_root_subfolder ( ) const {
return root_subfolder ;
}
2017-12-02 23:54:06 -02:00
void FileDialog : : set_mode_overrides_title ( bool p_override ) {
mode_overrides_title = p_override ;
}
bool FileDialog : : is_mode_overriding_title ( ) const {
return mode_overrides_title ;
}
2020-03-06 14:00:16 -03:00
void FileDialog : : set_file_mode ( FileMode p_mode ) {
2019-10-05 19:17:07 +02:00
ERR_FAIL_INDEX ( ( int ) p_mode , 5 ) ;
2022-03-16 15:50:48 +08:00
if ( mode = = p_mode ) {
return ;
}
2014-02-09 22:10:30 -03:00
mode = p_mode ;
switch ( mode ) {
2020-03-06 14:00:16 -03:00
case FILE_MODE_OPEN_FILE :
2025-04-19 00:57:00 +02:00
set_default_ok_text ( ETR ( " Open " ) ) ;
2020-05-14 16:41:43 +02:00
if ( mode_overrides_title ) {
2023-12-15 20:56:06 -03:00
set_title ( ETR ( " Open a File " ) ) ;
2020-05-14 16:41:43 +02:00
}
2016-06-07 23:24:32 +03:00
break ;
2020-03-06 14:00:16 -03:00
case FILE_MODE_OPEN_FILES :
2025-04-19 00:57:00 +02:00
set_default_ok_text ( ETR ( " Open " ) ) ;
2020-05-14 16:41:43 +02:00
if ( mode_overrides_title ) {
2023-12-15 20:56:06 -03:00
set_title ( ETR ( " Open File(s) " ) ) ;
2020-05-14 16:41:43 +02:00
}
2016-06-07 23:24:32 +03:00
break ;
2020-03-06 14:00:16 -03:00
case FILE_MODE_OPEN_DIR :
2025-04-19 00:57:00 +02:00
set_default_ok_text ( ETR ( " Select Current Folder " ) ) ;
2020-05-14 16:41:43 +02:00
if ( mode_overrides_title ) {
2023-12-15 20:56:06 -03:00
set_title ( ETR ( " Open a Directory " ) ) ;
2020-05-14 16:41:43 +02:00
}
2016-06-07 23:24:32 +03:00
break ;
2020-03-06 14:00:16 -03:00
case FILE_MODE_OPEN_ANY :
2025-04-19 00:57:00 +02:00
set_default_ok_text ( ETR ( " Open " ) ) ;
2020-05-14 16:41:43 +02:00
if ( mode_overrides_title ) {
2023-12-15 20:56:06 -03:00
set_title ( ETR ( " Open a File or Directory " ) ) ;
2020-05-14 16:41:43 +02:00
}
2025-04-22 00:23:16 +02:00
make_dir_button - > show ( ) ;
2016-06-07 23:24:32 +03:00
break ;
2020-03-06 14:00:16 -03:00
case FILE_MODE_SAVE_FILE :
2025-04-19 00:57:00 +02:00
set_default_ok_text ( ETR ( " Save " ) ) ;
2020-05-14 16:41:43 +02:00
if ( mode_overrides_title ) {
2023-12-15 20:56:06 -03:00
set_title ( ETR ( " Save a File " ) ) ;
2020-05-14 16:41:43 +02:00
}
2016-06-07 23:24:32 +03:00
break ;
2014-02-09 22:10:30 -03:00
}
2025-05-21 16:41:18 +02:00
_update_make_dir_visible ( ) ;
2014-02-09 22:10:30 -03:00
2020-03-06 14:00:16 -03:00
if ( mode = = FILE_MODE_OPEN_FILES ) {
2025-04-22 11:04:16 +02:00
file_list - > set_select_mode ( ItemList : : SELECT_MULTI ) ;
2014-02-09 22:10:30 -03:00
} else {
2025-04-22 11:04:16 +02:00
file_list - > set_select_mode ( ItemList : : SELECT_SINGLE ) ;
2014-02-09 22:10:30 -03:00
}
2023-07-08 18:10:57 +02:00
get_ok_button ( ) - > set_disabled ( _is_open_should_be_disabled ( ) ) ;
2014-02-09 22:10:30 -03:00
}
2020-03-06 14:00:16 -03:00
FileDialog : : FileMode FileDialog : : get_file_mode ( ) const {
2014-02-09 22:10:30 -03:00
return mode ;
}
2025-04-28 14:57:50 +02:00
void FileDialog : : set_display_mode ( DisplayMode p_mode ) {
ERR_FAIL_INDEX ( ( int ) p_mode , 2 ) ;
if ( display_mode = = p_mode ) {
return ;
}
display_mode = p_mode ;
if ( p_mode = = DISPLAY_THUMBNAILS ) {
thumbnail_mode_button - > set_pressed ( true ) ;
list_mode_button - > set_pressed ( false ) ;
} else {
thumbnail_mode_button - > set_pressed ( false ) ;
list_mode_button - > set_pressed ( true ) ;
}
invalidate ( ) ;
}
FileDialog : : DisplayMode FileDialog : : get_display_mode ( ) const {
return display_mode ;
}
2025-06-30 16:43:34 +02:00
void FileDialog : : set_favorite_list ( const PackedStringArray & p_favorites ) {
ERR_FAIL_COND_MSG ( Thread : : get_caller_id ( ) ! = Thread : : get_main_id ( ) , " Setting favorite list can only be done on the main thread. " ) ;
global_favorites . clear ( ) ;
global_favorites . reserve ( p_favorites . size ( ) ) ;
for ( const String & fav : p_favorites ) {
if ( fav . ends_with ( " / " ) ) {
global_favorites . push_back ( fav ) ;
} else {
global_favorites . push_back ( fav + " / " ) ;
}
}
}
PackedStringArray FileDialog : : get_favorite_list ( ) {
PackedStringArray ret ;
ERR_FAIL_COND_V_MSG ( Thread : : get_caller_id ( ) ! = Thread : : get_main_id ( ) , ret , " Getting favorite list can only be done on the main thread. " ) ;
ret . resize ( global_favorites . size ( ) ) ;
String * fav_write = ret . ptrw ( ) ;
int i = 0 ;
for ( const String & fav : global_favorites ) {
fav_write [ i ] = fav ;
i + + ;
}
return ret ;
}
void FileDialog : : set_recent_list ( const PackedStringArray & p_recents ) {
ERR_FAIL_COND_MSG ( Thread : : get_caller_id ( ) ! = Thread : : get_main_id ( ) , " Setting recent list can only be done on the main thread. " ) ;
global_recents . clear ( ) ;
global_recents . reserve ( p_recents . size ( ) ) ;
for ( const String & recent : p_recents ) {
if ( recent . ends_with ( " / " ) ) {
global_recents . push_back ( recent ) ;
} else {
global_recents . push_back ( recent + " / " ) ;
}
}
}
PackedStringArray FileDialog : : get_recent_list ( ) {
PackedStringArray ret ;
ERR_FAIL_COND_V_MSG ( Thread : : get_caller_id ( ) ! = Thread : : get_main_id ( ) , ret , " Getting recent list can only be done on the main thread. " ) ;
ret . resize ( global_recents . size ( ) ) ;
String * recent_write = ret . ptrw ( ) ;
int i = 0 ;
for ( const String & recent : global_recents ) {
recent_write [ i ] = recent ;
i + + ;
}
return ret ;
}
2025-05-21 16:41:18 +02:00
void FileDialog : : set_customization_flag_enabled ( Customization p_flag , bool p_enabled ) {
ERR_FAIL_INDEX ( p_flag , CUSTOMIZATION_MAX ) ;
if ( customization_flags [ p_flag ] = = p_enabled ) {
return ;
}
customization_flags [ p_flag ] = p_enabled ;
update_customization ( ) ;
}
bool FileDialog : : is_customization_flag_enabled ( Customization p_flag ) const {
ERR_FAIL_INDEX_V ( p_flag , CUSTOMIZATION_MAX , false ) ;
return customization_flags [ p_flag ] ;
}
2014-02-09 22:10:30 -03:00
void FileDialog : : set_access ( Access p_access ) {
ERR_FAIL_INDEX ( p_access , 3 ) ;
2020-05-14 16:41:43 +02:00
if ( access = = p_access ) {
2014-02-09 22:10:30 -03:00
return ;
2020-05-14 16:41:43 +02:00
}
2024-11-16 14:27:05 +05:30
access = p_access ;
root_prefix = " " ;
root_subfolder = " " ;
2014-02-09 22:10:30 -03:00
switch ( p_access ) {
case ACCESS_FILESYSTEM : {
dir_access = DirAccess : : create ( DirAccess : : ACCESS_FILESYSTEM ) ;
2024-11-16 14:27:05 +05:30
# ifdef ANDROID_ENABLED
2025-08-11 21:47:00 +05:30
set_current_dir ( OS : : get_singleton ( ) - > get_system_dir ( OS : : SYSTEM_DIR_DESKTOP ) ) ;
2024-11-16 14:27:05 +05:30
# endif
2014-02-09 22:10:30 -03:00
} break ;
case ACCESS_RESOURCES : {
dir_access = DirAccess : : create ( DirAccess : : ACCESS_RESOURCES ) ;
} break ;
case ACCESS_USERDATA : {
dir_access = DirAccess : : create ( DirAccess : : ACCESS_USERDATA ) ;
} break ;
}
_update_drives ( ) ;
invalidate ( ) ;
update_filters ( ) ;
update_dir ( ) ;
2025-04-23 00:11:36 +02:00
_update_favorite_list ( ) ;
_update_recent_list ( ) ;
2014-02-09 22:10:30 -03:00
}
void FileDialog : : invalidate ( ) {
2023-01-22 14:26:32 -03:00
if ( ! is_visible ( ) | | is_invalidating ) {
return ;
2014-02-09 22:10:30 -03:00
}
2023-01-22 14:26:32 -03:00
is_invalidating = true ;
callable_mp ( this , & FileDialog : : _invalidate ) . call_deferred ( ) ;
}
void FileDialog : : _invalidate ( ) {
if ( ! is_invalidating ) {
return ;
}
update_file_list ( ) ;
is_invalidating = false ;
2014-02-09 22:10:30 -03:00
}
2025-04-22 00:23:16 +02:00
void FileDialog : : _setup_button ( Button * p_button , const Ref < Texture2D > & p_icon ) {
p_button - > set_button_icon ( p_icon ) ;
p_button - > begin_bulk_theme_override ( ) ;
p_button - > add_theme_color_override ( SNAME ( " icon_normal_color " ) , theme_cache . icon_normal_color ) ;
p_button - > add_theme_color_override ( SNAME ( " icon_hover_color " ) , theme_cache . icon_hover_color ) ;
p_button - > add_theme_color_override ( SNAME ( " icon_focus_color " ) , theme_cache . icon_focus_color ) ;
p_button - > add_theme_color_override ( SNAME ( " icon_pressed_color " ) , theme_cache . icon_pressed_color ) ;
p_button - > end_bulk_theme_override ( ) ;
}
2025-05-21 16:41:18 +02:00
void FileDialog : : _update_make_dir_visible ( ) {
2025-10-01 14:49:52 +02:00
can_create_folders = customization_flags [ CUSTOMIZATION_CREATE_FOLDER ] & & mode ! = FILE_MODE_OPEN_FILE & & mode ! = FILE_MODE_OPEN_FILES ;
make_dir_container - > set_visible ( can_create_folders ) ;
2025-05-21 16:41:18 +02:00
}
2014-02-09 22:10:30 -03:00
FileDialog : : Access FileDialog : : get_access ( ) const {
return access ;
}
void FileDialog : : _make_dir_confirm ( ) {
2025-04-22 00:23:16 +02:00
Error err = dir_access - > make_dir ( new_dir_name - > get_text ( ) . strip_edges ( ) ) ;
2014-02-09 22:10:30 -03:00
if ( err = = OK ) {
2025-04-22 00:23:16 +02:00
_change_dir ( new_dir_name - > get_text ( ) . strip_edges ( ) ) ;
2014-02-09 22:10:30 -03:00
update_filters ( ) ;
2021-03-19 14:01:03 +02:00
_push_history ( ) ;
2014-02-09 22:10:30 -03:00
} else {
2020-03-06 14:00:16 -03:00
mkdirerr - > popup_centered ( Size2 ( 250 , 50 ) ) ;
2014-02-09 22:10:30 -03:00
}
2025-04-22 00:23:16 +02:00
new_dir_name - > set_text ( " " ) ; // reset label
2014-02-09 22:10:30 -03:00
}
void FileDialog : : _make_dir ( ) {
2025-04-22 00:23:16 +02:00
make_dir_dialog - > popup_centered ( Size2 ( 250 , 80 ) ) ;
new_dir_name - > grab_focus ( ) ;
2014-02-09 22:10:30 -03:00
}
void FileDialog : : _select_drive ( int p_idx ) {
String d = drives - > get_item_text ( p_idx ) ;
2022-03-13 01:35:20 +01:00
_change_dir ( d ) ;
2025-04-22 00:23:16 +02:00
filename_edit - > set_text ( " " ) ;
2022-03-13 01:35:20 +01:00
_push_history ( ) ;
}
void FileDialog : : _change_dir ( const String & p_new_dir ) {
if ( root_prefix . is_empty ( ) ) {
dir_access - > change_dir ( p_new_dir ) ;
} else {
String old_dir = dir_access - > get_current_dir ( ) ;
dir_access - > change_dir ( p_new_dir ) ;
if ( ! dir_access - > get_current_dir ( false ) . begins_with ( root_prefix ) ) {
dir_access - > change_dir ( old_dir ) ;
return ;
}
}
2014-02-09 22:10:30 -03:00
invalidate ( ) ;
update_dir ( ) ;
}
2022-01-24 13:12:46 +02:00
void FileDialog : : _update_drives ( bool p_select ) {
2014-02-09 22:10:30 -03:00
int dc = dir_access - > get_drive_count ( ) ;
if ( dc = = 0 | | access ! = ACCESS_FILESYSTEM ) {
drives - > hide ( ) ;
} else {
drives - > clear ( ) ;
2020-02-10 09:19:29 +01:00
Node * dp = drives - > get_parent ( ) ;
if ( dp ) {
dp - > remove_child ( drives ) ;
}
dp = dir_access - > drives_are_shortcuts ( ) ? shortcuts_container : drives_container ;
dp - > add_child ( drives ) ;
2014-02-09 22:10:30 -03:00
drives - > show ( ) ;
for ( int i = 0 ; i < dir_access - > get_drive_count ( ) ; i + + ) {
drives - > add_item ( dir_access - > get_drive ( i ) ) ;
}
2022-01-24 13:12:46 +02:00
if ( p_select ) {
drives - > select ( dir_access - > get_current_drive ( ) ) ;
}
2014-02-09 22:10:30 -03:00
}
}
2025-04-24 19:58:17 +02:00
void FileDialog : : _sort_option_selected ( int p_option ) {
for ( int i = 0 ; i < int ( FileSortOption : : MAX ) ; i + + ) {
file_sort_button - > get_popup ( ) - > set_item_checked ( i , ( i = = p_option ) ) ;
}
file_sort = FileSortOption ( p_option ) ;
invalidate ( ) ;
}
2025-04-23 00:11:36 +02:00
void FileDialog : : _favorite_selected ( int p_item ) {
ERR_FAIL_UNSIGNED_INDEX ( ( uint32_t ) p_item , global_favorites . size ( ) ) ;
_change_dir ( favorite_list - > get_item_metadata ( p_item ) ) ;
_push_history ( ) ;
}
void FileDialog : : _favorite_pressed ( ) {
String directory = get_current_dir ( ) ;
if ( ! directory . ends_with ( " / " ) ) {
directory + = " / " ;
}
bool found = false ;
for ( const String & name : global_favorites ) {
if ( ! _path_matches_access ( name ) ) {
continue ;
}
if ( name = = directory ) {
found = true ;
break ;
}
}
if ( found ) {
global_favorites . erase ( directory ) ;
} else {
global_favorites . push_back ( directory ) ;
}
_update_favorite_list ( ) ;
}
void FileDialog : : _favorite_move_up ( ) {
int current = favorite_list - > get_current ( ) ;
if ( current < = 0 ) {
return ;
}
int a_idx = global_favorites . find ( favorite_list - > get_item_metadata ( current - 1 ) ) ;
int b_idx = global_favorites . find ( favorite_list - > get_item_metadata ( current ) ) ;
if ( a_idx = = - 1 | | b_idx = = - 1 ) {
return ;
}
SWAP ( global_favorites [ a_idx ] , global_favorites [ b_idx ] ) ;
_update_favorite_list ( ) ;
}
void FileDialog : : _favorite_move_down ( ) {
int current = favorite_list - > get_current ( ) ;
if ( current = = - 1 | | current > = favorite_list - > get_item_count ( ) - 1 ) {
return ;
}
int a_idx = global_favorites . find ( favorite_list - > get_item_metadata ( current ) ) ;
int b_idx = global_favorites . find ( favorite_list - > get_item_metadata ( current + 1 ) ) ;
if ( a_idx = = - 1 | | b_idx = = - 1 ) {
return ;
}
SWAP ( global_favorites [ a_idx ] , global_favorites [ b_idx ] ) ;
_update_favorite_list ( ) ;
}
void FileDialog : : _update_favorite_list ( ) {
const String current = get_current_dir ( ) ;
favorite_list - > clear ( ) ;
favorite_button - > set_pressed ( false ) ;
Vector < String > favorited_paths ;
Vector < String > favorited_names ;
int current_favorite = - 1 ;
for ( uint32_t i = 0 ; i < global_favorites . size ( ) ; i + + ) {
String name = global_favorites [ i ] ;
if ( ! _path_matches_access ( name ) ) {
continue ;
}
if ( ! name . ends_with ( " / " ) | | ! name . begins_with ( root_prefix ) ) {
continue ;
}
if ( ! dir_access - > dir_exists ( name ) ) {
// Remove invalid directory from the list of favorited directories.
global_favorites . remove_at ( i ) ;
i - - ;
continue ;
}
if ( name = = current ) {
current_favorite = favorited_names . size ( ) ;
}
favorited_paths . append ( name ) ;
// Compute favorite display text.
if ( name = = " res:// " | | name = = " user:// " ) {
name = " / " ;
} else {
if ( current_favorite = = - 1 & & name = = current + " / " ) {
current_favorite = favorited_names . size ( ) ;
}
name = name . trim_suffix ( " / " ) ;
name = name . get_file ( ) ;
}
favorited_names . append ( name ) ;
}
// EditorNode::disambiguate_filenames(favorited_paths, favorited_names); // TODO Needs a non-editor method.
const int favorites_size = favorited_paths . size ( ) ;
for ( int i = 0 ; i < favorites_size ; i + + ) {
favorite_list - > add_item ( favorited_names [ i ] , theme_cache . folder ) ;
favorite_list - > set_item_tooltip ( - 1 , favorited_paths [ i ] ) ;
favorite_list - > set_item_metadata ( - 1 , favorited_paths [ i ] ) ;
if ( i = = current_favorite ) {
favorite_button - > set_pressed ( true ) ;
favorite_list - > set_current ( favorite_list - > get_item_count ( ) - 1 ) ;
recent_list - > deselect_all ( ) ;
}
}
_update_fav_buttons ( ) ;
}
void FileDialog : : _update_fav_buttons ( ) {
const int current = favorite_list - > get_current ( ) ;
fav_up_button - > set_disabled ( current < 1 ) ;
fav_down_button - > set_disabled ( current = = - 1 | | current > = favorite_list - > get_item_count ( ) - 1 ) ;
}
void FileDialog : : _recent_selected ( int p_item ) {
ERR_FAIL_UNSIGNED_INDEX ( ( uint32_t ) p_item , global_recents . size ( ) ) ;
_change_dir ( recent_list - > get_item_metadata ( p_item ) ) ;
_push_history ( ) ;
}
void FileDialog : : _save_to_recent ( ) {
String directory = get_current_dir ( ) ;
if ( ! directory . ends_with ( " / " ) ) {
directory + = " / " ;
}
int count = 0 ;
for ( uint32_t i = 0 ; i < global_recents . size ( ) ; i + + ) {
const String & dir = global_recents [ i ] ;
if ( ! _path_matches_access ( dir ) ) {
continue ;
}
if ( dir = = directory | | count > MAX_RECENTS ) {
global_recents . remove_at ( i ) ;
i - - ;
} else {
count + + ;
}
}
global_recents . insert ( 0 , directory ) ;
_update_recent_list ( ) ;
}
void FileDialog : : _update_recent_list ( ) {
recent_list - > clear ( ) ;
Vector < String > recent_dir_paths ;
Vector < String > recent_dir_names ;
for ( uint32_t i = 0 ; i < global_recents . size ( ) ; i + + ) {
String name = global_recents [ i ] ;
if ( ! _path_matches_access ( name ) ) {
continue ;
}
if ( ! name . begins_with ( root_prefix ) ) {
continue ;
}
if ( ! dir_access - > dir_exists ( name ) ) {
// Remove invalid directory from the list of recent directories.
global_recents . remove_at ( i ) ;
i - - ;
continue ;
}
recent_dir_paths . append ( name ) ;
// Compute recent directory display text.
if ( name = = " res:// " | | name = = " user:// " ) {
name = " / " ;
} else {
name = name . trim_suffix ( " / " ) . get_file ( ) ;
}
recent_dir_names . append ( name ) ;
}
// EditorNode::disambiguate_filenames(recent_dir_paths, recent_dir_names); // TODO Needs a non-editor method.
const int recent_size = recent_dir_paths . size ( ) ;
for ( int i = 0 ; i < recent_size ; i + + ) {
recent_list - > add_item ( recent_dir_names [ i ] , theme_cache . folder ) ;
recent_list - > set_item_tooltip ( - 1 , recent_dir_paths [ i ] ) ;
recent_list - > set_item_metadata ( - 1 , recent_dir_paths [ i ] ) ;
}
}
bool FileDialog : : _path_matches_access ( const String & p_path ) const {
bool is_res = p_path . begins_with ( " res:// " ) ;
bool is_user = p_path . begins_with ( " user:// " ) ;
if ( access = = ACCESS_RESOURCES ) {
return is_res ;
} else if ( access = = ACCESS_USERDATA ) {
return is_user ;
}
return ! is_res & & ! is_user ;
}
2023-10-13 12:37:46 +03:00
TypedArray < Dictionary > FileDialog : : _get_options ( ) const {
TypedArray < Dictionary > out ;
for ( const FileDialog : : Option & opt : options ) {
Dictionary dict ;
dict [ " name " ] = opt . name ;
dict [ " values " ] = opt . values ;
dict [ " default " ] = ( int ) selected_options . get ( opt . name , opt . default_idx ) ;
out . push_back ( dict ) ;
}
return out ;
}
void FileDialog : : _option_changed_checkbox_toggled ( bool p_pressed , const String & p_name ) {
if ( selected_options . has ( p_name ) ) {
selected_options [ p_name ] = p_pressed ;
}
}
void FileDialog : : _option_changed_item_selected ( int p_idx , const String & p_name ) {
if ( selected_options . has ( p_name ) ) {
selected_options [ p_name ] = p_idx ;
}
}
void FileDialog : : _update_option_controls ( ) {
if ( ! options_dirty ) {
return ;
}
options_dirty = false ;
2025-04-22 15:32:34 +02:00
while ( flow_checkbox_options - > get_child_count ( ) > 0 ) {
Node * child = flow_checkbox_options - > get_child ( 0 ) ;
flow_checkbox_options - > remove_child ( child ) ;
child - > queue_free ( ) ;
}
while ( grid_select_options - > get_child_count ( ) > 0 ) {
Node * child = grid_select_options - > get_child ( 0 ) ;
grid_select_options - > remove_child ( child ) ;
2023-10-13 12:37:46 +03:00
child - > queue_free ( ) ;
}
selected_options . clear ( ) ;
for ( const FileDialog : : Option & opt : options ) {
if ( opt . values . is_empty ( ) ) {
CheckBox * cb = memnew ( CheckBox ) ;
2025-04-22 15:32:34 +02:00
cb - > set_text ( opt . name ) ;
2025-03-21 16:42:23 +02:00
cb - > set_accessibility_name ( opt . name ) ;
2023-10-13 12:37:46 +03:00
cb - > set_pressed ( opt . default_idx ) ;
2025-04-22 15:32:34 +02:00
flow_checkbox_options - > add_child ( cb ) ;
2024-06-01 13:15:13 +03:00
cb - > connect ( SceneStringName ( toggled ) , callable_mp ( this , & FileDialog : : _option_changed_checkbox_toggled ) . bind ( opt . name ) ) ;
2023-10-13 12:37:46 +03:00
selected_options [ opt . name ] = ( bool ) opt . default_idx ;
} else {
2025-04-22 15:32:34 +02:00
Label * lbl = memnew ( Label ) ;
lbl - > set_text ( opt . name ) ;
grid_select_options - > add_child ( lbl ) ;
2023-10-13 12:37:46 +03:00
OptionButton * ob = memnew ( OptionButton ) ;
for ( const String & val : opt . values ) {
ob - > add_item ( val ) ;
}
2025-03-21 16:42:23 +02:00
ob - > set_accessibility_name ( opt . name ) ;
2023-10-13 12:37:46 +03:00
ob - > select ( opt . default_idx ) ;
2025-04-22 15:32:34 +02:00
grid_select_options - > add_child ( ob ) ;
2024-05-14 14:21:31 +02:00
ob - > connect ( SceneStringName ( item_selected ) , callable_mp ( this , & FileDialog : : _option_changed_item_selected ) . bind ( opt . name ) ) ;
2023-10-13 12:37:46 +03:00
selected_options [ opt . name ] = opt . default_idx ;
}
}
}
Dictionary FileDialog : : get_selected_options ( ) const {
return selected_options ;
}
String FileDialog : : get_option_name ( int p_option ) const {
ERR_FAIL_INDEX_V ( p_option , options . size ( ) , String ( ) ) ;
return options [ p_option ] . name ;
}
Vector < String > FileDialog : : get_option_values ( int p_option ) const {
ERR_FAIL_INDEX_V ( p_option , options . size ( ) , Vector < String > ( ) ) ;
return options [ p_option ] . values ;
}
int FileDialog : : get_option_default ( int p_option ) const {
ERR_FAIL_INDEX_V ( p_option , options . size ( ) , - 1 ) ;
return options [ p_option ] . default_idx ;
}
void FileDialog : : set_option_name ( int p_option , const String & p_name ) {
if ( p_option < 0 ) {
p_option + = get_option_count ( ) ;
}
ERR_FAIL_INDEX ( p_option , options . size ( ) ) ;
options . write [ p_option ] . name = p_name ;
options_dirty = true ;
if ( is_visible ( ) ) {
_update_option_controls ( ) ;
}
}
void FileDialog : : set_option_values ( int p_option , const Vector < String > & p_values ) {
if ( p_option < 0 ) {
p_option + = get_option_count ( ) ;
}
ERR_FAIL_INDEX ( p_option , options . size ( ) ) ;
options . write [ p_option ] . values = p_values ;
if ( p_values . is_empty ( ) ) {
options . write [ p_option ] . default_idx = CLAMP ( options [ p_option ] . default_idx , 0 , 1 ) ;
} else {
options . write [ p_option ] . default_idx = CLAMP ( options [ p_option ] . default_idx , 0 , options [ p_option ] . values . size ( ) - 1 ) ;
}
options_dirty = true ;
if ( is_visible ( ) ) {
_update_option_controls ( ) ;
}
}
void FileDialog : : set_option_default ( int p_option , int p_index ) {
if ( p_option < 0 ) {
p_option + = get_option_count ( ) ;
}
ERR_FAIL_INDEX ( p_option , options . size ( ) ) ;
if ( options [ p_option ] . values . is_empty ( ) ) {
options . write [ p_option ] . default_idx = CLAMP ( p_index , 0 , 1 ) ;
} else {
options . write [ p_option ] . default_idx = CLAMP ( p_index , 0 , options [ p_option ] . values . size ( ) - 1 ) ;
}
options_dirty = true ;
if ( is_visible ( ) ) {
_update_option_controls ( ) ;
}
}
void FileDialog : : add_option ( const String & p_name , const Vector < String > & p_values , int p_index ) {
Option opt ;
opt . name = p_name ;
opt . values = p_values ;
if ( opt . values . is_empty ( ) ) {
opt . default_idx = CLAMP ( p_index , 0 , 1 ) ;
} else {
opt . default_idx = CLAMP ( p_index , 0 , opt . values . size ( ) - 1 ) ;
}
options . push_back ( opt ) ;
options_dirty = true ;
if ( is_visible ( ) ) {
_update_option_controls ( ) ;
}
}
void FileDialog : : set_option_count ( int p_count ) {
ERR_FAIL_COND ( p_count < 0 ) ;
2024-03-21 10:42:38 +02:00
if ( options . size ( ) = = p_count ) {
2023-10-13 12:37:46 +03:00
return ;
}
options . resize ( p_count ) ;
options_dirty = true ;
notify_property_list_changed ( ) ;
if ( is_visible ( ) ) {
_update_option_controls ( ) ;
}
}
int FileDialog : : get_option_count ( ) const {
return options . size ( ) ;
}
2014-02-09 22:10:30 -03:00
void FileDialog : : _bind_methods ( ) {
2017-02-13 12:47:24 +01:00
ClassDB : : bind_method ( D_METHOD ( " _cancel_pressed " ) , & FileDialog : : _cancel_pressed ) ;
2017-03-05 16:44:50 +01:00
2017-02-13 12:47:24 +01:00
ClassDB : : bind_method ( D_METHOD ( " clear_filters " ) , & FileDialog : : clear_filters ) ;
2022-07-04 16:26:26 -05:00
ClassDB : : bind_method ( D_METHOD ( " add_filter " , " filter " , " description " ) , & FileDialog : : add_filter , DEFVAL ( " " ) ) ;
2017-02-13 12:47:24 +01:00
ClassDB : : bind_method ( D_METHOD ( " set_filters " , " filters " ) , & FileDialog : : set_filters ) ;
ClassDB : : bind_method ( D_METHOD ( " get_filters " ) , & FileDialog : : get_filters ) ;
2024-02-22 18:34:46 +01:00
ClassDB : : bind_method ( D_METHOD ( " clear_filename_filter " ) , & FileDialog : : clear_filename_filter ) ;
ClassDB : : bind_method ( D_METHOD ( " set_filename_filter " , " filter " ) , & FileDialog : : set_filename_filter ) ;
ClassDB : : bind_method ( D_METHOD ( " get_filename_filter " ) , & FileDialog : : get_filename_filter ) ;
2023-10-13 12:37:46 +03:00
ClassDB : : bind_method ( D_METHOD ( " get_option_name " , " option " ) , & FileDialog : : get_option_name ) ;
ClassDB : : bind_method ( D_METHOD ( " get_option_values " , " option " ) , & FileDialog : : get_option_values ) ;
ClassDB : : bind_method ( D_METHOD ( " get_option_default " , " option " ) , & FileDialog : : get_option_default ) ;
ClassDB : : bind_method ( D_METHOD ( " set_option_name " , " option " , " name " ) , & FileDialog : : set_option_name ) ;
ClassDB : : bind_method ( D_METHOD ( " set_option_values " , " option " , " values " ) , & FileDialog : : set_option_values ) ;
2024-03-21 10:42:38 +02:00
ClassDB : : bind_method ( D_METHOD ( " set_option_default " , " option " , " default_value_index " ) , & FileDialog : : set_option_default ) ;
2023-10-13 12:37:46 +03:00
ClassDB : : bind_method ( D_METHOD ( " set_option_count " , " count " ) , & FileDialog : : set_option_count ) ;
ClassDB : : bind_method ( D_METHOD ( " get_option_count " ) , & FileDialog : : get_option_count ) ;
2024-03-21 10:42:38 +02:00
ClassDB : : bind_method ( D_METHOD ( " add_option " , " name " , " values " , " default_value_index " ) , & FileDialog : : add_option ) ;
2023-10-13 12:37:46 +03:00
ClassDB : : bind_method ( D_METHOD ( " get_selected_options " ) , & FileDialog : : get_selected_options ) ;
2017-02-13 12:47:24 +01:00
ClassDB : : bind_method ( D_METHOD ( " get_current_dir " ) , & FileDialog : : get_current_dir ) ;
ClassDB : : bind_method ( D_METHOD ( " get_current_file " ) , & FileDialog : : get_current_file ) ;
ClassDB : : bind_method ( D_METHOD ( " get_current_path " ) , & FileDialog : : get_current_path ) ;
ClassDB : : bind_method ( D_METHOD ( " set_current_dir " , " dir " ) , & FileDialog : : set_current_dir ) ;
ClassDB : : bind_method ( D_METHOD ( " set_current_file " , " file " ) , & FileDialog : : set_current_file ) ;
ClassDB : : bind_method ( D_METHOD ( " set_current_path " , " path " ) , & FileDialog : : set_current_path ) ;
2017-12-02 23:54:06 -02:00
ClassDB : : bind_method ( D_METHOD ( " set_mode_overrides_title " , " override " ) , & FileDialog : : set_mode_overrides_title ) ;
ClassDB : : bind_method ( D_METHOD ( " is_mode_overriding_title " ) , & FileDialog : : is_mode_overriding_title ) ;
2020-03-06 14:00:16 -03:00
ClassDB : : bind_method ( D_METHOD ( " set_file_mode " , " mode " ) , & FileDialog : : set_file_mode ) ;
ClassDB : : bind_method ( D_METHOD ( " get_file_mode " ) , & FileDialog : : get_file_mode ) ;
2025-04-28 14:57:50 +02:00
ClassDB : : bind_method ( D_METHOD ( " set_display_mode " , " mode " ) , & FileDialog : : set_display_mode ) ;
ClassDB : : bind_method ( D_METHOD ( " get_display_mode " ) , & FileDialog : : get_display_mode ) ;
2017-08-09 13:19:41 +02:00
ClassDB : : bind_method ( D_METHOD ( " get_vbox " ) , & FileDialog : : get_vbox ) ;
2018-06-06 01:19:24 +02:00
ClassDB : : bind_method ( D_METHOD ( " get_line_edit " ) , & FileDialog : : get_line_edit ) ;
2017-02-13 12:47:24 +01:00
ClassDB : : bind_method ( D_METHOD ( " set_access " , " access " ) , & FileDialog : : set_access ) ;
ClassDB : : bind_method ( D_METHOD ( " get_access " ) , & FileDialog : : get_access ) ;
2022-03-13 01:35:20 +01:00
ClassDB : : bind_method ( D_METHOD ( " set_root_subfolder " , " dir " ) , & FileDialog : : set_root_subfolder ) ;
ClassDB : : bind_method ( D_METHOD ( " get_root_subfolder " ) , & FileDialog : : get_root_subfolder ) ;
2017-02-13 12:47:24 +01:00
ClassDB : : bind_method ( D_METHOD ( " set_show_hidden_files " , " show " ) , & FileDialog : : set_show_hidden_files ) ;
ClassDB : : bind_method ( D_METHOD ( " is_showing_hidden_files " ) , & FileDialog : : is_showing_hidden_files ) ;
2021-03-30 15:42:50 +03:00
ClassDB : : bind_method ( D_METHOD ( " set_use_native_dialog " , " native " ) , & FileDialog : : set_use_native_dialog ) ;
ClassDB : : bind_method ( D_METHOD ( " get_use_native_dialog " ) , & FileDialog : : get_use_native_dialog ) ;
2025-05-21 16:41:18 +02:00
ClassDB : : bind_method ( D_METHOD ( " set_customization_flag_enabled " , " flag " , " enabled " ) , & FileDialog : : set_customization_flag_enabled ) ;
ClassDB : : bind_method ( D_METHOD ( " is_customization_flag_enabled " , " flag " ) , & FileDialog : : is_customization_flag_enabled ) ;
2020-12-21 10:26:41 +00:00
ClassDB : : bind_method ( D_METHOD ( " deselect_all " ) , & FileDialog : : deselect_all ) ;
2017-03-05 16:44:50 +01:00
2025-06-30 16:43:34 +02:00
ClassDB : : bind_static_method ( " FileDialog " , D_METHOD ( " set_favorite_list " , " favorites " ) , & FileDialog : : set_favorite_list ) ;
ClassDB : : bind_static_method ( " FileDialog " , D_METHOD ( " get_favorite_list " ) , & FileDialog : : get_favorite_list ) ;
ClassDB : : bind_static_method ( " FileDialog " , D_METHOD ( " set_recent_list " , " recents " ) , & FileDialog : : set_recent_list ) ;
ClassDB : : bind_static_method ( " FileDialog " , D_METHOD ( " get_recent_list " ) , & FileDialog : : get_recent_list ) ;
2025-06-30 19:07:12 +02:00
ClassDB : : bind_static_method ( " FileDialog " , D_METHOD ( " set_get_icon_callback " , " callback " ) , & FileDialog : : set_get_icon_callback ) ;
ClassDB : : bind_static_method ( " FileDialog " , D_METHOD ( " set_get_thumbnail_callback " , " callback " ) , & FileDialog : : set_get_thumbnail_callback ) ;
2025-06-30 16:43:34 +02:00
2017-02-13 12:47:24 +01:00
ClassDB : : bind_method ( D_METHOD ( " invalidate " ) , & FileDialog : : invalidate ) ;
2017-03-05 16:44:50 +01:00
2018-01-12 00:35:12 +02:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " mode_overrides_title " ) , " set_mode_overrides_title " , " is_mode_overriding_title " ) ;
2020-03-06 14:00:16 -03:00
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " file_mode " , PROPERTY_HINT_ENUM , " Open File,Open Files,Open Folder,Open Any,Save " ) , " set_file_mode " , " get_file_mode " ) ;
2025-04-28 14:57:50 +02:00
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " display_mode " , PROPERTY_HINT_ENUM , " Thumbnails,List " ) , " set_display_mode " , " get_display_mode " ) ;
2022-05-12 15:03:16 -05:00
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " access " , PROPERTY_HINT_ENUM , " Resources,User Data,File System " ) , " set_access " , " get_access " ) ;
2022-03-13 01:35:20 +01:00
ADD_PROPERTY ( PropertyInfo ( Variant : : STRING , " root_subfolder " ) , " set_root_subfolder " , " get_root_subfolder " ) ;
2020-02-17 18:06:54 -03:00
ADD_PROPERTY ( PropertyInfo ( Variant : : PACKED_STRING_ARRAY , " filters " ) , " set_filters " , " get_filters " ) ;
2024-02-22 18:34:46 +01:00
ADD_PROPERTY ( PropertyInfo ( Variant : : STRING , " filename_filter " ) , " set_filename_filter " , " get_filename_filter " ) ;
2018-01-12 00:35:12 +02:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " show_hidden_files " ) , " set_show_hidden_files " , " is_showing_hidden_files " ) ;
2021-03-30 15:42:50 +03:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " use_native_dialog " ) , " set_use_native_dialog " , " get_use_native_dialog " ) ;
2025-05-21 16:41:18 +02:00
ADD_ARRAY_COUNT ( " Options " , " option_count " , " set_option_count " , " get_option_count " , " option_ " ) ;
ADD_GROUP ( " Customization " , " " ) ;
ADD_PROPERTYI ( PropertyInfo ( Variant : : BOOL , " hidden_files_toggle_enabled " ) , " set_customization_flag_enabled " , " is_customization_flag_enabled " , CUSTOMIZATION_HIDDEN_FILES ) ;
ADD_PROPERTYI ( PropertyInfo ( Variant : : BOOL , " file_filter_toggle_enabled " ) , " set_customization_flag_enabled " , " is_customization_flag_enabled " , CUSTOMIZATION_FILE_FILTER ) ;
ADD_PROPERTYI ( PropertyInfo ( Variant : : BOOL , " file_sort_options_enabled " ) , " set_customization_flag_enabled " , " is_customization_flag_enabled " , CUSTOMIZATION_FILE_SORT ) ;
ADD_PROPERTYI ( PropertyInfo ( Variant : : BOOL , " folder_creation_enabled " ) , " set_customization_flag_enabled " , " is_customization_flag_enabled " , CUSTOMIZATION_CREATE_FOLDER ) ;
ADD_PROPERTYI ( PropertyInfo ( Variant : : BOOL , " favorites_enabled " ) , " set_customization_flag_enabled " , " is_customization_flag_enabled " , CUSTOMIZATION_FAVORITES ) ;
ADD_PROPERTYI ( PropertyInfo ( Variant : : BOOL , " recent_list_enabled " ) , " set_customization_flag_enabled " , " is_customization_flag_enabled " , CUSTOMIZATION_RECENT ) ;
ADD_PROPERTYI ( PropertyInfo ( Variant : : BOOL , " layout_toggle_enabled " ) , " set_customization_flag_enabled " , " is_customization_flag_enabled " , CUSTOMIZATION_LAYOUT ) ;
2025-10-02 13:23:02 +02:00
ADD_PROPERTYI ( PropertyInfo ( Variant : : BOOL , " overwrite_warning_enabled " ) , " set_customization_flag_enabled " , " is_customization_flag_enabled " , CUSTOMIZATION_OVERWRITE_WARNING ) ;
2025-10-01 14:49:52 +02:00
ADD_PROPERTYI ( PropertyInfo ( Variant : : BOOL , " deleting_enabled " ) , " set_customization_flag_enabled " , " is_customization_flag_enabled " , CUSTOMIZATION_DELETE ) ;
2025-05-21 16:41:18 +02:00
2021-10-29 20:15:33 +02:00
ADD_PROPERTY ( PropertyInfo ( Variant : : STRING , " current_dir " , PROPERTY_HINT_DIR , " " , PROPERTY_USAGE_NONE ) , " set_current_dir " , " get_current_dir " ) ;
2025-04-15 13:16:42 +02:00
ADD_PROPERTY ( PropertyInfo ( Variant : : STRING , " current_file " , PROPERTY_HINT_FILE_PATH , " * " , PROPERTY_USAGE_NONE ) , " set_current_file " , " get_current_file " ) ;
2021-10-29 20:15:33 +02:00
ADD_PROPERTY ( PropertyInfo ( Variant : : STRING , " current_path " , PROPERTY_HINT_NONE , " " , PROPERTY_USAGE_NONE ) , " set_current_path " , " get_current_path " ) ;
2018-01-12 00:35:12 +02:00
2014-02-09 22:10:30 -03:00
ADD_SIGNAL ( MethodInfo ( " file_selected " , PropertyInfo ( Variant : : STRING , " path " ) ) ) ;
2020-02-17 18:06:54 -03:00
ADD_SIGNAL ( MethodInfo ( " files_selected " , PropertyInfo ( Variant : : PACKED_STRING_ARRAY , " paths " ) ) ) ;
2014-02-09 22:10:30 -03:00
ADD_SIGNAL ( MethodInfo ( " dir_selected " , PropertyInfo ( Variant : : STRING , " dir " ) ) ) ;
2024-02-22 18:34:46 +01:00
ADD_SIGNAL ( MethodInfo ( " filename_filter_changed " , PropertyInfo ( Variant : : STRING , " filter " ) ) ) ;
2017-03-05 16:44:50 +01:00
2020-03-06 14:00:16 -03:00
BIND_ENUM_CONSTANT ( FILE_MODE_OPEN_FILE ) ;
BIND_ENUM_CONSTANT ( FILE_MODE_OPEN_FILES ) ;
BIND_ENUM_CONSTANT ( FILE_MODE_OPEN_DIR ) ;
BIND_ENUM_CONSTANT ( FILE_MODE_OPEN_ANY ) ;
BIND_ENUM_CONSTANT ( FILE_MODE_SAVE_FILE ) ;
2017-08-20 17:45:01 +02:00
BIND_ENUM_CONSTANT ( ACCESS_RESOURCES ) ;
BIND_ENUM_CONSTANT ( ACCESS_USERDATA ) ;
BIND_ENUM_CONSTANT ( ACCESS_FILESYSTEM ) ;
2023-09-08 21:00:10 +02:00
2025-04-28 14:57:50 +02:00
BIND_ENUM_CONSTANT ( DISPLAY_THUMBNAILS ) ;
BIND_ENUM_CONSTANT ( DISPLAY_LIST ) ;
2025-05-21 16:41:18 +02:00
BIND_ENUM_CONSTANT ( CUSTOMIZATION_HIDDEN_FILES ) ;
BIND_ENUM_CONSTANT ( CUSTOMIZATION_CREATE_FOLDER ) ;
BIND_ENUM_CONSTANT ( CUSTOMIZATION_FILE_FILTER ) ;
BIND_ENUM_CONSTANT ( CUSTOMIZATION_FILE_SORT ) ;
BIND_ENUM_CONSTANT ( CUSTOMIZATION_FAVORITES ) ;
BIND_ENUM_CONSTANT ( CUSTOMIZATION_RECENT ) ;
BIND_ENUM_CONSTANT ( CUSTOMIZATION_LAYOUT ) ;
2025-10-02 13:23:02 +02:00
BIND_ENUM_CONSTANT ( CUSTOMIZATION_OVERWRITE_WARNING ) ;
2025-10-01 14:49:52 +02:00
BIND_ENUM_CONSTANT ( CUSTOMIZATION_DELETE ) ;
2025-04-28 14:57:50 +02:00
2025-05-21 16:41:18 +02:00
BIND_THEME_ITEM ( Theme : : DATA_TYPE_CONSTANT , FileDialog , thumbnail_size ) ;
2023-09-08 21:00:10 +02:00
BIND_THEME_ITEM ( Theme : : DATA_TYPE_ICON , FileDialog , parent_folder ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_ICON , FileDialog , forward_folder ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_ICON , FileDialog , back_folder ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_ICON , FileDialog , reload ) ;
2025-04-23 00:11:36 +02:00
BIND_THEME_ITEM ( Theme : : DATA_TYPE_ICON , FileDialog , favorite ) ;
2023-09-08 21:00:10 +02:00
BIND_THEME_ITEM ( Theme : : DATA_TYPE_ICON , FileDialog , toggle_hidden ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_ICON , FileDialog , folder ) ;
2024-02-22 18:34:46 +01:00
BIND_THEME_ITEM ( Theme : : DATA_TYPE_ICON , FileDialog , toggle_filename_filter ) ;
2023-09-08 21:00:10 +02:00
BIND_THEME_ITEM ( Theme : : DATA_TYPE_ICON , FileDialog , file ) ;
2024-02-25 11:51:50 -05:00
BIND_THEME_ITEM ( Theme : : DATA_TYPE_ICON , FileDialog , create_folder ) ;
2025-04-24 19:58:17 +02:00
BIND_THEME_ITEM ( Theme : : DATA_TYPE_ICON , FileDialog , sort ) ;
2025-04-23 00:11:36 +02:00
BIND_THEME_ITEM ( Theme : : DATA_TYPE_ICON , FileDialog , favorite_up ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_ICON , FileDialog , favorite_down ) ;
2025-04-28 14:57:50 +02:00
BIND_THEME_ITEM ( Theme : : DATA_TYPE_ICON , FileDialog , thumbnail_mode ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_ICON , FileDialog , list_mode ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_ICON , FileDialog , file_thumbnail ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_ICON , FileDialog , folder_thumbnail ) ;
2023-09-08 21:00:10 +02:00
BIND_THEME_ITEM ( Theme : : DATA_TYPE_COLOR , FileDialog , folder_icon_color ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_COLOR , FileDialog , file_icon_color ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_COLOR , FileDialog , file_disabled_color ) ;
// TODO: Define own colors?
BIND_THEME_ITEM_EXT ( Theme : : DATA_TYPE_COLOR , FileDialog , icon_normal_color , " font_color " , " Button " ) ;
BIND_THEME_ITEM_EXT ( Theme : : DATA_TYPE_COLOR , FileDialog , icon_hover_color , " font_hover_color " , " Button " ) ;
BIND_THEME_ITEM_EXT ( Theme : : DATA_TYPE_COLOR , FileDialog , icon_focus_color , " font_focus_color " , " Button " ) ;
BIND_THEME_ITEM_EXT ( Theme : : DATA_TYPE_COLOR , FileDialog , icon_pressed_color , " font_pressed_color " , " Button " ) ;
2024-02-14 02:11:45 +01:00
Option defaults ;
base_property_helper . set_prefix ( " option_ " ) ;
2024-05-09 14:19:16 +02:00
base_property_helper . set_array_length_getter ( & FileDialog : : get_option_count ) ;
2024-02-14 02:11:45 +01:00
base_property_helper . register_property ( PropertyInfo ( Variant : : STRING , " name " ) , defaults . name , & FileDialog : : set_option_name , & FileDialog : : get_option_name ) ;
base_property_helper . register_property ( PropertyInfo ( Variant : : PACKED_STRING_ARRAY , " values " ) , defaults . values , & FileDialog : : set_option_values , & FileDialog : : get_option_values ) ;
base_property_helper . register_property ( PropertyInfo ( Variant : : INT , " default " ) , defaults . default_idx , & FileDialog : : set_option_default , & FileDialog : : get_option_default ) ;
2024-07-03 10:39:18 +03:00
PropertyListHelper : : register_base_helper ( & base_property_helper ) ;
2025-03-06 15:42:10 -03:00
ADD_CLASS_DEPENDENCY ( " Button " ) ;
ADD_CLASS_DEPENDENCY ( " ConfirmationDialog " ) ;
ADD_CLASS_DEPENDENCY ( " LineEdit " ) ;
ADD_CLASS_DEPENDENCY ( " OptionButton " ) ;
ADD_CLASS_DEPENDENCY ( " Tree " ) ;
2014-02-09 22:10:30 -03:00
}
2015-03-23 11:31:03 -03:00
void FileDialog : : set_show_hidden_files ( bool p_show ) {
2022-03-16 15:50:48 +08:00
if ( show_hidden_files = = p_show ) {
return ;
}
2015-03-23 11:31:03 -03:00
show_hidden_files = p_show ;
invalidate ( ) ;
}
2024-02-22 18:34:46 +01:00
void FileDialog : : set_show_filename_filter ( bool p_show ) {
if ( p_show = = show_filename_filter ) {
return ;
}
if ( p_show ) {
filename_filter - > grab_focus ( ) ;
} else {
if ( filename_filter - > has_focus ( ) ) {
2025-09-03 20:39:18 -03:00
callable_mp ( ( Control * ) file_list , & Control : : grab_focus ) . call_deferred ( false ) ;
2024-02-22 18:34:46 +01:00
}
}
show_filename_filter = p_show ;
update_filename_filter_gui ( ) ;
invalidate ( ) ;
}
bool FileDialog : : get_show_filename_filter ( ) const {
return show_filename_filter ;
}
2015-03-23 11:31:03 -03:00
bool FileDialog : : is_showing_hidden_files ( ) const {
return show_hidden_files ;
}
void FileDialog : : set_default_show_hidden_files ( bool p_show ) {
default_show_hidden_files = p_show ;
}
2014-02-09 22:10:30 -03:00
2025-10-02 13:23:02 +02:00
void FileDialog : : set_default_display_mode ( DisplayMode p_mode ) {
default_display_mode = p_mode ;
}
2025-06-30 19:07:12 +02:00
void FileDialog : : set_get_icon_callback ( const Callable & p_callback ) {
get_icon_callback = p_callback ;
}
void FileDialog : : set_get_thumbnail_callback ( const Callable & p_callback ) {
get_thumbnail_callback = p_callback ;
}
2021-03-30 15:42:50 +03:00
void FileDialog : : set_use_native_dialog ( bool p_native ) {
use_native_dialog = p_native ;
2024-04-06 15:45:45 -03:00
# ifdef TOOLS_ENABLED
if ( is_part_of_edited_scene ( ) ) {
return ;
}
# endif
// Replace the built-in dialog with the native one if it's currently visible.
2024-10-21 14:13:44 +05:30
if ( is_inside_tree ( ) & & is_visible ( ) & & _can_use_native_popup ( ) & & ( use_native_dialog | | OS : : get_singleton ( ) - > is_sandboxed ( ) ) ) {
2024-04-06 15:45:45 -03:00
ConfirmationDialog : : set_visible ( false ) ;
_native_popup ( ) ;
}
2021-03-30 15:42:50 +03:00
}
bool FileDialog : : get_use_native_dialog ( ) const {
return use_native_dialog ;
}
2014-02-09 22:10:30 -03:00
FileDialog : : FileDialog ( ) {
2025-04-22 00:23:16 +02:00
set_title ( ETR ( " Save a File " ) ) ;
set_hide_on_ok ( false ) ;
set_size ( Size2 ( 640 , 360 ) ) ;
2025-04-19 00:57:00 +02:00
set_default_ok_text ( ETR ( " Save " ) ) ; // Default mode text.
2025-10-09 22:31:55 +02:00
set_process_shortcut_input ( true ) ;
2025-06-30 19:07:12 +02:00
thumbnail_callback = callable_mp ( this , & FileDialog : : _thumbnail_callback ) ;
2015-03-23 11:31:03 -03:00
2025-05-21 16:41:18 +02:00
for ( int i = 0 ; i < CUSTOMIZATION_MAX ; i + + ) {
customization_flags [ i ] = true ;
}
2025-10-09 22:31:55 +02:00
action_shortcuts [ ITEM_MENU_DELETE ] = Shortcut : : make_from_action ( " ui_filedialog_delete " ) ;
action_shortcuts [ ITEM_MENU_GO_UP ] = Shortcut : : make_from_action ( " ui_filedialog_up_one_level " ) ;
action_shortcuts [ ITEM_MENU_REFRESH ] = Shortcut : : make_from_action ( " ui_filedialog_refresh " ) ;
action_shortcuts [ ITEM_MENU_TOGGLE_HIDDEN ] = Shortcut : : make_from_action ( " ui_filedialog_show_hidden " ) ;
action_shortcuts [ ITEM_MENU_FIND ] = Shortcut : : make_from_action ( " ui_filedialog_find " ) ;
action_shortcuts [ ITEM_MENU_FOCUS_PATH ] = Shortcut : : make_from_action ( " ui_filedialog_focus_path " ) ;
2025-04-22 00:23:16 +02:00
show_hidden_files = default_show_hidden_files ;
2025-10-02 13:23:02 +02:00
display_mode = default_display_mode ;
2025-04-22 00:23:16 +02:00
dir_access = DirAccess : : create ( DirAccess : : ACCESS_RESOURCES ) ;
2017-01-10 01:49:55 -03:00
2025-04-22 00:23:16 +02:00
main_vbox = memnew ( VBoxContainer ) ;
add_child ( main_vbox , false , INTERNAL_MODE_FRONT ) ;
2016-03-09 00:00:52 +01:00
2025-04-22 00:23:16 +02:00
HBoxContainer * top_toolbar = memnew ( HBoxContainer ) ;
main_vbox - > add_child ( top_toolbar ) ;
2017-11-27 18:58:28 +03:00
2021-03-19 14:01:03 +02:00
dir_prev = memnew ( Button ) ;
2024-12-05 17:13:20 +03:00
dir_prev - > set_theme_type_variation ( SceneStringName ( FlatButton ) ) ;
2023-12-15 20:56:06 -03:00
dir_prev - > set_tooltip_text ( ETR ( " Go to previous folder. " ) ) ;
2025-04-22 00:23:16 +02:00
top_toolbar - > add_child ( dir_prev ) ;
dir_prev - > connect ( SceneStringName ( pressed ) , callable_mp ( this , & FileDialog : : _go_back ) ) ;
2021-03-19 14:01:03 +02:00
dir_next = memnew ( Button ) ;
2024-12-05 17:13:20 +03:00
dir_next - > set_theme_type_variation ( SceneStringName ( FlatButton ) ) ;
2023-12-15 20:56:06 -03:00
dir_next - > set_tooltip_text ( ETR ( " Go to next folder. " ) ) ;
2025-04-22 00:23:16 +02:00
top_toolbar - > add_child ( dir_next ) ;
dir_next - > connect ( SceneStringName ( pressed ) , callable_mp ( this , & FileDialog : : _go_forward ) ) ;
2020-06-19 20:49:04 +02:00
dir_up = memnew ( Button ) ;
2024-12-05 17:13:20 +03:00
dir_up - > set_theme_type_variation ( SceneStringName ( FlatButton ) ) ;
2023-12-15 20:56:06 -03:00
dir_up - > set_tooltip_text ( ETR ( " Go to parent folder. " ) ) ;
2025-10-09 22:31:55 +02:00
dir_up - > set_shortcut ( action_shortcuts [ ITEM_MENU_GO_UP ] ) ;
2025-04-22 00:23:16 +02:00
top_toolbar - > add_child ( dir_up ) ;
2024-05-14 09:40:21 +02:00
dir_up - > connect ( SceneStringName ( pressed ) , callable_mp ( this , & FileDialog : : _go_up ) ) ;
2017-11-27 18:58:28 +03:00
2025-04-22 00:23:16 +02:00
{
Label * label = memnew ( Label ( ETR ( " Path: " ) ) ) ;
top_toolbar - > add_child ( label ) ;
}
2020-02-10 09:19:29 +01:00
drives_container = memnew ( HBoxContainer ) ;
2025-04-22 00:23:16 +02:00
top_toolbar - > add_child ( drives_container ) ;
2020-02-10 09:19:29 +01:00
2019-10-26 17:29:22 +02:00
drives = memnew ( OptionButton ) ;
2024-05-14 14:21:31 +02:00
drives - > connect ( SceneStringName ( item_selected ) , callable_mp ( this , & FileDialog : : _select_drive ) ) ;
2025-03-21 16:42:23 +02:00
drives - > set_accessibility_name ( ETR ( " Drive " ) ) ;
2025-04-22 00:23:16 +02:00
top_toolbar - > add_child ( drives ) ;
2019-10-26 17:29:22 +02:00
2025-04-22 00:23:16 +02:00
directory_edit = memnew ( LineEdit ) ;
2025-06-12 07:55:29 +03:00
directory_edit - > set_accessibility_name ( ETR ( " Path: " ) ) ;
2025-04-22 00:23:16 +02:00
directory_edit - > set_structured_text_bidi_override ( TextServer : : STRUCTURED_TEXT_FILE ) ;
directory_edit - > set_h_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
top_toolbar - > add_child ( directory_edit ) ;
directory_edit - > connect ( SceneStringName ( text_submitted ) , callable_mp ( this , & FileDialog : : _dir_submitted ) ) ;
2014-02-09 22:10:30 -03:00
2025-04-22 00:23:16 +02:00
shortcuts_container = memnew ( HBoxContainer ) ;
top_toolbar - > add_child ( shortcuts_container ) ;
refresh_button = memnew ( Button ) ;
refresh_button - > set_theme_type_variation ( SceneStringName ( FlatButton ) ) ;
refresh_button - > set_tooltip_text ( ETR ( " Refresh files. " ) ) ;
2025-10-09 22:31:55 +02:00
refresh_button - > set_shortcut ( action_shortcuts [ ITEM_MENU_REFRESH ] ) ;
2025-04-22 00:23:16 +02:00
top_toolbar - > add_child ( refresh_button ) ;
refresh_button - > connect ( SceneStringName ( pressed ) , callable_mp ( this , & FileDialog : : update_file_list ) ) ;
2015-12-04 19:33:30 +01:00
2025-04-23 00:11:36 +02:00
favorite_button = memnew ( Button ) ;
favorite_button - > set_theme_type_variation ( SceneStringName ( FlatButton ) ) ;
2025-08-06 14:50:03 +02:00
favorite_button - > set_tooltip_text ( ETR ( " (Un)favorite current folder. " ) ) ;
2025-04-23 00:11:36 +02:00
top_toolbar - > add_child ( favorite_button ) ;
favorite_button - > connect ( SceneStringName ( pressed ) , callable_mp ( this , & FileDialog : : _favorite_pressed ) ) ;
2025-05-21 16:41:18 +02:00
make_dir_container = memnew ( HBoxContainer ) ;
top_toolbar - > add_child ( make_dir_container ) ;
make_dir_container - > add_child ( memnew ( VSeparator ) ) ;
2025-04-24 19:58:17 +02:00
make_dir_button = memnew ( Button ) ;
make_dir_button - > set_theme_type_variation ( SceneStringName ( FlatButton ) ) ;
make_dir_button - > set_tooltip_text ( ETR ( " Create a new folder. " ) ) ;
2025-05-21 16:41:18 +02:00
make_dir_container - > add_child ( make_dir_button ) ;
2025-04-24 19:58:17 +02:00
make_dir_button - > connect ( SceneStringName ( pressed ) , callable_mp ( this , & FileDialog : : _make_dir ) ) ;
2025-04-23 00:11:36 +02:00
HSplitContainer * main_split = memnew ( HSplitContainer ) ;
main_split - > set_v_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
main_vbox - > add_child ( main_split ) ;
{
VSplitContainer * fav_split = memnew ( VSplitContainer ) ;
main_split - > add_child ( fav_split ) ;
2025-05-21 16:41:18 +02:00
favorite_vbox = memnew ( VBoxContainer ) ;
favorite_vbox - > set_v_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
fav_split - > add_child ( favorite_vbox ) ;
2025-04-23 00:11:36 +02:00
HBoxContainer * fav_hbox = memnew ( HBoxContainer ) ;
2025-05-21 16:41:18 +02:00
favorite_vbox - > add_child ( fav_hbox ) ;
2025-04-23 00:11:36 +02:00
{
Label * label = memnew ( Label ( ETR ( " Favorites: " ) ) ) ;
label - > set_h_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
fav_hbox - > add_child ( label ) ;
}
fav_up_button = memnew ( Button ) ;
fav_up_button - > set_theme_type_variation ( SceneStringName ( FlatButton ) ) ;
fav_hbox - > add_child ( fav_up_button ) ;
fav_up_button - > connect ( SceneStringName ( pressed ) , callable_mp ( this , & FileDialog : : _favorite_move_up ) ) ;
fav_down_button = memnew ( Button ) ;
fav_down_button - > set_theme_type_variation ( SceneStringName ( FlatButton ) ) ;
fav_hbox - > add_child ( fav_down_button ) ;
fav_down_button - > connect ( SceneStringName ( pressed ) , callable_mp ( this , & FileDialog : : _favorite_move_down ) ) ;
favorite_list = memnew ( ItemList ) ;
favorite_list - > set_v_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
favorite_list - > set_auto_translate_mode ( AUTO_TRANSLATE_MODE_DISABLED ) ;
2025-10-02 13:23:02 +02:00
favorite_list - > set_theme_type_variation ( " ItemListSecondary " ) ;
2025-06-12 07:55:29 +03:00
favorite_list - > set_accessibility_name ( ETR ( " Favorites: " ) ) ;
2025-05-21 16:41:18 +02:00
favorite_vbox - > add_child ( favorite_list ) ;
2025-04-23 00:11:36 +02:00
favorite_list - > connect ( SceneStringName ( item_selected ) , callable_mp ( this , & FileDialog : : _favorite_selected ) ) ;
2025-05-21 16:41:18 +02:00
recent_vbox = memnew ( VBoxContainer ) ;
2025-04-23 00:11:36 +02:00
recent_vbox - > set_v_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
fav_split - > add_child ( recent_vbox ) ;
{
Label * label = memnew ( Label ( ETR ( " Recent: " ) ) ) ;
recent_vbox - > add_child ( label ) ;
}
recent_list = memnew ( ItemList ) ;
recent_list - > set_v_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
recent_list - > set_auto_translate_mode ( AUTO_TRANSLATE_MODE_DISABLED ) ;
2025-10-02 13:23:02 +02:00
recent_list - > set_theme_type_variation ( " ItemListSecondary " ) ;
2025-06-12 07:55:29 +03:00
recent_list - > set_accessibility_name ( ETR ( " Recent: " ) ) ;
2025-04-23 00:11:36 +02:00
recent_vbox - > add_child ( recent_list ) ;
recent_list - > connect ( SceneStringName ( item_selected ) , callable_mp ( this , & FileDialog : : _recent_selected ) ) ;
}
VBoxContainer * file_vbox = memnew ( VBoxContainer ) ;
main_split - > add_child ( file_vbox ) ;
2025-04-24 19:58:17 +02:00
HBoxContainer * lower_toolbar = memnew ( HBoxContainer ) ;
2025-04-23 00:11:36 +02:00
file_vbox - > add_child ( lower_toolbar ) ;
2025-04-24 19:58:17 +02:00
{
Label * label = memnew ( Label ( ETR ( " Directories & Files: " ) ) ) ;
label - > set_h_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
label - > set_theme_type_variation ( " HeaderSmall " ) ;
lower_toolbar - > add_child ( label ) ;
}
2020-06-19 20:49:04 +02:00
show_hidden = memnew ( Button ) ;
2024-12-05 17:13:20 +03:00
show_hidden - > set_theme_type_variation ( SceneStringName ( FlatButton ) ) ;
2019-06-10 00:20:24 -03:00
show_hidden - > set_toggle_mode ( true ) ;
show_hidden - > set_pressed ( is_showing_hidden_files ( ) ) ;
2023-12-15 20:56:06 -03:00
show_hidden - > set_tooltip_text ( ETR ( " Toggle the visibility of hidden files. " ) ) ;
2025-10-09 22:31:55 +02:00
show_hidden - > set_shortcut ( action_shortcuts [ ITEM_MENU_TOGGLE_HIDDEN ] ) ;
2025-04-24 19:58:17 +02:00
lower_toolbar - > add_child ( show_hidden ) ;
2024-06-01 13:15:13 +03:00
show_hidden - > connect ( SceneStringName ( toggled ) , callable_mp ( this , & FileDialog : : set_show_hidden_files ) ) ;
2019-06-10 00:20:24 -03:00
2025-05-21 16:41:18 +02:00
show_hidden_separator = memnew ( VSeparator ) ;
lower_toolbar - > add_child ( show_hidden_separator ) ;
layout_container = memnew ( HBoxContainer ) ;
lower_toolbar - > add_child ( layout_container ) ;
2025-04-24 19:58:17 +02:00
2025-04-28 14:57:50 +02:00
Ref < ButtonGroup > view_mode_group ;
view_mode_group . instantiate ( ) ;
thumbnail_mode_button = memnew ( Button ) ;
thumbnail_mode_button - > set_toggle_mode ( true ) ;
thumbnail_mode_button - > set_pressed ( true ) ;
thumbnail_mode_button - > set_button_group ( view_mode_group ) ;
thumbnail_mode_button - > set_theme_type_variation ( SceneStringName ( FlatButton ) ) ;
thumbnail_mode_button - > set_tooltip_text ( ETR ( " View items as a grid of thumbnails. " ) ) ;
2025-05-21 16:41:18 +02:00
layout_container - > add_child ( thumbnail_mode_button ) ;
2025-04-28 14:57:50 +02:00
thumbnail_mode_button - > connect ( SceneStringName ( pressed ) , callable_mp ( this , & FileDialog : : set_display_mode ) . bind ( DISPLAY_THUMBNAILS ) ) ;
list_mode_button = memnew ( Button ) ;
list_mode_button - > set_toggle_mode ( true ) ;
list_mode_button - > set_button_group ( view_mode_group ) ;
list_mode_button - > set_theme_type_variation ( SceneStringName ( FlatButton ) ) ;
list_mode_button - > set_tooltip_text ( ETR ( " View items as a list. " ) ) ;
2025-05-21 16:41:18 +02:00
layout_container - > add_child ( list_mode_button ) ;
2025-04-28 14:57:50 +02:00
list_mode_button - > connect ( SceneStringName ( pressed ) , callable_mp ( this , & FileDialog : : set_display_mode ) . bind ( DISPLAY_LIST ) ) ;
2025-05-21 16:41:18 +02:00
layout_separator = memnew ( VSeparator ) ;
layout_container - > add_child ( layout_separator ) ;
2025-04-28 14:57:50 +02:00
2024-02-22 18:34:46 +01:00
show_filename_filter_button = memnew ( Button ) ;
2024-12-05 17:13:20 +03:00
show_filename_filter_button - > set_theme_type_variation ( SceneStringName ( FlatButton ) ) ;
2024-02-22 18:34:46 +01:00
show_filename_filter_button - > set_toggle_mode ( true ) ;
2025-04-03 20:40:09 +02:00
show_filename_filter_button - > set_tooltip_text ( ETR ( " Toggle the visibility of the filter for file names. " ) ) ;
2025-10-09 22:31:55 +02:00
show_filename_filter_button - > set_shortcut ( action_shortcuts [ ITEM_MENU_FIND ] ) ;
2025-04-24 19:58:17 +02:00
lower_toolbar - > add_child ( show_filename_filter_button ) ;
2024-02-22 18:34:46 +01:00
show_filename_filter_button - > connect ( SceneStringName ( toggled ) , callable_mp ( this , & FileDialog : : set_show_filename_filter ) ) ;
2025-04-24 19:58:17 +02:00
file_sort_button = memnew ( MenuButton ) ;
file_sort_button - > set_flat ( false ) ;
file_sort_button - > set_theme_type_variation ( " FlatMenuButton " ) ;
file_sort_button - > set_tooltip_text ( ETR ( " Sort files " ) ) ;
PopupMenu * sort_menu = file_sort_button - > get_popup ( ) ;
sort_menu - > add_radio_check_item ( ETR ( " Sort by Name (Ascending) " ) , int ( FileSortOption : : NAME ) ) ;
sort_menu - > add_radio_check_item ( ETR ( " Sort by Name (Descending) " ) , int ( FileSortOption : : NAME_REVERSE ) ) ;
sort_menu - > add_radio_check_item ( ETR ( " Sort by Type (Ascending) " ) , int ( FileSortOption : : TYPE ) ) ;
sort_menu - > add_radio_check_item ( ETR ( " Sort by Type (Descending) " ) , int ( FileSortOption : : TYPE_REVERSE ) ) ;
sort_menu - > add_radio_check_item ( ETR ( " Sort by Modified Time (Newest First) " ) , int ( FileSortOption : : MODIFIED_TIME ) ) ;
sort_menu - > add_radio_check_item ( ETR ( " Sort by Modified Time (Oldest First) " ) , int ( FileSortOption : : MODIFIED_TIME_REVERSE ) ) ;
sort_menu - > set_item_checked ( 0 , true ) ;
lower_toolbar - > add_child ( file_sort_button ) ;
sort_menu - > connect ( SceneStringName ( id_pressed ) , callable_mp ( this , & FileDialog : : _sort_option_selected ) ) ;
2016-03-09 00:00:52 +01:00
2025-04-22 11:04:16 +02:00
file_list = memnew ( ItemList ) ;
file_list - > set_auto_translate_mode ( AUTO_TRANSLATE_MODE_DISABLED ) ;
file_list - > set_v_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
2025-06-12 07:55:29 +03:00
file_list - > set_accessibility_name ( ETR ( " Directories & Files: " ) ) ;
2025-04-22 11:04:16 +02:00
file_list - > set_allow_rmb_select ( true ) ;
2025-04-23 00:11:36 +02:00
file_vbox - > add_child ( file_list ) ;
2025-04-22 11:04:16 +02:00
file_list - > connect ( " multi_selected " , callable_mp ( this , & FileDialog : : _file_list_multi_selected ) ) ;
file_list - > connect ( " item_selected " , callable_mp ( this , & FileDialog : : _file_list_selected ) ) ;
file_list - > connect ( " item_activated " , callable_mp ( this , & FileDialog : : _file_list_item_activated ) ) ;
file_list - > connect ( " item_clicked " , callable_mp ( this , & FileDialog : : _item_clicked ) ) ;
file_list - > connect ( " empty_clicked " , callable_mp ( this , & FileDialog : : _empty_clicked ) ) ;
2016-03-09 00:00:52 +01:00
2021-03-19 14:01:03 +02:00
message = memnew ( Label ) ;
2025-04-22 11:57:16 +03:00
message - > set_focus_mode ( Control : : FOCUS_ACCESSIBILITY ) ;
2021-03-19 14:01:03 +02:00
message - > hide ( ) ;
2022-03-18 19:02:57 -05:00
message - > set_anchors_and_offsets_preset ( Control : : PRESET_FULL_RECT ) ;
2021-11-24 20:58:47 -06:00
message - > set_horizontal_alignment ( HORIZONTAL_ALIGNMENT_CENTER ) ;
message - > set_vertical_alignment ( VERTICAL_ALIGNMENT_CENTER ) ;
2025-04-22 11:04:16 +02:00
file_list - > add_child ( message ) ;
2021-03-19 14:01:03 +02:00
2024-02-22 18:34:46 +01:00
filename_filter_box = memnew ( HBoxContainer ) ;
2025-04-22 00:23:16 +02:00
filename_filter_box - > set_visible ( false ) ;
2025-04-23 00:11:36 +02:00
file_vbox - > add_child ( filename_filter_box ) ;
2025-04-22 00:23:16 +02:00
{
Label * label = memnew ( Label ( ETR ( " Filter: " ) ) ) ;
filename_filter_box - > add_child ( label ) ;
}
2024-02-22 18:34:46 +01:00
filename_filter = memnew ( LineEdit ) ;
filename_filter - > set_structured_text_bidi_override ( TextServer : : STRUCTURED_TEXT_FILE ) ;
filename_filter - > set_stretch_ratio ( 4 ) ;
filename_filter - > set_h_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
filename_filter - > set_clear_button_enabled ( true ) ;
2025-06-12 07:55:29 +03:00
filename_filter - > set_accessibility_name ( ETR ( " Filename Filter: " ) ) ;
2024-02-22 18:34:46 +01:00
filename_filter_box - > add_child ( filename_filter ) ;
2025-04-22 00:23:16 +02:00
filename_filter - > connect ( SceneStringName ( text_changed ) , callable_mp ( this , & FileDialog : : _filename_filter_changed ) . unbind ( 1 ) ) ;
filename_filter - > connect ( SceneStringName ( text_submitted ) , callable_mp ( this , & FileDialog : : _filename_filter_selected ) . unbind ( 1 ) ) ;
2024-02-22 18:34:46 +01:00
2019-10-26 17:29:22 +02:00
file_box = memnew ( HBoxContainer ) ;
2025-04-23 00:11:36 +02:00
file_vbox - > add_child ( file_box ) ;
2025-04-22 00:23:16 +02:00
{
Label * label = memnew ( Label ( ETR ( " File: " ) ) ) ;
file_box - > add_child ( label ) ;
}
filename_edit = memnew ( LineEdit ) ;
2025-06-12 07:55:29 +03:00
filename_edit - > set_accessibility_name ( ETR ( " File: " ) ) ;
2025-04-22 00:23:16 +02:00
filename_edit - > set_structured_text_bidi_override ( TextServer : : STRUCTURED_TEXT_FILE ) ;
filename_edit - > set_stretch_ratio ( 4 ) ;
filename_edit - > set_h_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
file_box - > add_child ( filename_edit ) ;
filename_edit - > connect ( SceneStringName ( text_submitted ) , callable_mp ( this , & FileDialog : : _file_submitted ) ) ;
2014-02-09 22:10:30 -03:00
filter = memnew ( OptionButton ) ;
2017-10-06 21:01:54 +07:00
filter - > set_stretch_ratio ( 3 ) ;
2020-03-06 14:00:16 -03:00
filter - > set_h_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
2025-04-22 00:23:16 +02:00
filter - > set_clip_text ( true ) ; // Too many extensions overflows it.
2019-10-26 17:29:22 +02:00
file_box - > add_child ( filter ) ;
2025-04-22 00:23:16 +02:00
filter - > connect ( SceneStringName ( item_selected ) , callable_mp ( this , & FileDialog : : _filter_selected ) ) ;
2014-02-09 22:10:30 -03:00
2025-04-22 15:32:34 +02:00
flow_checkbox_options = memnew ( HFlowContainer ) ;
flow_checkbox_options - > set_h_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
flow_checkbox_options - > set_alignment ( FlowContainer : : ALIGNMENT_CENTER ) ;
main_vbox - > add_child ( flow_checkbox_options ) ;
grid_select_options = memnew ( GridContainer ) ;
grid_select_options - > set_h_size_flags ( Control : : SIZE_SHRINK_CENTER ) ;
grid_select_options - > set_columns ( 2 ) ;
main_vbox - > add_child ( grid_select_options ) ;
2016-03-09 00:00:52 +01:00
2014-02-09 22:10:30 -03:00
confirm_save = memnew ( ConfirmationDialog ) ;
2021-08-25 15:49:30 +02:00
add_child ( confirm_save , false , INTERNAL_MODE_FRONT ) ;
2024-05-14 14:28:18 +02:00
confirm_save - > connect ( SceneStringName ( confirmed ) , callable_mp ( this , & FileDialog : : _save_confirm_pressed ) ) ;
2016-03-09 00:00:52 +01:00
2025-10-01 14:49:52 +02:00
delete_dialog = memnew ( ConfirmationDialog ) ;
delete_dialog - > set_text ( ETR ( " Delete the selected file? \n Depending on your filesystem configuration, the files will either be moved to the system trash or deleted permanently. " ) ) ;
add_child ( delete_dialog , false , INTERNAL_MODE_FRONT ) ;
delete_dialog - > connect ( SceneStringName ( confirmed ) , callable_mp ( this , & FileDialog : : _delete_confirm ) ) ;
2025-04-22 00:23:16 +02:00
make_dir_dialog = memnew ( ConfirmationDialog ) ;
make_dir_dialog - > set_title ( ETR ( " Create Folder " ) ) ;
add_child ( make_dir_dialog , false , INTERNAL_MODE_FRONT ) ;
make_dir_dialog - > connect ( SceneStringName ( confirmed ) , callable_mp ( this , & FileDialog : : _make_dir_confirm ) ) ;
2014-02-09 22:10:30 -03:00
VBoxContainer * makevb = memnew ( VBoxContainer ) ;
2025-04-22 00:23:16 +02:00
make_dir_dialog - > add_child ( makevb ) ;
new_dir_name = memnew ( LineEdit ) ;
new_dir_name - > set_structured_text_bidi_override ( TextServer : : STRUCTURED_TEXT_FILE ) ;
makevb - > add_margin_child ( ETR ( " Name: " ) , new_dir_name ) ;
make_dir_dialog - > register_text_enter ( new_dir_name ) ;
2014-02-09 22:10:30 -03:00
mkdirerr = memnew ( AcceptDialog ) ;
2023-12-15 20:56:06 -03:00
mkdirerr - > set_text ( ETR ( " Could not create folder. " ) ) ;
2021-08-25 15:49:30 +02:00
add_child ( mkdirerr , false , INTERNAL_MODE_FRONT ) ;
2014-02-09 22:10:30 -03:00
2014-04-10 00:18:27 -03:00
exterr = memnew ( AcceptDialog ) ;
2023-12-15 20:56:06 -03:00
exterr - > set_text ( ETR ( " Invalid extension, or empty filename. " ) ) ;
2021-08-25 15:49:30 +02:00
add_child ( exterr , false , INTERNAL_MODE_FRONT ) ;
2014-04-10 00:18:27 -03:00
2024-11-04 11:50:42 +02:00
item_menu = memnew ( PopupMenu ) ;
item_menu - > connect ( SceneStringName ( id_pressed ) , callable_mp ( this , & FileDialog : : _item_menu_id_pressed ) ) ;
2025-04-22 00:23:16 +02:00
add_child ( item_menu , false , INTERNAL_MODE_FRONT ) ;
2024-11-04 11:50:42 +02:00
2025-04-22 00:23:16 +02:00
connect ( SceneStringName ( confirmed ) , callable_mp ( this , & FileDialog : : _action_pressed ) ) ;
_update_drives ( ) ;
2014-02-09 22:10:30 -03:00
update_filters ( ) ;
2024-02-22 18:34:46 +01:00
update_filename_filter_gui ( ) ;
2014-02-09 22:10:30 -03:00
update_dir ( ) ;
2020-05-14 16:41:43 +02:00
if ( register_func ) {
2014-02-09 22:10:30 -03:00
register_func ( this ) ;
2020-05-14 16:41:43 +02:00
}
2024-02-14 02:11:45 +01:00
property_helper . setup_for_instance ( base_property_helper , this ) ;
2014-02-09 22:10:30 -03:00
}
FileDialog : : ~ FileDialog ( ) {
2020-05-14 16:41:43 +02:00
if ( unregister_func ) {
2014-02-09 22:10:30 -03:00
unregister_func ( this ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-09 22:10:30 -03:00
}