You've already forked godot
mirror of
https://github.com/godotengine/godot.git
synced 2025-11-04 12:00:25 +00:00
Improve native file dialog parent window selection.
This commit is contained in:
committed by
Pāvels Nadtočajevs
parent
134da37497
commit
3e4e6e6c0c
@@ -979,21 +979,25 @@ Error DisplayServerMacOS::dialog_show(String p_title, String p_description, Vect
|
||||
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) {
|
||||
return _file_dialog_with_options_show(p_title, p_current_directory, String(), p_filename, p_show_hidden, p_mode, p_filters, TypedArray<Dictionary>(), p_callback, false);
|
||||
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, WindowID p_window_id) {
|
||||
return _file_dialog_with_options_show(p_title, p_current_directory, String(), p_filename, p_show_hidden, p_mode, p_filters, TypedArray<Dictionary>(), p_callback, false, p_window_id);
|
||||
}
|
||||
|
||||
Error DisplayServerMacOS::file_dialog_with_options_show(const String &p_title, const String &p_current_directory, const String &p_root, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const TypedArray<Dictionary> &p_options, const Callable &p_callback) {
|
||||
return _file_dialog_with_options_show(p_title, p_current_directory, p_root, p_filename, p_show_hidden, p_mode, p_filters, p_options, p_callback, true);
|
||||
Error DisplayServerMacOS::file_dialog_with_options_show(const String &p_title, const String &p_current_directory, const String &p_root, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const TypedArray<Dictionary> &p_options, const Callable &p_callback, WindowID p_window_id) {
|
||||
return _file_dialog_with_options_show(p_title, p_current_directory, p_root, p_filename, p_show_hidden, p_mode, p_filters, p_options, p_callback, true, p_window_id);
|
||||
}
|
||||
|
||||
Error DisplayServerMacOS::_file_dialog_with_options_show(const String &p_title, const String &p_current_directory, const String &p_root, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const TypedArray<Dictionary> &p_options, const Callable &p_callback, bool p_options_in_cb) {
|
||||
Error DisplayServerMacOS::_file_dialog_with_options_show(const String &p_title, const String &p_current_directory, const String &p_root, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const TypedArray<Dictionary> &p_options, const Callable &p_callback, bool p_options_in_cb, WindowID p_window_id) {
|
||||
_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()];
|
||||
WindowID prev_focus = last_focused_window;
|
||||
|
||||
NSWindow *nswindow = nullptr;
|
||||
if (windows.has(p_window_id) && !windows[p_window_id].is_popup) {
|
||||
nswindow = windows[p_window_id].window_object;
|
||||
}
|
||||
|
||||
GodotOpenSaveDelegate *panel_delegate = [[GodotOpenSaveDelegate alloc] init];
|
||||
if (p_root.length() > 0) {
|
||||
@@ -1015,98 +1019,102 @@ Error DisplayServerMacOS::_file_dialog_with_options_show(const String &p_title,
|
||||
[panel setNameFieldStringValue:fileurl];
|
||||
}
|
||||
|
||||
[panel beginSheetModalForWindow:[[NSApplication sharedApplication] mainWindow]
|
||||
completionHandler:^(NSInteger ret) {
|
||||
if (ret == NSModalResponseOK) {
|
||||
// Save bookmark for folder.
|
||||
if (OS::get_singleton()->is_sandboxed()) {
|
||||
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_valid()) {
|
||||
if (p_options_in_cb) {
|
||||
Variant v_result = true;
|
||||
Variant v_files = files;
|
||||
Variant v_index = [panel_delegate getIndex];
|
||||
Variant v_opt = [panel_delegate getSelection];
|
||||
Variant ret;
|
||||
Callable::CallError ce;
|
||||
const Variant *args[4] = { &v_result, &v_files, &v_index, &v_opt };
|
||||
void (^completion_handler)(NSInteger ret) = ^(NSInteger ret) {
|
||||
if (ret == NSModalResponseOK) {
|
||||
// Save bookmark for folder.
|
||||
if (OS::get_singleton()->is_sandboxed()) {
|
||||
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_valid()) {
|
||||
if (p_options_in_cb) {
|
||||
Variant v_result = true;
|
||||
Variant v_files = files;
|
||||
Variant v_index = [panel_delegate getIndex];
|
||||
Variant v_opt = [panel_delegate getSelection];
|
||||
Variant ret;
|
||||
Callable::CallError ce;
|
||||
const Variant *args[4] = { &v_result, &v_files, &v_index, &v_opt };
|
||||
|
||||
callback.callp(args, 4, ret, ce);
|
||||
if (ce.error != Callable::CallError::CALL_OK) {
|
||||
ERR_PRINT(vformat("Failed to execute file dialog callback: %s.", Variant::get_callable_error_text(callback, args, 4, ce)));
|
||||
}
|
||||
} else {
|
||||
Variant v_result = true;
|
||||
Variant v_files = files;
|
||||
Variant v_index = [panel_delegate getIndex];
|
||||
Variant ret;
|
||||
Callable::CallError ce;
|
||||
const Variant *args[3] = { &v_result, &v_files, &v_index };
|
||||
callback.callp(args, 4, ret, ce);
|
||||
if (ce.error != Callable::CallError::CALL_OK) {
|
||||
ERR_PRINT(vformat("Failed to execute file dialog callback: %s.", Variant::get_callable_error_text(callback, args, 4, ce)));
|
||||
}
|
||||
} else {
|
||||
Variant v_result = true;
|
||||
Variant v_files = files;
|
||||
Variant v_index = [panel_delegate getIndex];
|
||||
Variant ret;
|
||||
Callable::CallError ce;
|
||||
const Variant *args[3] = { &v_result, &v_files, &v_index };
|
||||
|
||||
callback.callp(args, 3, ret, ce);
|
||||
if (ce.error != Callable::CallError::CALL_OK) {
|
||||
ERR_PRINT(vformat("Failed to execute file dialog callback: %s.", Variant::get_callable_error_text(callback, args, 3, ce)));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (callback.is_valid()) {
|
||||
if (p_options_in_cb) {
|
||||
Variant v_result = false;
|
||||
Variant v_files = Vector<String>();
|
||||
Variant v_index = [panel_delegate getIndex];
|
||||
Variant v_opt = [panel_delegate getSelection];
|
||||
Variant ret;
|
||||
Callable::CallError ce;
|
||||
const Variant *args[4] = { &v_result, &v_files, &v_index, &v_opt };
|
||||
callback.callp(args, 3, ret, ce);
|
||||
if (ce.error != Callable::CallError::CALL_OK) {
|
||||
ERR_PRINT(vformat("Failed to execute file dialog callback: %s.", Variant::get_callable_error_text(callback, args, 3, ce)));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (callback.is_valid()) {
|
||||
if (p_options_in_cb) {
|
||||
Variant v_result = false;
|
||||
Variant v_files = Vector<String>();
|
||||
Variant v_index = [panel_delegate getIndex];
|
||||
Variant v_opt = [panel_delegate getSelection];
|
||||
Variant ret;
|
||||
Callable::CallError ce;
|
||||
const Variant *args[4] = { &v_result, &v_files, &v_index, &v_opt };
|
||||
|
||||
callback.callp(args, 4, ret, ce);
|
||||
if (ce.error != Callable::CallError::CALL_OK) {
|
||||
ERR_PRINT(vformat("Failed to execute file dialog callback: %s.", Variant::get_callable_error_text(callback, args, 4, ce)));
|
||||
}
|
||||
} else {
|
||||
Variant v_result = false;
|
||||
Variant v_files = Vector<String>();
|
||||
Variant v_index = [panel_delegate getIndex];
|
||||
Variant ret;
|
||||
Callable::CallError ce;
|
||||
const Variant *args[3] = { &v_result, &v_files, &v_index };
|
||||
callback.callp(args, 4, ret, ce);
|
||||
if (ce.error != Callable::CallError::CALL_OK) {
|
||||
ERR_PRINT(vformat("Failed to execute file dialog callback: %s.", Variant::get_callable_error_text(callback, args, 4, ce)));
|
||||
}
|
||||
} else {
|
||||
Variant v_result = false;
|
||||
Variant v_files = Vector<String>();
|
||||
Variant v_index = [panel_delegate getIndex];
|
||||
Variant ret;
|
||||
Callable::CallError ce;
|
||||
const Variant *args[3] = { &v_result, &v_files, &v_index };
|
||||
|
||||
callback.callp(args, 3, ret, ce);
|
||||
if (ce.error != Callable::CallError::CALL_OK) {
|
||||
ERR_PRINT(vformat("Failed to execute file dialog callback: %s.", Variant::get_callable_error_text(callback, args, 3, ce)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (prev_focus != INVALID_WINDOW_ID) {
|
||||
callable_mp(DisplayServer::get_singleton(), &DisplayServer::window_move_to_foreground).call_deferred(prev_focus);
|
||||
}
|
||||
}];
|
||||
callback.callp(args, 3, ret, ce);
|
||||
if (ce.error != Callable::CallError::CALL_OK) {
|
||||
ERR_PRINT(vformat("Failed to execute file dialog callback: %s.", Variant::get_callable_error_text(callback, args, 3, ce)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (p_window_id != INVALID_WINDOW_ID) {
|
||||
callable_mp(DisplayServer::get_singleton(), &DisplayServer::window_move_to_foreground).call_deferred(p_window_id);
|
||||
}
|
||||
};
|
||||
if (nswindow) {
|
||||
[panel beginSheetModalForWindow:nswindow completionHandler:completion_handler];
|
||||
} else {
|
||||
[panel beginWithCompletionHandler:completion_handler];
|
||||
}
|
||||
} else {
|
||||
NSOpenPanel *panel = [NSOpenPanel openPanel];
|
||||
|
||||
@@ -1125,104 +1133,108 @@ Error DisplayServerMacOS::_file_dialog_with_options_show(const String &p_title,
|
||||
}
|
||||
[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_valid()) {
|
||||
if (p_options_in_cb) {
|
||||
Variant v_result = true;
|
||||
Variant v_files = files;
|
||||
Variant v_index = [panel_delegate getIndex];
|
||||
Variant v_opt = [panel_delegate getSelection];
|
||||
Variant ret;
|
||||
Callable::CallError ce;
|
||||
const Variant *args[4] = { &v_result, &v_files, &v_index, &v_opt };
|
||||
void (^completion_handler)(NSInteger ret) = ^(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_valid()) {
|
||||
if (p_options_in_cb) {
|
||||
Variant v_result = true;
|
||||
Variant v_files = files;
|
||||
Variant v_index = [panel_delegate getIndex];
|
||||
Variant v_opt = [panel_delegate getSelection];
|
||||
Variant ret;
|
||||
Callable::CallError ce;
|
||||
const Variant *args[4] = { &v_result, &v_files, &v_index, &v_opt };
|
||||
|
||||
callback.callp(args, 4, ret, ce);
|
||||
if (ce.error != Callable::CallError::CALL_OK) {
|
||||
ERR_PRINT(vformat("Failed to execute file dialog callback: %s.", Variant::get_callable_error_text(callback, args, 4, ce)));
|
||||
}
|
||||
} else {
|
||||
Variant v_result = true;
|
||||
Variant v_files = files;
|
||||
Variant v_index = [panel_delegate getIndex];
|
||||
Variant ret;
|
||||
Callable::CallError ce;
|
||||
const Variant *args[3] = { &v_result, &v_files, &v_index };
|
||||
callback.callp(args, 4, ret, ce);
|
||||
if (ce.error != Callable::CallError::CALL_OK) {
|
||||
ERR_PRINT(vformat("Failed to execute file dialog callback: %s.", Variant::get_callable_error_text(callback, args, 4, ce)));
|
||||
}
|
||||
} else {
|
||||
Variant v_result = true;
|
||||
Variant v_files = files;
|
||||
Variant v_index = [panel_delegate getIndex];
|
||||
Variant ret;
|
||||
Callable::CallError ce;
|
||||
const Variant *args[3] = { &v_result, &v_files, &v_index };
|
||||
|
||||
callback.callp(args, 3, ret, ce);
|
||||
if (ce.error != Callable::CallError::CALL_OK) {
|
||||
ERR_PRINT(vformat("Failed to execute file dialog callback: %s.", Variant::get_callable_error_text(callback, args, 3, ce)));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (callback.is_valid()) {
|
||||
if (p_options_in_cb) {
|
||||
Variant v_result = false;
|
||||
Variant v_files = Vector<String>();
|
||||
Variant v_index = [panel_delegate getIndex];
|
||||
Variant v_opt = [panel_delegate getSelection];
|
||||
Variant ret;
|
||||
Callable::CallError ce;
|
||||
const Variant *args[4] = { &v_result, &v_files, &v_index, &v_opt };
|
||||
callback.callp(args, 3, ret, ce);
|
||||
if (ce.error != Callable::CallError::CALL_OK) {
|
||||
ERR_PRINT(vformat("Failed to execute file dialog callback: %s.", Variant::get_callable_error_text(callback, args, 3, ce)));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (callback.is_valid()) {
|
||||
if (p_options_in_cb) {
|
||||
Variant v_result = false;
|
||||
Variant v_files = Vector<String>();
|
||||
Variant v_index = [panel_delegate getIndex];
|
||||
Variant v_opt = [panel_delegate getSelection];
|
||||
Variant ret;
|
||||
Callable::CallError ce;
|
||||
const Variant *args[4] = { &v_result, &v_files, &v_index, &v_opt };
|
||||
|
||||
callback.callp(args, 4, ret, ce);
|
||||
if (ce.error != Callable::CallError::CALL_OK) {
|
||||
ERR_PRINT(vformat("Failed to execute file dialog callback: %s.", Variant::get_callable_error_text(callback, args, 4, ce)));
|
||||
}
|
||||
} else {
|
||||
Variant v_result = false;
|
||||
Variant v_files = Vector<String>();
|
||||
Variant v_index = [panel_delegate getIndex];
|
||||
Variant ret;
|
||||
Callable::CallError ce;
|
||||
const Variant *args[3] = { &v_result, &v_files, &v_index };
|
||||
callback.callp(args, 4, ret, ce);
|
||||
if (ce.error != Callable::CallError::CALL_OK) {
|
||||
ERR_PRINT(vformat("Failed to execute file dialog callback: %s.", Variant::get_callable_error_text(callback, args, 4, ce)));
|
||||
}
|
||||
} else {
|
||||
Variant v_result = false;
|
||||
Variant v_files = Vector<String>();
|
||||
Variant v_index = [panel_delegate getIndex];
|
||||
Variant ret;
|
||||
Callable::CallError ce;
|
||||
const Variant *args[3] = { &v_result, &v_files, &v_index };
|
||||
|
||||
callback.callp(args, 3, ret, ce);
|
||||
if (ce.error != Callable::CallError::CALL_OK) {
|
||||
ERR_PRINT(vformat("Failed to execute file dialog callback: %s.", Variant::get_callable_error_text(callback, args, 3, ce)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (prev_focus != INVALID_WINDOW_ID) {
|
||||
callable_mp(DisplayServer::get_singleton(), &DisplayServer::window_move_to_foreground).call_deferred(prev_focus);
|
||||
}
|
||||
}];
|
||||
callback.callp(args, 3, ret, ce);
|
||||
if (ce.error != Callable::CallError::CALL_OK) {
|
||||
ERR_PRINT(vformat("Failed to execute file dialog callback: %s.", Variant::get_callable_error_text(callback, args, 3, ce)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (p_window_id != INVALID_WINDOW_ID) {
|
||||
callable_mp(DisplayServer::get_singleton(), &DisplayServer::window_move_to_foreground).call_deferred(p_window_id);
|
||||
}
|
||||
};
|
||||
if (nswindow) {
|
||||
[panel beginSheetModalForWindow:nswindow completionHandler:completion_handler];
|
||||
} else {
|
||||
[panel beginWithCompletionHandler:completion_handler];
|
||||
}
|
||||
}
|
||||
|
||||
return OK;
|
||||
|
||||
Reference in New Issue
Block a user