Merge pull request #81218 from bruvzg/_temp_fs

[Native File Dialogs] Improve filter list handling, add selected filter to the callback.
This commit is contained in:
Rémi Verschelde 2023-10-04 15:34:16 +02:00
commit d5db0e5032
No known key found for this signature in database
GPG key ID: C3336907360768E1
8 changed files with 347 additions and 184 deletions

View file

@ -119,7 +119,7 @@
<param index="6" name="callback" type="Callable" /> <param index="6" name="callback" type="Callable" />
<description> <description>
Displays OS native dialog for selecting files or directories in the file system. Displays OS native dialog for selecting files or directories in the file system.
Callbacks have the following arguments: [code]bool status, PackedStringArray selected_paths[/code]. Callbacks have the following arguments: [code]bool status, PackedStringArray selected_paths, int selected_filter_index[/code].
[b]Note:[/b] This method is implemented if the display server has the [code]FEATURE_NATIVE_DIALOG[/code] feature. [b]Note:[/b] This method is implemented if the display server has the [code]FEATURE_NATIVE_DIALOG[/code] feature.
[b]Note:[/b] This method is implemented on Linux, Windows and macOS. [b]Note:[/b] This method is implemented on Linux, Windows and macOS.
[b]Note:[/b] [param current_directory] might be ignored. [b]Note:[/b] [param current_directory] might be ignored.

View file

