diff --git a/src/EnvRenderer.c b/src/EnvRenderer.c index 6d8e00fc4..2b4e972f0 100644 --- a/src/EnvRenderer.c +++ b/src/EnvRenderer.c @@ -21,7 +21,6 @@ cc_bool EnvRenderer_Legacy, EnvRenderer_Minimal; -#define ENV_SMALL_VERTICES 4096 static float CalcBlendFactor(float x) { /* return -0.05 + 0.22 * (Math_Log(x) * 0.25f); */ double blend = -0.13 + 0.28 * (Math_Log(x) * 0.25); diff --git a/src/Menus.c b/src/Menus.c index a8292e658..512668fff 100644 --- a/src/Menus.c +++ b/src/Menus.c @@ -201,16 +201,18 @@ static struct Widget* list_widgets[10] = { static void ListScreen_Layout(void* screen) { struct ListScreen* s = (struct ListScreen*)screen; int i; + for (i = 0; i < LIST_SCREEN_ITEMS; i++) { Widget_SetLocation(&s->btns[i], ANCHOR_CENTRE, ANCHOR_CENTRE, 0, (i - 2) * 50); } - if (s->UploadClick) { + if (s->UploadClick && Input_TouchMode) { Widget_SetLocation(&s->done, ANCHOR_CENTRE_MIN, ANCHOR_MAX, -150, 25); Widget_SetLocation(&s->upload, ANCHOR_CENTRE_MAX, ANCHOR_MAX, -150, 25); } else { - Menu_LayoutBack(&s->done); + Widget_SetLocation(&s->done, ANCHOR_CENTRE, ANCHOR_MAX, 0, 25); + Widget_SetLocation(&s->upload, ANCHOR_CENTRE, ANCHOR_MAX, 0, 70); } Widget_SetLocation(&s->left, ANCHOR_CENTRE, ANCHOR_CENTRE, -220, 0); @@ -384,7 +386,11 @@ static void ListScreen_ContextRecreated(void* screen) { ListScreen_UpdatePage(s); if (!s->UploadClick) return; +#ifdef CC_BUILD_WEB ButtonWidget_SetConst(&s->upload, "Upload", &s->font); +#else + ButtonWidget_SetConst(&s->upload, "Load file...", &s->font); +#endif } static void ListScreen_Reload(struct ListScreen* s) { @@ -1587,7 +1593,8 @@ static void TexturePackScreen_UploadCallback(const cc_string* path) { } static void TexturePackScreen_UploadFunc(void* s, void* w) { - Window_OpenFileDialog(".zip", TexturePackScreen_UploadCallback); + static const char* const filters[] = { ".zip", NULL }; + Window_OpenFileDialog(filters, TexturePackScreen_UploadCallback); } #else #define TexturePackScreen_UploadFunc NULL @@ -1764,14 +1771,14 @@ static void LoadLevelScreen_LoadEntries(struct ListScreen* s) { StringsBuffer_Sort(&s->entries); } -#ifdef CC_BUILD_WEB static void LoadLevelScreen_UploadCallback(const cc_string* path) { Map_LoadFrom(path); } static void LoadLevelScreen_UploadFunc(void* s, void* w) { - Window_OpenFileDialog(".cw", LoadLevelScreen_UploadCallback); + static const char* const filters[] = { + ".cw", ".dat", ".lvl", ".mine", ".fcm", NULL + }; + cc_result res = Window_OpenFileDialog(filters, LoadLevelScreen_UploadCallback); + if (res) Logger_SimpleWarn(res, "showing open file dialog"); } -#else -#define LoadLevelScreen_UploadFunc NULL -#endif void LoadLevelScreen_Show(void) { struct ListScreen* s = &ListScreen; diff --git a/src/Window.h b/src/Window.h index c2a26c90a..8334c0f74 100644 --- a/src/Window.h +++ b/src/Window.h @@ -133,7 +133,7 @@ void Cursor_SetVisible(cc_bool visible); CC_API void Window_ShowDialog(const char* title, const char* msg); typedef void (*OpenFileDialogCallback)(const cc_string* path); /* Shows an 'load file' dialog window. */ -cc_result Window_OpenFileDialog(const char* filter, OpenFileDialogCallback callback); +cc_result Window_OpenFileDialog(const char* const* filters, OpenFileDialogCallback callback); /* 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 04b21f3d9..db17cc176 100644 --- a/src/Window_Android.c +++ b/src/Window_Android.c @@ -363,6 +363,10 @@ 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) { + return ERR_NOT_SUPPORTED; +} + static struct Bitmap fb_bmp; void Window_AllocFramebuffer(struct Bitmap* bmp) { bmp->scan0 = (BitmapCol*)Mem_Alloc(bmp->width * bmp->height, 4, "window pixels"); diff --git a/src/Window_Carbon.c b/src/Window_Carbon.c index 2f1dbc439..af81cceee 100644 --- a/src/Window_Carbon.c +++ b/src/Window_Carbon.c @@ -607,6 +607,10 @@ static void ShowDialogCore(const char* title, const char* msg) { showingDialog = false; } +cc_result Window_OpenFileDialog(const char* const* filters, OpenFileDialogCallback callback) { + return ERR_NOT_SUPPORTED; +} + static CGrafPtr fb_port; static struct Bitmap fb_bmp; static CGColorSpaceRef colorSpace; diff --git a/src/Window_SDL.c b/src/Window_SDL.c index 720415ee8..3132c1275 100644 --- a/src/Window_SDL.c +++ b/src/Window_SDL.c @@ -272,6 +272,10 @@ 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) { + return ERR_NOT_SUPPORTED; +} + static SDL_Surface* surface; void Window_AllocFramebuffer(struct Bitmap* bmp) { surface = SDL_GetWindowSurface(win_handle); diff --git a/src/Window_Web.c b/src/Window_Web.c index 571268927..fbe3d01cc 100644 --- a/src/Window_Web.c +++ b/src/Window_Web.c @@ -545,11 +545,24 @@ EMSCRIPTEN_KEEPALIVE void Window_OnFileUploaded(const char* src) { } extern void interop_OpenFileDialog(const char* filter); -cc_result Window_OpenFileDialog(const char* filter, OpenFileDialogCallback callback) { +cc_result Window_OpenFileDialog(const char* const* filters, OpenFileDialogCallback callback) { + cc_string path; char pathBuffer[NATIVE_STR_LEN]; + char filter[NATIVE_STR_LEN]; + int i; + + /* Filter tokens are , separated - e.g. ".cw,.dat */ + String_InitArray(path, pathBuffer); + for (i = 0; filters[i]; i++) + { + if (i) String_Append(&path, ','); + String_AppendConst(&path, filters[i]); + } + Platform_EncodeUtf8(filter, &path); + uploadCallback = callback; /* Calls Window_OnFileUploaded on success */ interop_OpenFileDialog(filter); - return ERR_NOT_SUPPORTED; + return 0; } void Window_AllocFramebuffer(struct Bitmap* bmp) { } diff --git a/src/Window_Win.c b/src/Window_Win.c index b4c53b4a8..5ee81cd20 100644 --- a/src/Window_Win.c +++ b/src/Window_Win.c @@ -22,6 +22,7 @@ /* Hence the actual minimum supported OS is Windows 2000. This just avoids redeclaring structs. */ #endif #include +#include /* https://docs.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-setpixelformat */ #define CC_WIN_STYLE WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN @@ -136,7 +137,8 @@ static LRESULT CALLBACK Window_Procedure(HWND handle, UINT message, WPARAM wPara case WM_MOUSEMOVE: /* Set before position change, in case mouse buttons changed when outside window */ - Input_SetNonRepeatable(KEY_LMOUSE, wParam & 0x01); + if (!(wParam & 0x01)) Input_SetReleased(KEY_LMOUSE); + //Input_SetNonRepeatable(KEY_LMOUSE, wParam & 0x01); Input_SetNonRepeatable(KEY_RMOUSE, wParam & 0x02); Input_SetNonRepeatable(KEY_MMOUSE, wParam & 0x10); /* TODO: do we need to set XBUTTON1/XBUTTON2 here */ @@ -535,6 +537,51 @@ 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_string path; char pathBuffer[NATIVE_STR_LEN]; + WCHAR str[MAX_PATH] = { 0 }; + OPENFILENAMEW ofn = { 0 }; + WCHAR filter[MAX_PATH]; + int i; + + /* Filter tokens are \0 separated - e.g. "Maps (*.cw;*.dat)\0*.cw;*.dat\0 */ + String_InitArray(path, pathBuffer); + String_AppendConst(&path, "All supported files ("); + for (i = 0; filters[i]; i++) + { + if (i) String_Append(&path, ';'); + String_Format1(&path, "*%c", filters[i]); + } + String_Append(&path, ')'); + String_Append(&path, '\0'); + + for (i = 0; filters[i]; i++) + { + if (i) String_Append(&path, ';'); + String_Format1(&path, "*%c", filters[i]); + } + String_Append(&path, '\0'); + Platform_EncodeUtf16(filter, &path); + + ofn.lStructSize = sizeof(ofn); + ofn.hwndOwner = win_handle; + ofn.lpstrFile = str; + ofn.nMaxFile = MAX_PATH; + ofn.lpstrFilter = filter; + ofn.nFilterIndex = 1; + ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR; + + if (!GetOpenFileNameW(&ofn)) + return CommDlgExtendedError(); + String_InitArray(path, pathBuffer); + + for (i = 0; i < MAX_PATH && str[i]; i++) { + String_Append(&path, Convert_CodepointToCP437(str[i])); + } + callback(&path); + return 0; +} + static HDC draw_DC; static HBITMAP draw_DIB; void Window_AllocFramebuffer(struct Bitmap* bmp) { diff --git a/src/Window_X11.c b/src/Window_X11.c index 8e694d9e8..50b6963cc 100644 --- a/src/Window_X11.c +++ b/src/Window_X11.c @@ -10,6 +10,7 @@ #include #include #include +#include #ifdef X_HAVE_UTF8_STRING #define CC_BUILD_XIM @@ -966,6 +967,46 @@ 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_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 ("); + + for (i = 0; filters[i]; i++) + { + if (i) String_Append(&path, ','); + String_Format1(&path, "*%c", filters[i]); + } + String_AppendConst(&path, ") |"); + + for (i = 0; filters[i]; i++) + { + String_Format1(&path, " *%c", filters[i]); + } + String_AppendConst(&path, "'"); + path.buffer[path.length] = '\0'; + + /* TODO this doesn't detect when Zenity doesn't exist */ + fp = popen(path.buffer, "r"); + if (!fp) return 0; + + /* result is normally just one string */ + while (fgets(result, sizeof(result), fp)) { } + len = String_Length(result); + + if (len) { + String_InitArray(path, pathBuffer); + String_AppendUtf8(&path, result, len); + callback(&path); + } + pclose(fp); + return 0; +} + static GC fb_gc; static XImage* fb_image; static struct Bitmap fb_bmp; diff --git a/src/interop_cocoa.m b/src/interop_cocoa.m index 1895c9594..cf340d4e2 100644 --- a/src/interop_cocoa.m +++ b/src/interop_cocoa.m @@ -114,7 +114,7 @@ void Clipboard_SetText(const cc_string* value) { NSString* str; Platform_EncodeUtf8(raw, value); - str = [[NSString alloc] initWithUTF8String:raw]; + str = [NSString stringWithUTF8String:raw]; pasteboard = [NSPasteboard generalPasteboard]; [pasteboard declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil]; @@ -299,7 +299,7 @@ void Window_SetTitle(const cc_string* title) { NSString* str; Platform_EncodeUtf8(raw, title); - str = [[NSString alloc] initWithUTF8String:raw]; + str = [NSString stringWithUTF8String:raw]; [winHandle setTitle:str]; [str release]; } @@ -507,6 +507,39 @@ void ShowDialogCore(const char* title, const char* msg) { CFRelease(msgCF); } +cc_result Window_OpenFileDialog(const char* const* filters, OpenFileDialogCallback callback) { + NSOpenPanel* dlg = [NSOpenPanel openPanel]; + NSArray* files; + NSString* str; + const char* src; + int len, i; + + NSMutableArray* types = [NSMutableArray array]; + for (i = 0; filters[i]; i++) + { + NSString* filter = [NSString stringWithUTF8String:filters[i]]; + filter = [filter substringFromIndex:1]; + [types addObject:filter]; + } + + [dlg setCanChooseFiles: YES]; + [dlg setAllowedFileTypes:types]; + if ([dlg runModal] != NSOKButton) return 0; + + files = [dlg URLs]; + if ([files count] < 1) return 0; + + str = [[files objectAtIndex:0] path]; + src = [str UTF8String]; + len = String_Length(src); + + cc_string path; char pathBuffer[NATIVE_STR_LEN]; + String_InitArray(path, pathBuffer); + String_AppendUtf8(&path, src, len); + callback(&path); + return 0; +} + static struct Bitmap fb_bmp; void Window_AllocFramebuffer(struct Bitmap* bmp) { bmp->scan0 = (BitmapCol*)Mem_Alloc(bmp->width * bmp->height, 4, "window pixels"); diff --git a/src/interop_ios.m b/src/interop_ios.m index 4646c6351..f14cea482 100644 --- a/src/interop_ios.m +++ b/src/interop_ios.m @@ -260,6 +260,10 @@ void Window_LockLandscapeOrientation(cc_bool lock) { [UIViewController attemptRotationToDeviceOrientation]; } +cc_result Window_OpenFileDialog(const char* const* filters, OpenFileDialogCallback callback) { + return ERR_NOT_SUPPORTED; +} + /*#########################################################################################################################* *--------------------------------------------------------2D window--------------------------------------------------------*