From 5d8f9bf6da8ee67249b7993b47d3a99615d5800f Mon Sep 17 00:00:00 2001 From: UnknownShadow200 Date: Fri, 14 Oct 2022 22:03:51 +1100 Subject: [PATCH] Improve open file dialog API Allows filetype description on Windows/Linux Avoids Menus.c needing to call internal webclient interop_SaveNode function For texture packs in webclient: Avoid saving into temp file, then reading into a copy in memory, then writing the copy into texpacks folder. Now just saves directly into texpacks folder --- src/Menus.c | 23 ++++++++++++++++------- src/Window.h | 15 +++++++++++++-- src/Window_Android.c | 2 +- src/Window_Carbon.c | 2 +- src/Window_Haiku.cpp | 6 +++--- src/Window_SDL.c | 2 +- src/Window_Web.c | 20 ++++++++++---------- src/Window_Win.c | 7 ++++--- src/Window_X11.c | 7 ++++--- src/interop_cocoa.m | 5 +++-- src/interop_ios.m | 4 ++-- src/interop_web.js | 21 +++++++++------------ 12 files changed, 67 insertions(+), 47 deletions(-) diff --git a/src/Menus.c b/src/Menus.c index 1e456f1e4..f83d36a44 100644 --- a/src/Menus.c +++ b/src/Menus.c @@ -1572,15 +1572,11 @@ static void TexturePackScreen_LoadEntries(struct ListScreen* s) { StringsBuffer_Sort(&s->entries); } -extern void interop_UploadTexPack(const char* path); static void TexturePackScreen_UploadCallback(const cc_string* path) { #ifdef CC_BUILD_WEB - char str[NATIVE_STR_LEN]; cc_string relPath = *path; - Platform_EncodeUtf8(str, path); Utils_UNSAFE_GetFilename(&relPath); - interop_UploadTexPack(str); ListScreen_Reload(&ListScreen); TexturePack_SetDefault(&relPath); #else @@ -1590,8 +1586,15 @@ static void TexturePackScreen_UploadCallback(const cc_string* path) { } static void TexturePackScreen_UploadFunc(void* s, void* w) { - static const char* const filters[] = { ".zip", NULL }; - Window_OpenFileDialog(filters, TexturePackScreen_UploadCallback); + static const char* const filters[] = { + ".zip", NULL + }; + static struct OpenFileDialogArgs args = { + "Texture packs", filters, + TexturePackScreen_UploadCallback, + OFD_UPLOAD_PERSIST, "texpacks" + }; + Window_OpenFileDialog(&args); } void TexturePackScreen_Show(void) { @@ -1770,7 +1773,13 @@ static void LoadLevelScreen_UploadFunc(void* s, void* w) { static const char* const filters[] = { ".cw", ".dat", ".lvl", ".mine", ".fcm", NULL }; - cc_result res = Window_OpenFileDialog(filters, LoadLevelScreen_UploadCallback); + static struct OpenFileDialogArgs args = { + "Classic map files", filters, + LoadLevelScreen_UploadCallback, + OFD_UPLOAD_DELETE, "tmp" + }; + + cc_result res = Window_OpenFileDialog(&args); if (res) Logger_SimpleWarn(res, "showing open file dialog"); } diff --git a/src/Window.h b/src/Window.h index c87ba40b4..6d1d271d4 100644 --- a/src/Window.h +++ b/src/Window.h @@ -128,9 +128,20 @@ void Window_ProcessEvents(void); void Cursor_SetPosition(int x, int y); /* Shows a dialog box window. */ CC_API void Window_ShowDialog(const char* title, const char* msg); + +#define OFD_UPLOAD_DELETE 0 /* (webclient) Deletes the uploaded file after invoking callback function */ +#define OFD_UPLOAD_PERSIST 1 /* (webclient) Saves the uploded file into IndexedDB */ typedef void (*OpenFileDialogCallback)(const cc_string* path); -/* Shows an 'load file' dialog window. */ -cc_result Window_OpenFileDialog(const char* const* filters, OpenFileDialogCallback callback); + +struct OpenFileDialogArgs { + const char* description; /* Describes the types of files supported (e.g. "Texture packs") */ + const char* const* filters; + OpenFileDialogCallback Callback; + int uploadAction; /* Action webclient takes after invoking callback function */ + const char* uploadFolder; /* For webclient, folder to upload the file to */ +}; +/* Shows an 'load file' dialog window */ +cc_result Window_OpenFileDialog(const struct OpenFileDialogArgs* args); /* Allocates a framebuffer that can be drawn/transferred to the window. */ /* NOTE: Do NOT free bmp->Scan0, use Window_FreeFramebuffer. */ diff --git a/src/Window_Android.c b/src/Window_Android.c index 8eed32995..3c554e45d 100644 --- a/src/Window_Android.c +++ b/src/Window_Android.c @@ -359,7 +359,7 @@ static void ShowDialogCore(const char* title, const char* msg) { (*env)->DeleteLocalRef(env, args[1].l); } -cc_result Window_OpenFileDialog(const char* const* filters, OpenFileDialogCallback callback) { +cc_result Window_OpenFileDialog(const struct OpenFileDialogArgs* args) { return ERR_NOT_SUPPORTED; } diff --git a/src/Window_Carbon.c b/src/Window_Carbon.c index d81667500..34466aeb8 100644 --- a/src/Window_Carbon.c +++ b/src/Window_Carbon.c @@ -591,7 +591,7 @@ static void ShowDialogCore(const char* title, const char* msg) { showingDialog = false; } -cc_result Window_OpenFileDialog(const char* const* filters, OpenFileDialogCallback callback) { +cc_result Window_OpenFileDialog(const struct OpenFileDialogArgs* args) { return ERR_NOT_SUPPORTED; } diff --git a/src/Window_Haiku.cpp b/src/Window_Haiku.cpp index 3a5a1b8c3..76a7b9f0c 100644 --- a/src/Window_Haiku.cpp +++ b/src/Window_Haiku.cpp @@ -526,7 +526,7 @@ static void CallOpenFileCallback(const char* rawPath) { open_callback = NULL; } -cc_result Window_OpenFileDialog(const char* const* filters, OpenFileDialogCallback callback) { +cc_result Window_OpenFileDialog(const struct OpenFileDialogArgs* args) { if (!open_panel) { open_panel = new BFilePanel(B_OPEN_PANEL); open_panel->SetRefFilter(new CC_BRefFilter()); @@ -534,8 +534,8 @@ cc_result Window_OpenFileDialog(const char* const* filters, OpenFileDialogCallba // so this is technically a memory leak.. but meh } - open_callback = callback; - open_filters = filters; + open_callback = args->Callback; + open_filters = args->filters; open_panel->Show(); return 0; } diff --git a/src/Window_SDL.c b/src/Window_SDL.c index 1225325e8..377fa59d3 100644 --- a/src/Window_SDL.c +++ b/src/Window_SDL.c @@ -277,7 +277,7 @@ static void ShowDialogCore(const char* title, const char* msg) { SDL_ShowSimpleMessageBox(0, title, msg, win_handle); } -cc_result Window_OpenFileDialog(const char* const* filters, OpenFileDialogCallback callback) { +cc_result Window_OpenFileDialog(const struct OpenFileDialogArgs* args) { return ERR_NOT_SUPPORTED; } diff --git a/src/Window_Web.c b/src/Window_Web.c index 2abfb24ab..0cb83aff5 100644 --- a/src/Window_Web.c +++ b/src/Window_Web.c @@ -544,24 +544,24 @@ EMSCRIPTEN_KEEPALIVE void Window_OnFileUploaded(const char* src) { uploadCallback = NULL; } -extern void interop_OpenFileDialog(const char* filter); -cc_result Window_OpenFileDialog(const char* const* filters, OpenFileDialogCallback callback) { - cc_string path; char pathBuffer[NATIVE_STR_LEN]; - char filter[NATIVE_STR_LEN]; +extern void interop_OpenFileDialog(const char* filter, int action, const char* folder); +cc_result Window_OpenFileDialog(const struct OpenFileDialogArgs* args) { + const char* const* filters = args->filters; + cc_string filter; char filterBuffer[1024]; int i; /* Filter tokens are , separated - e.g. ".cw,.dat */ - String_InitArray(path, pathBuffer); + String_InitArray_NT(filter, filterBuffer); for (i = 0; filters[i]; i++) { - if (i) String_Append(&path, ','); - String_AppendConst(&path, filters[i]); + if (i) String_Append(&filter, ','); + String_AppendConst(&filter, filters[i]); } - Platform_EncodeUtf8(filter, &path); + filter.buffer[filter.length] = '\0'; - uploadCallback = callback; + uploadCallback = args->Callback; /* Calls Window_OnFileUploaded on success */ - interop_OpenFileDialog(filter); + interop_OpenFileDialog(filter.buffer, args->uploadAction, args->uploadFolder); return 0; } diff --git a/src/Window_Win.c b/src/Window_Win.c index c8fa68a34..2e278db8a 100644 --- a/src/Window_Win.c +++ b/src/Window_Win.c @@ -551,7 +551,8 @@ static void ShowDialogCore(const char* title, const char* msg) { MessageBoxA(win_handle, msg, title, 0); } -cc_result Window_OpenFileDialog(const char* const* filters, OpenFileDialogCallback callback) { +cc_result Window_OpenFileDialog(const struct OpenFileDialogArgs* args) { + const char* const* filters = args->filters; cc_string path; char pathBuffer[NATIVE_STR_LEN]; WCHAR str[MAX_PATH] = { 0 }; OPENFILENAMEW ofn = { 0 }; @@ -560,7 +561,7 @@ cc_result Window_OpenFileDialog(const char* const* filters, OpenFileDialogCallba /* Filter tokens are \0 separated - e.g. "Maps (*.cw;*.dat)\0*.cw;*.dat\0 */ String_InitArray(path, pathBuffer); - String_AppendConst(&path, "All supported files ("); + String_Format1(&path, "%c (", args->description); for (i = 0; filters[i]; i++) { if (i) String_Append(&path, ';'); @@ -592,7 +593,7 @@ cc_result Window_OpenFileDialog(const char* const* filters, OpenFileDialogCallba for (i = 0; i < MAX_PATH && str[i]; i++) { String_Append(&path, Convert_CodepointToCP437(str[i])); } - callback(&path); + args->Callback(&path); return 0; } diff --git a/src/Window_X11.c b/src/Window_X11.c index d18287495..f1e1b2aa0 100644 --- a/src/Window_X11.c +++ b/src/Window_X11.c @@ -967,14 +967,15 @@ static void ShowDialogCore(const char* title, const char* msg) { XFlush(m.dpy); /* flush so window disappears immediately */ } -cc_result Window_OpenFileDialog(const char* const* filters, OpenFileDialogCallback callback) { +cc_result Window_OpenFileDialog(const struct OpenFileDialogArgs* args) { + const char* const* filters = args->filters; cc_string path; char pathBuffer[1024]; char result[4096] = { 0 }; int len, i; FILE* fp; String_InitArray_NT(path, pathBuffer); - String_AppendConst(&path, "zenity --file-selection --file-filter='All supported files ("); + String_Format1(&path, "zenity --file-selection --file-filter='%c (", args->description); for (i = 0; filters[i]; i++) { @@ -1001,7 +1002,7 @@ cc_result Window_OpenFileDialog(const char* const* filters, OpenFileDialogCallba if (len) { String_InitArray(path, pathBuffer); String_AppendUtf8(&path, result, len); - callback(&path); + args->Callback(&path); } pclose(fp); return 0; diff --git a/src/interop_cocoa.m b/src/interop_cocoa.m index 0868ab414..53ed2879a 100644 --- a/src/interop_cocoa.m +++ b/src/interop_cocoa.m @@ -515,7 +515,8 @@ void ShowDialogCore(const char* title, const char* msg) { CFRelease(msgCF); } -cc_result Window_OpenFileDialog(const char* const* filters, OpenFileDialogCallback callback) { +cc_result Window_OpenFileDialog(const struct OpenFileDialogArgs* args) { + const char* const* filters = args->filters; NSOpenPanel* dlg = [NSOpenPanel openPanel]; NSArray* files; NSString* str; @@ -547,7 +548,7 @@ cc_result Window_OpenFileDialog(const char* const* filters, OpenFileDialogCallba cc_string path; char pathBuffer[NATIVE_STR_LEN]; String_InitArray(path, pathBuffer); String_AppendUtf8(&path, src, len); - callback(&path); + args->Callback(&path); return 0; } diff --git a/src/interop_ios.m b/src/interop_ios.m index 797fd2bfb..dc6746fe7 100644 --- a/src/interop_ios.m +++ b/src/interop_ios.m @@ -478,8 +478,8 @@ void Window_LockLandscapeOrientation(cc_bool lock) { [UIViewController attemptRotationToDeviceOrientation]; } -cc_result Window_OpenFileDialog(const char* const* filters, OpenFileDialogCallback callback) { - NSArray* types = @[ @"public.png" ]; // TODO fill in +cc_result Window_OpenFileDialog(const struct OpenFileDialogArgs* args) { + NSArray* types = @[ @"public.png" ]; // TODO fill in with args->filters UIDocumentPickerViewController* dlg; dlg = [UIDocumentPickerViewController alloc]; diff --git a/src/interop_web.js b/src/interop_web.js index 8237ce7f2..e9bccd9ba 100644 --- a/src/interop_web.js +++ b/src/interop_web.js @@ -107,14 +107,6 @@ mergeInto(LibraryManager.library, { } }, interop_DownloadMap__deps: ['interop_SaveBlob'], - interop_UploadTexPack: function(path) { - // Move from temp into texpacks folder - // TODO: This is pretty awful and should be rewritten - var name = UTF8ToString(path); - var data = CCFS.readFile(name); - CCFS.writeFile('texpacks/' + name.substring(1), data); - _interop_SaveNode('texpacks/' + name.substring(1)); - }, //######################################################################################################################## @@ -835,8 +827,10 @@ mergeInto(LibraryManager.library, { window.cc_divElem = null; window.cc_inputElem = null; }, - interop_OpenFileDialog: function(filter) { + interop_OpenFileDialog: function(filter, action, folder) { var elem = window.cc_uploadElem; + var root = UTF8ToString(folder); + if (!elem) { elem = document.createElement('input'); elem.setAttribute('type', 'file'); @@ -852,9 +846,12 @@ mergeInto(LibraryManager.library, { reader.onload = function(e) { var data = new Uint8Array(e.target.result); - CCFS.writeFile('/' + name, data); - ccall('Window_OnFileUploaded', 'void', ['string'], ['/' + name]); - CCFS.unlink('/' + name); + var path = root + '/' + name; + CCFS.writeFile(path, data); + ccall('Window_OnFileUploaded', 'void', ['string'], [path]); + + if (action == 0) CCFS.unlink(path); // OFD_UPLOAD_DELETE + if (action == 1) _interop_SaveNode(path); // OFD_UPLOAD_PERSIST }; reader.readAsArrayBuffer(files[i]); }