@ -142,36 +142,40 @@ void FreeDesktopPortalDesktop::append_dbus_string(DBusMessageIter *p_iter, const
} }
} }
void FreeDesktopPortalDesktop::append_dbus_dict_filters(DBusMessageIter *p_iter, const Vector<String> &p_filters) { void FreeDesktopPortalDesktop::append_dbus_dict_filters(DBusMessageIter *p_iter, const Vector<String> &p_filter_names, const Vector<String> &p_filter_exts) {
DBusMessageIter dict_iter; DBusMessageIter dict_iter;
DBusMessageIter var_iter; DBusMessageIter var_iter;
DBusMessageIter arr_iter; DBusMessageIter arr_iter;
const char *filters_key = "filters"; const char *filters_key = "filters";
ERR_FAIL_COND(p_filter_names.size() != p_filter_exts.size());
dbus_message_iter_open_container(p_iter, DBUS_TYPE_DICT_ENTRY, nullptr, &dict_iter); dbus_message_iter_open_container(p_iter, DBUS_TYPE_DICT_ENTRY, nullptr, &dict_iter);
dbus_message_iter_append_basic(&dict_iter, DBUS_TYPE_STRING, &filters_key); dbus_message_iter_append_basic(&dict_iter, DBUS_TYPE_STRING, &filters_key);
dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_VARIANT, "a(sa(us))", &var_iter); dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_VARIANT, "a(sa(us))", &var_iter);
dbus_message_iter_open_container(&var_iter, DBUS_TYPE_ARRAY, "(sa(us))", &arr_iter); dbus_message_iter_open_container(&var_iter, DBUS_TYPE_ARRAY, "(sa(us))", &arr_iter);
for (int i = 0; i < p_filters.size(); i++) { for (int i = 0; i < p_filter_names.size(); i++) {
Vector<String> tokens = p_filters[i].split(";"); DBusMessageIter struct_iter;
if (tokens.size() == 2) { DBusMessageIter array_iter;
DBusMessageIter struct_iter; DBusMessageIter array_struct_iter;
DBusMessageIter array_iter; dbus_message_iter_open_container(&arr_iter, DBUS_TYPE_STRUCT, nullptr, &struct_iter);
DBusMessageIter array_struct_iter; append_dbus_string(&struct_iter, p_filter_names[i]);
dbus_message_iter_open_container(&arr_iter, DBUS_TYPE_STRUCT, nullptr, &struct_iter);
append_dbus_string(&struct_iter, tokens[0]);
dbus_message_iter_open_container(&struct_iter, DBUS_TYPE_ARRAY, "(us)", &array_iter); dbus_message_iter_open_container(&struct_iter, DBUS_TYPE_ARRAY, "(us)", &array_iter);
String flt = p_filter_exts[i];
int filter_slice_count = flt.get_slice_count(",");
for (int j = 0; j < filter_slice_count; j++) {
dbus_message_iter_open_container(&array_iter, DBUS_TYPE_STRUCT, nullptr, &array_struct_iter); dbus_message_iter_open_container(&array_iter, DBUS_TYPE_STRUCT, nullptr, &array_struct_iter);
String str = (flt.get_slice(",", j).strip_edges());
{ {
const unsigned nil = 0; const unsigned nil = 0;
dbus_message_iter_append_basic(&array_struct_iter, DBUS_TYPE_UINT32, &nil); dbus_message_iter_append_basic(&array_struct_iter, DBUS_TYPE_UINT32, &nil);
} }
append_dbus_string(&array_struct_iter, tokens[1]); append_dbus_string(&array_struct_iter, str);
dbus_message_iter_close_container(&array_iter, &array_struct_iter); dbus_message_iter_close_container(&array_iter, &array_struct_iter);
dbus_message_iter_close_container(&struct_iter, &array_iter);
dbus_message_iter_close_container(&arr_iter, &struct_iter);
} }
dbus_message_iter_close_container(&struct_iter, &array_iter);
dbus_message_iter_close_container(&arr_iter, &struct_iter);
} }
dbus_message_iter_close_container(&var_iter, &arr_iter); dbus_message_iter_close_container(&var_iter, &arr_iter);
dbus_message_iter_close_container(&dict_iter, &var_iter); dbus_message_iter_close_container(&dict_iter, &var_iter);
@ -219,7 +223,7 @@ void FreeDesktopPortalDesktop::append_dbus_dict_bool(DBusMessageIter *p_iter, co
dbus_message_iter_close_container(p_iter, &dict_iter); dbus_message_iter_close_container(p_iter, &dict_iter);
} }
bool FreeDesktopPortalDesktop::file_chooser_parse_response(DBusMessageIter *p_iter, bool &r_cancel, Vector<String> &r_urls) { bool FreeDesktopPortalDesktop::file_chooser_parse_response(DBusMessageIter *p_iter, const Vector<String> &p_names, bool &r_cancel, Vector<String> &r_urls, int &r_index) {
ERR_FAIL_COND_V(dbus_message_iter_get_arg_type(p_iter) != DBUS_TYPE_UINT32, false); ERR_FAIL_COND_V(dbus_message_iter_get_arg_type(p_iter) != DBUS_TYPE_UINT32, false);
dbus_uint32_t resp_code; dbus_uint32_t resp_code;
@ -243,7 +247,22 @@ bool FreeDesktopPortalDesktop::file_chooser_parse_response(DBusMessageIter *p_it
DBusMessageIter var_iter; DBusMessageIter var_iter;
dbus_message_iter_recurse(&iter, &var_iter); dbus_message_iter_recurse(&iter, &var_iter);
if (strcmp(key, "uris") == 0) { if (strcmp(key, "current_filter") == 0) { // (sa(us))
if (dbus_message_iter_get_arg_type(&var_iter) == DBUS_TYPE_STRUCT) {
DBusMessageIter struct_iter;
dbus_message_iter_recurse(&var_iter, &struct_iter);
while (dbus_message_iter_get_arg_type(&struct_iter) == DBUS_TYPE_STRING) {
const char *value;
dbus_message_iter_get_basic(&struct_iter, &value);
String name = String::utf8(value);
r_index = p_names.find(name);
if (!dbus_message_iter_next(&struct_iter)) {
break;
}
}
}
} else if (strcmp(key, "uris") == 0) { // as
if (dbus_message_iter_get_arg_type(&var_iter) == DBUS_TYPE_ARRAY) { if (dbus_message_iter_get_arg_type(&var_iter) == DBUS_TYPE_ARRAY) {
DBusMessageIter uri_iter; DBusMessageIter uri_iter;
dbus_message_iter_recurse(&var_iter, &uri_iter); dbus_message_iter_recurse(&var_iter, &uri_iter);
@ -271,6 +290,30 @@ Error FreeDesktopPortalDesktop::file_dialog_show(DisplayServer::WindowID p_windo
return FAILED; return FAILED;
} }
ERR_FAIL_INDEX_V(int(p_mode), DisplayServer::FILE_DIALOG_MODE_SAVE_MAX, FAILED);
Vector<String> filter_names;
Vector<String> filter_exts;
for (int i = 0; i < p_filters.size(); i++) {
Vector<String> tokens = p_filters[i].split(";");
if (tokens.size() >= 1) {
String flt = tokens[0].strip_edges();
if (!flt.is_empty()) {
if (tokens.size() == 2) {
filter_exts.push_back(flt);
filter_names.push_back(tokens[1]);
} else {
filter_exts.push_back(flt);
filter_names.push_back(flt);
}
}
}
}
if (filter_names.is_empty()) {
filter_exts.push_back("*.*");
filter_names.push_back(RTR("All Files"));
}
DBusError err; DBusError err;
dbus_error_init(&err); dbus_error_init(&err);
@ -278,6 +321,7 @@ Error FreeDesktopPortalDesktop::file_dialog_show(DisplayServer::WindowID p_windo
FileDialogData fd; FileDialogData fd;
fd.callback = p_callback; fd.callback = p_callback;
fd.prev_focus = p_window_id; fd.prev_focus = p_window_id;
fd.filter_names = filter_names;
CryptoCore::RandomGenerator rng; CryptoCore::RandomGenerator rng;
ERR_FAIL_COND_V_MSG(rng.init(), FAILED, "Failed to initialize random number generator."); ERR_FAIL_COND_V_MSG(rng.init(), FAILED, "Failed to initialize random number generator.");
@ -308,16 +352,10 @@ Error FreeDesktopPortalDesktop::file_dialog_show(DisplayServer::WindowID p_windo
// Generate FileChooser message. // Generate FileChooser message.
const char *method = nullptr; const char *method = nullptr;
switch (p_mode) { if (p_mode == DisplayServer::FILE_DIALOG_MODE_SAVE_FILE) {
case DisplayServer::FILE_DIALOG_MODE_SAVE_FILE: { method = "SaveFile";
method = "SaveFile"; } else {
} break; method = "OpenFile";
case DisplayServer::FILE_DIALOG_MODE_OPEN_ANY:
case DisplayServer::FILE_DIALOG_MODE_OPEN_FILE:
case DisplayServer::FILE_DIALOG_MODE_OPEN_DIR:
case DisplayServer::FILE_DIALOG_MODE_OPEN_FILES: {
method = "OpenFile";
} break;
} }
DBusMessage *message = dbus_message_new_method_call(BUS_OBJECT_NAME, BUS_OBJECT_PATH, BUS_INTERFACE_FILE_CHOOSER, method); DBusMessage *message = dbus_message_new_method_call(BUS_OBJECT_NAME, BUS_OBJECT_PATH, BUS_INTERFACE_FILE_CHOOSER, method);
@ -334,7 +372,7 @@ Error FreeDesktopPortalDesktop::file_dialog_show(DisplayServer::WindowID p_windo
append_dbus_dict_string(&arr_iter, "handle_token", token); append_dbus_dict_string(&arr_iter, "handle_token", token);
append_dbus_dict_bool(&arr_iter, "multiple", p_mode == DisplayServer::FILE_DIALOG_MODE_OPEN_FILES); append_dbus_dict_bool(&arr_iter, "multiple", p_mode == DisplayServer::FILE_DIALOG_MODE_OPEN_FILES);
append_dbus_dict_bool(&arr_iter, "directory", p_mode == DisplayServer::FILE_DIALOG_MODE_OPEN_DIR); append_dbus_dict_bool(&arr_iter, "directory", p_mode == DisplayServer::FILE_DIALOG_MODE_OPEN_DIR);
append_dbus_dict_filters(&arr_iter, p_filters); append_dbus_dict_filters(&arr_iter, filter_names, filter_exts);
append_dbus_dict_string(&arr_iter, "current_folder", p_current_directory, true); append_dbus_dict_string(&arr_iter, "current_folder", p_current_directory, true);
if (p_mode == DisplayServer::FILE_DIALOG_MODE_SAVE_FILE) { if (p_mode == DisplayServer::FILE_DIALOG_MODE_SAVE_FILE) {
append_dbus_dict_string(&arr_iter, "current_name", p_filename); append_dbus_dict_string(&arr_iter, "current_name", p_filename);
@ -409,13 +447,15 @@ void FreeDesktopPortalDesktop::_thread_file_dialog_monitor(void *p_ud) {
if (dbus_message_iter_init(msg, &iter)) { if (dbus_message_iter_init(msg, &iter)) {
bool cancel = false; bool cancel = false;
Vector<String> uris; Vector<String> uris;
file_chooser_parse_response(&iter, cancel, uris); int index = 0;
file_chooser_parse_response(&iter, fd.filter_names, cancel, uris, index);
if (fd.callback.is_valid()) { if (fd.callback.is_valid()) {
Variant v_status = !cancel; Variant v_status = !cancel;
Variant v_files = uris; Variant v_files = uris;
Variant *v_args[2] = { &v_status, &v_files }; Variant v_index = index;
fd.callback.call_deferredp((const Variant **)&v_args, 2); Variant *v_args[3] = { &v_status, &v_files, &v_index };
fd.callback.call_deferredp((const Variant **)&v_args, 3);
} }
if (fd.prev_focus != DisplayServer::INVALID_WINDOW_ID) { if (fd.prev_focus != DisplayServer::INVALID_WINDOW_ID) {
callable_mp(DisplayServer::get_singleton(), &DisplayServer::window_move_to_foreground).call_deferred(fd.prev_focus); callable_mp(DisplayServer::get_singleton(), &DisplayServer::window_move_to_foreground).call_deferred(fd.prev_focus);

View file

@ -49,12 +49,13 @@ private:
bool read_setting(const char *p_namespace, const char *p_key, int p_type, void *r_value); bool read_setting(const char *p_namespace, const char *p_key, int p_type, void *r_value);
static void append_dbus_string(DBusMessageIter *p_iter, const String &p_string); static void append_dbus_string(DBusMessageIter *p_iter, const String &p_string);
static void append_dbus_dict_filters(DBusMessageIter *p_iter, const Vector<String> &p_filters); static void append_dbus_dict_filters(DBusMessageIter *p_iter, const Vector<String> &p_filter_names, const Vector<String> &p_filter_exts);
static void append_dbus_dict_string(DBusMessageIter *p_iter, const String &p_key, const String &p_value, bool p_as_byte_array = false); static void append_dbus_dict_string(DBusMessageIter *p_iter, const String &p_key, const String &p_value, bool p_as_byte_array = false);
static void append_dbus_dict_bool(DBusMessageIter *p_iter, const String &p_key, bool p_value); static void append_dbus_dict_bool(DBusMessageIter *p_iter, const String &p_key, bool p_value);
static bool file_chooser_parse_response(DBusMessageIter *p_iter, bool &r_cancel, Vector<String> &r_urls); static bool file_chooser_parse_response(DBusMessageIter *p_iter, const Vector<String> &p_names, bool &r_cancel, Vector<String> &r_urls, int &r_index);
struct FileDialogData { struct FileDialogData {
Vector<String> filter_names;
DBusConnection *connection = nullptr; DBusConnection *connection = nullptr;
DisplayServer::WindowID prev_focus = DisplayServer::INVALID_WINDOW_ID; DisplayServer::WindowID prev_focus = DisplayServer::INVALID_WINDOW_ID;
Callable callback; Callable callback;

View file

@ -2012,179 +2012,275 @@ Error DisplayServerMacOS::dialog_show(String p_title, String p_description, Vect
return OK; return OK;
} }
Error DisplayServerMacOS::file_dialog_show(const String &p_title, const String &p_current_directory, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback) { @interface FileDialogDropdown : NSObject {
_THREAD_SAFE_METHOD_ NSSavePanel *dialog;
NSMutableArray *allowed_types;
int cur_index;
}
- (instancetype)initWithDialog:(NSSavePanel *)p_dialog fileTypes:(NSMutableArray *)p_allowed_types;
- (void)popupAction:(id)sender;
- (int)getIndex;
@end
@implementation FileDialogDropdown
- (int)getIndex {
return cur_index;
}
- (instancetype)initWithDialog:(NSSavePanel *)p_dialog fileTypes:(NSMutableArray *)p_allowed_types {
if ((self = [super init])) {
dialog = p_dialog;
allowed_types = p_allowed_types;
cur_index = 0;
}
return self;
}
- (void)popupAction:(id)sender {
NSUInteger index = [sender indexOfSelectedItem];
if (index < [allowed_types count]) {
[dialog setAllowedFileTypes:[allowed_types objectAtIndex:index]];
cur_index = index;
} else {
[dialog setAllowedFileTypes:@[]];
cur_index = -1;
}
}
@end
FileDialogDropdown *_make_accessory_view(NSSavePanel *p_panel, const Vector<String> &p_filters) {
NSView *group = [[NSView alloc] initWithFrame:NSZeroRect];
group.translatesAutoresizingMaskIntoConstraints = NO;
NSTextField *label = [NSTextField labelWithString:[NSString stringWithUTF8String:RTR("Format").utf8().get_data()]];
label.translatesAutoresizingMaskIntoConstraints = NO;
if (@available(macOS 10.14, *)) {
label.textColor = NSColor.secondaryLabelColor;
}
if (@available(macOS 11.10, *)) {
label.font = [NSFont systemFontOfSize:[NSFont smallSystemFontSize]];
}
[group addSubview:label];
NSPopUpButton *popup = [[NSPopUpButton alloc] initWithFrame:NSZeroRect pullsDown:NO];
popup.translatesAutoresizingMaskIntoConstraints = NO;
NSString *url = [NSString stringWithUTF8String:p_current_directory.utf8().get_data()];
NSMutableArray *allowed_types = [[NSMutableArray alloc] init]; NSMutableArray *allowed_types = [[NSMutableArray alloc] init];
bool allow_other = false; bool allow_other = false;
for (int i = 0; i < p_filters.size(); i++) { for (int i = 0; i < p_filters.size(); i++) {
Vector<String> tokens = p_filters[i].split(";"); Vector<String> tokens = p_filters[i].split(";");
if (tokens.size() > 0) { if (tokens.size() >= 1) {
if (tokens[0].strip_edges() == "*.*") { String flt = tokens[0].strip_edges();
allow_other = true; int filter_slice_count = flt.get_slice_count(",");
} else {
[allowed_types addObject:[NSString stringWithUTF8String:tokens[0].replace("*.", "").strip_edges().utf8().get_data()]]; NSMutableArray *type_filters = [[NSMutableArray alloc] init];
for (int j = 0; j < filter_slice_count; j++) {
String str = (flt.get_slice(",", j).strip_edges());
if (str.strip_edges() == "*.*" || str.strip_edges() == "*") {
allow_other = true;
} else if (!str.is_empty()) {
[type_filters addObject:[NSString stringWithUTF8String:str.replace("*.", "").strip_edges().utf8().get_data()]];
}
}
if ([type_filters count] > 0) {
NSString *name_str = [NSString stringWithUTF8String:((tokens.size() == 1) ? tokens[0] : vformat("%s (%s)", tokens[1], tokens[0])).strip_edges().utf8().get_data()];
[allowed_types addObject:type_filters];
[popup addItemWithTitle:name_str];
} }
} }
} }
FileDialogDropdown *handler = [[FileDialogDropdown alloc] initWithDialog:p_panel fileTypes:allowed_types];
popup.target = handler;
popup.action = @selector(popupAction:);
[group addSubview:popup];
NSView *view = [[NSView alloc] initWithFrame:NSZeroRect];
view.translatesAutoresizingMaskIntoConstraints = NO;
[view addSubview:group];
NSMutableArray *constraints = [NSMutableArray array];
[constraints addObject:[popup.topAnchor constraintEqualToAnchor:group.topAnchor constant:10]];
[constraints addObject:[label.leadingAnchor constraintEqualToAnchor:group.leadingAnchor constant:10]];
[constraints addObject:[popup.leadingAnchor constraintEqualToAnchor:label.trailingAnchor constant:10]];
[constraints addObject:[popup.firstBaselineAnchor constraintEqualToAnchor:label.firstBaselineAnchor]];
[constraints addObject:[group.trailingAnchor constraintEqualToAnchor:popup.trailingAnchor constant:10]];
[constraints addObject:[group.bottomAnchor constraintEqualToAnchor:popup.bottomAnchor constant:10]];
[constraints addObject:[group.topAnchor constraintEqualToAnchor:view.topAnchor]];
[constraints addObject:[group.centerXAnchor constraintEqualToAnchor:view.centerXAnchor]];
[constraints addObject:[view.bottomAnchor constraintEqualToAnchor:group.bottomAnchor]];
[NSLayoutConstraint activateConstraints:constraints];
[p_panel setAllowsOtherFileTypes:allow_other];
if ([allowed_types count] > 0) {
[p_panel setAccessoryView:view];
[p_panel setAllowedFileTypes:[allowed_types objectAtIndex:0]];
}
return handler;
}
Error DisplayServerMacOS::file_dialog_show(const String &p_title, const String &p_current_directory, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback) {
_THREAD_SAFE_METHOD_
ERR_FAIL_INDEX_V(int(p_mode), FILE_DIALOG_MODE_SAVE_MAX, FAILED);
NSString *url = [NSString stringWithUTF8String:p_current_directory.utf8().get_data()];
FileDialogDropdown *handler = nullptr;
WindowID prev_focus = last_focused_window; WindowID prev_focus = last_focused_window;
Callable callback = p_callback; // Make a copy for async completion handler. Callable callback = p_callback; // Make a copy for async completion handler.
switch (p_mode) { if (p_mode == FILE_DIALOG_MODE_SAVE_FILE) {
case FILE_DIALOG_MODE_SAVE_FILE: { NSSavePanel *panel = [NSSavePanel savePanel];
NSSavePanel *panel = [NSSavePanel savePanel];
[panel setDirectoryURL:[NSURL fileURLWithPath:url]]; [panel setDirectoryURL:[NSURL fileURLWithPath:url]];
if ([allowed_types count]) { handler = _make_accessory_view(panel, p_filters);
[panel setAllowedFileTypes:allowed_types]; [panel setExtensionHidden:YES];
} [panel setCanSelectHiddenExtension:YES];
[panel setAllowsOtherFileTypes:allow_other]; [panel setCanCreateDirectories:YES];
[panel setExtensionHidden:YES]; [panel setShowsHiddenFiles:p_show_hidden];
[panel setCanSelectHiddenExtension:YES]; if (p_filename != "") {
[panel setCanCreateDirectories:YES]; NSString *fileurl = [NSString stringWithUTF8String:p_filename.utf8().get_data()];
[panel setShowsHiddenFiles:p_show_hidden]; [panel setNameFieldStringValue:fileurl];
if (p_filename != "") { }
NSString *fileurl = [NSString stringWithUTF8String:p_filename.utf8().get_data()];
[panel setNameFieldStringValue:fileurl];
}
[panel beginSheetModalForWindow:[[NSApplication sharedApplication] mainWindow] [panel beginSheetModalForWindow:[[NSApplication sharedApplication] mainWindow]
completionHandler:^(NSInteger ret) { completionHandler:^(NSInteger ret) {
if (ret == NSModalResponseOK) { if (ret == NSModalResponseOK) {
// Save bookmark for folder. // Save bookmark for folder.
if (OS::get_singleton()->is_sandboxed()) { if (OS::get_singleton()->is_sandboxed()) {
NSArray *bookmarks = [[NSUserDefaults standardUserDefaults] arrayForKey:@"sec_bookmarks"]; NSArray *bookmarks = [[NSUserDefaults standardUserDefaults] arrayForKey:@"sec_bookmarks"];
bool skip = false;
for (id bookmark in bookmarks) {
NSError *error = nil;
BOOL isStale = NO;
NSURL *exurl = [NSURL URLByResolvingBookmarkData:bookmark options:NSURLBookmarkResolutionWithSecurityScope relativeToURL:nil bookmarkDataIsStale:&isStale error:&error];
if (!error && !isStale && ([[exurl path] compare:[[panel directoryURL] path]] == NSOrderedSame)) {
skip = true;
break;
}
}
if (!skip) {
NSError *error = nil;
NSData *bookmark = [[panel directoryURL] bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope includingResourceValuesForKeys:nil relativeToURL:nil error:&error];
if (!error) {
NSArray *new_bookmarks = [bookmarks arrayByAddingObject:bookmark];
[[NSUserDefaults standardUserDefaults] setObject:new_bookmarks forKey:@"sec_bookmarks"];
}
}
}
// Callback.
Vector<String> files;
String url;
url.parse_utf8([[[panel URL] path] UTF8String]);
files.push_back(url);
if (!callback.is_null()) {
Variant v_status = true;
Variant v_files = files;
Variant v_index = [handler getIndex];
Variant *v_args[3] = { &v_status, &v_files, &v_index };
Variant ret;
Callable::CallError ce;
callback.callp((const Variant **)&v_args, 3, ret, ce);
}
} else {
if (!callback.is_null()) {
Variant v_status = false;
Variant v_files = Vector<String>();
Variant v_index = [handler getIndex];
Variant *v_args[3] = { &v_status, &v_files, &v_index };
Variant ret;
Callable::CallError ce;
callback.callp((const Variant **)&v_args, 3, ret, ce);
}
}
if (prev_focus != INVALID_WINDOW_ID) {
callable_mp(DisplayServer::get_singleton(), &DisplayServer::window_move_to_foreground).call_deferred(prev_focus);
}
}];
} else {
NSOpenPanel *panel = [NSOpenPanel openPanel];
[panel setDirectoryURL:[NSURL fileURLWithPath:url]];
handler = _make_accessory_view(panel, p_filters);
[panel setExtensionHidden:YES];
[panel setCanSelectHiddenExtension:YES];
[panel setCanCreateDirectories:YES];
[panel setCanChooseFiles:(p_mode != FILE_DIALOG_MODE_OPEN_DIR)];
[panel setCanChooseDirectories:(p_mode == FILE_DIALOG_MODE_OPEN_DIR || p_mode == FILE_DIALOG_MODE_OPEN_ANY)];
[panel setShowsHiddenFiles:p_show_hidden];
if (p_filename != "") {
NSString *fileurl = [NSString stringWithUTF8String:p_filename.utf8().get_data()];
[panel setNameFieldStringValue:fileurl];
}
[panel setAllowsMultipleSelection:(p_mode == FILE_DIALOG_MODE_OPEN_FILES)];
[panel beginSheetModalForWindow:[[NSApplication sharedApplication] mainWindow]
completionHandler:^(NSInteger ret) {
if (ret == NSModalResponseOK) {
// Save bookmark for folder.
NSArray *urls = [(NSOpenPanel *)panel URLs];
if (OS::get_singleton()->is_sandboxed()) {
NSArray *bookmarks = [[NSUserDefaults standardUserDefaults] arrayForKey:@"sec_bookmarks"];
NSMutableArray *new_bookmarks = [bookmarks mutableCopy];
for (NSUInteger i = 0; i != [urls count]; ++i) {
bool skip = false; bool skip = false;
for (id bookmark in bookmarks) { for (id bookmark in bookmarks) {
NSError *error = nil; NSError *error = nil;
BOOL isStale = NO; BOOL isStale = NO;
NSURL *exurl = [NSURL URLByResolvingBookmarkData:bookmark options:NSURLBookmarkResolutionWithSecurityScope relativeToURL:nil bookmarkDataIsStale:&isStale error:&error]; NSURL *exurl = [NSURL URLByResolvingBookmarkData:bookmark options:NSURLBookmarkResolutionWithSecurityScope relativeToURL:nil bookmarkDataIsStale:&isStale error:&error];
if (!error && !isStale && ([[exurl path] compare:[[panel directoryURL] path]] == NSOrderedSame)) { if (!error && !isStale && ([[exurl path] compare:[[urls objectAtIndex:i] path]] == NSOrderedSame)) {
skip = true; skip = true;
break; break;
} }
} }
if (!skip) { if (!skip) {
NSError *error = nil; NSError *error = nil;
NSData *bookmark = [[panel directoryURL] bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope includingResourceValuesForKeys:nil relativeToURL:nil error:&error]; NSData *bookmark = [[urls objectAtIndex:i] bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope includingResourceValuesForKeys:nil relativeToURL:nil error:&error];
if (!error) { if (!error) {
NSArray *new_bookmarks = [bookmarks arrayByAddingObject:bookmark]; [new_bookmarks addObject:bookmark];
[[NSUserDefaults standardUserDefaults] setObject:new_bookmarks forKey:@"sec_bookmarks"];
} }
} }
} }
// Callback. [[NSUserDefaults standardUserDefaults] setObject:new_bookmarks forKey:@"sec_bookmarks"];
Vector<String> files; }
// Callback.
Vector<String> files;
for (NSUInteger i = 0; i != [urls count]; ++i) {
String url; String url;
url.parse_utf8([[[panel URL] path] UTF8String]); url.parse_utf8([[[urls objectAtIndex:i] path] UTF8String]);
files.push_back(url); files.push_back(url);
if (!callback.is_null()) {
Variant v_status = true;
Variant v_files = files;
Variant *v_args[2] = { &v_status, &v_files };
Variant ret;
Callable::CallError ce;
callback.callp((const Variant **)&v_args, 2, ret, ce);
}
} else {
if (!callback.is_null()) {
Variant v_status = false;
Variant v_files = Vector<String>();
Variant *v_args[2] = { &v_status, &v_files };
Variant ret;
Callable::CallError ce;
callback.callp((const Variant **)&v_args, 2, ret, ce);
}
} }
if (prev_focus != INVALID_WINDOW_ID) { if (!callback.is_null()) {
callable_mp(DisplayServer::get_singleton(), &DisplayServer::window_move_to_foreground).call_deferred(prev_focus); Variant v_status = true;
Variant v_files = files;
Variant v_index = [handler getIndex];
Variant *v_args[3] = { &v_status, &v_files, &v_index };
Variant ret;
Callable::CallError ce;
callback.callp((const Variant **)&v_args, 3, ret, ce);
} }
}]; } else {
} break; if (!callback.is_null()) {
case FILE_DIALOG_MODE_OPEN_ANY: Variant v_status = false;
case FILE_DIALOG_MODE_OPEN_FILE: Variant v_files = Vector<String>();
case FILE_DIALOG_MODE_OPEN_FILES: Variant v_index = [handler getIndex];
case FILE_DIALOG_MODE_OPEN_DIR: { Variant *v_args[3] = { &v_status, &v_files, &v_index };
NSOpenPanel *panel = [NSOpenPanel openPanel]; Variant ret;
Callable::CallError ce;
[panel setDirectoryURL:[NSURL fileURLWithPath:url]]; callback.callp((const Variant **)&v_args, 3, ret, ce);
if ([allowed_types count]) {
[panel setAllowedFileTypes:allowed_types];
}
[panel setAllowsOtherFileTypes:allow_other];
[panel setExtensionHidden:YES];
[panel setCanSelectHiddenExtension:YES];
[panel setCanCreateDirectories:YES];
[panel setCanChooseFiles:(p_mode != FILE_DIALOG_MODE_OPEN_DIR)];
[panel setCanChooseDirectories:(p_mode == FILE_DIALOG_MODE_OPEN_DIR || p_mode == FILE_DIALOG_MODE_OPEN_ANY)];
[panel setShowsHiddenFiles:p_show_hidden];
if (p_filename != "") {
NSString *fileurl = [NSString stringWithUTF8String:p_filename.utf8().get_data()];
[panel setNameFieldStringValue:fileurl];
}
[panel setAllowsMultipleSelection:(p_mode == FILE_DIALOG_MODE_OPEN_FILES)];
[panel beginSheetModalForWindow:[[NSApplication sharedApplication] mainWindow]
completionHandler:^(NSInteger ret) {
if (ret == NSModalResponseOK) {
// Save bookmark for folder.
NSArray *urls = [(NSOpenPanel *)panel URLs];
if (OS::get_singleton()->is_sandboxed()) {
NSArray *bookmarks = [[NSUserDefaults standardUserDefaults] arrayForKey:@"sec_bookmarks"];
NSMutableArray *new_bookmarks = [bookmarks mutableCopy];
for (NSUInteger i = 0; i != [urls count]; ++i) {
bool skip = false;
for (id bookmark in bookmarks) {
NSError *error = nil;
BOOL isStale = NO;
NSURL *exurl = [NSURL URLByResolvingBookmarkData:bookmark options:NSURLBookmarkResolutionWithSecurityScope relativeToURL:nil bookmarkDataIsStale:&isStale error:&error];
if (!error && !isStale && ([[exurl path] compare:[[urls objectAtIndex:i] path]] == NSOrderedSame)) {
skip = true;
break;
}
}
if (!skip) {
NSError *error = nil;
NSData *bookmark = [[urls objectAtIndex:i] bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope includingResourceValuesForKeys:nil relativeToURL:nil error:&error];
if (!error) {
[new_bookmarks addObject:bookmark];
}
}
}
[[NSUserDefaults standardUserDefaults] setObject:new_bookmarks forKey:@"sec_bookmarks"];
}
// Callback.
Vector<String> files;
for (NSUInteger i = 0; i != [urls count]; ++i) {
String url;
url.parse_utf8([[[urls objectAtIndex:i] path] UTF8String]);
files.push_back(url);
}
if (!callback.is_null()) {
Variant v_status = true;
Variant v_files = files;
Variant *v_args[2] = { &v_status, &v_files };
Variant ret;
Callable::CallError ce;
callback.callp((const Variant **)&v_args, 2, ret, ce);
}
} else {
if (!callback.is_null()) {
Variant v_status = false;
Variant v_files = Vector<String>();
Variant *v_args[2] = { &v_status, &v_files };
Variant ret;
Callable::CallError ce;
callback.callp((const Variant **)&v_args, 2, ret, ce);
}
} }
if (prev_focus != INVALID_WINDOW_ID) { }
callable_mp(DisplayServer::get_singleton(), &DisplayServer::window_move_to_foreground).call_deferred(prev_focus); if (prev_focus != INVALID_WINDOW_ID) {
} callable_mp(DisplayServer::get_singleton(), &DisplayServer::window_move_to_foreground).call_deferred(prev_focus);
}]; }
} break; }];
} }
return OK; return OK;

View file

@ -222,18 +222,37 @@ void DisplayServerWindows::tts_stop() {
Error DisplayServerWindows::file_dialog_show(const String &p_title, const String &p_current_directory, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback) { Error DisplayServerWindows::file_dialog_show(const String &p_title, const String &p_current_directory, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback) {
_THREAD_SAFE_METHOD_ _THREAD_SAFE_METHOD_
ERR_FAIL_INDEX_V(int(p_mode), FILE_DIALOG_MODE_SAVE_MAX, FAILED);
Vector<Char16String> filter_names; Vector<Char16String> filter_names;
Vector<Char16String> filter_exts; Vector<Char16String> filter_exts;
for (const String &E : p_filters) { for (const String &E : p_filters) {
Vector<String> tokens = E.split(";"); Vector<String> tokens = E.split(";");
if (tokens.size() == 2) { if (tokens.size() >= 1) {
filter_exts.push_back(tokens[0].strip_edges().utf16()); String flt = tokens[0].strip_edges();
filter_names.push_back(tokens[1].strip_edges().utf16()); int filter_slice_count = flt.get_slice_count(",");
} else if (tokens.size() == 1) { Vector<String> exts;
filter_exts.push_back(tokens[0].strip_edges().utf16()); for (int j = 0; j < filter_slice_count; j++) {
filter_names.push_back(tokens[0].strip_edges().utf16()); String str = (flt.get_slice(",", j).strip_edges());
if (!str.is_empty()) {
exts.push_back(str);
}
}
if (!exts.is_empty()) {
String str = String(";").join(exts);
filter_exts.push_back(str.utf16());
if (tokens.size() == 2) {
filter_names.push_back(tokens[1].strip_edges().utf16());
} else {
filter_names.push_back(str.utf16());
}
}
} }
} }
if (filter_names.is_empty()) {
filter_exts.push_back(String("*.*").utf16());
filter_names.push_back(RTR("All Files").utf16());
}
Vector<COMDLG_FILTERSPEC> filters; Vector<COMDLG_FILTERSPEC> filters;
for (int i = 0; i < filter_names.size(); i++) { for (int i = 0; i < filter_names.size(); i++) {
@ -287,6 +306,9 @@ Error DisplayServerWindows::file_dialog_show(const String &p_title, const String
} }
hr = pfd->Show(windows[window_id].hWnd); hr = pfd->Show(windows[window_id].hWnd);
UINT index = 0;
pfd->GetFileTypeIndex(&index);
if (SUCCEEDED(hr)) { if (SUCCEEDED(hr)) {
Vector<String> file_names; Vector<String> file_names;
@ -326,19 +348,21 @@ Error DisplayServerWindows::file_dialog_show(const String &p_title, const String
if (!p_callback.is_null()) { if (!p_callback.is_null()) {
Variant v_status = true; Variant v_status = true;
Variant v_files = file_names; Variant v_files = file_names;
Variant *v_args[2] = { &v_status, &v_files }; Variant v_index = index;
Variant *v_args[3] = { &v_status, &v_files, &v_index };
Variant ret; Variant ret;
Callable::CallError ce; Callable::CallError ce;
p_callback.callp((const Variant **)&v_args, 2, ret, ce); p_callback.callp((const Variant **)&v_args, 3, ret, ce);
} }
} else { } else {
if (!p_callback.is_null()) { if (!p_callback.is_null()) {
Variant v_status = false; Variant v_status = false;
Variant v_files = Vector<String>(); Variant v_files = Vector<String>();
Variant *v_args[2] = { &v_status, &v_files }; Variant v_index = index;
Variant *v_args[3] = { &v_status, &v_files, &v_index };
Variant ret; Variant ret;
Callable::CallError ce; Callable::CallError ce;
p_callback.callp((const Variant **)&v_args, 2, ret, ce); p_callback.callp((const Variant **)&v_args, 3, ret, ce);
} }
} }
pfd->Release(); pfd->Release();

View file

@ -73,7 +73,7 @@ void FileDialog::set_visible(bool p_visible) {
} }
} }
void FileDialog::_native_dialog_cb(bool p_ok, const Vector<String> &p_files) { void FileDialog::_native_dialog_cb(bool p_ok, const Vector<String> &p_files, int p_filter) {
if (p_ok) { if (p_ok) {
if (p_files.size() > 0) { if (p_files.size() > 0) {
String f = p_files[0]; String f = p_files[0];
@ -90,6 +90,7 @@ void FileDialog::_native_dialog_cb(bool p_ok, const Vector<String> &p_files) {
} }
file->set_text(f); file->set_text(f);
dir->set_text(f.get_base_dir()); dir->set_text(f.get_base_dir());
_filter_selected(p_filter);
} }
} else { } else {
file->set_text(""); file->set_text("");

View file

@ -159,7 +159,7 @@ private:
virtual void shortcut_input(const Ref<InputEvent> &p_event) override; virtual void shortcut_input(const Ref<InputEvent> &p_event) override;
void _native_dialog_cb(bool p_ok, const Vector<String> &p_files); void _native_dialog_cb(bool p_ok, const Vector<String> &p_files, int p_filter);
bool _is_open_should_be_disabled(); bool _is_open_should_be_disabled();

View file

@ -506,7 +506,8 @@ public:
FILE_DIALOG_MODE_OPEN_FILES, FILE_DIALOG_MODE_OPEN_FILES,
FILE_DIALOG_MODE_OPEN_DIR, FILE_DIALOG_MODE_OPEN_DIR,
FILE_DIALOG_MODE_OPEN_ANY, FILE_DIALOG_MODE_OPEN_ANY,
FILE_DIALOG_MODE_SAVE_FILE FILE_DIALOG_MODE_SAVE_FILE,
FILE_DIALOG_MODE_SAVE_MAX
}; };
virtual Error file_dialog_show(const String &p_title, const String &p_current_directory, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback); virtual Error file_dialog_show(const String &p_title, const String &p_current_directory, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback);