diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 3aa8d5fceee92..d3d3f28c6c2fa 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -204,6 +204,7 @@ add_sdl_test_executable(testcustomcursor testcustomcursor.c) add_sdl_test_executable(gamepadmap NEEDS_RESOURCES TESTUTILS gamepadmap.c) add_sdl_test_executable(testvulkan testvulkan.c) add_sdl_test_executable(testoffscreen testoffscreen.c) +add_sdl_test_executable(testpopup testpopup.c) check_c_compiler_flag(-Wformat-overflow HAVE_WFORMAT_OVERFLOW) if(HAVE_WFORMAT_OVERFLOW) diff --git a/test/testpopup.c b/test/testpopup.c new file mode 100644 index 0000000000000..4e5f80e6b5114 --- /dev/null +++ b/test/testpopup.c @@ -0,0 +1,248 @@ +/* +Copyright (C) 1997-2023 Sam Lantinga + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely. +*/ +/* Simple program: Move N sprites around on the screen as fast as possible */ + +#include +#include + +#ifdef __EMSCRIPTEN__ +#include +#endif + +#include +#include +#include + +#define MENU_WIDTH 120 +#define MENU_HEIGHT 300 + +#define TOOLTIP_DELAY 500 +#define TOOLTIP_WIDTH 175 +#define TOOLTIP_HEIGHT 32 + +static SDLTest_CommonState *state; +static int num_menus; +static Uint64 tooltip_timer; +static int done; +static const SDL_Color colors[] = { + { 0x7F, 0x00, 0x00, 0xFF }, + { 0x00, 0x7F, 0x00, 0xFF }, + { 0x00, 0x00, 0x7F, 0xFF } +}; + +struct PopupWindow +{ + SDL_Window *win; + SDL_Window *parent; + SDL_Renderer *renderer; + int idx; +}; + +struct PopupWindow *menus; +struct PopupWindow tooltip; + +/* Call this instead of exit(), so we can clean up SDL: atexit() is evil. */ +static void quit(int rc) +{ + SDL_free(menus); + menus = NULL; + + SDLTest_CommonQuit(state); + exit(rc); +} + +static int get_menu_index_by_window(SDL_Window *window) +{ + int i; + for (i = 0; i < num_menus; ++i) { + if (menus[i].win == window) { + return i; + } + } + + return -1; +} + +static SDL_bool window_is_root(SDL_Window *window) +{ + int i; + for (i = 0; i < state->num_windows; ++i) { + if (window == state->windows[i]) { + return SDL_TRUE; + } + } + + return SDL_FALSE; +} + +static SDL_bool create_popup(struct PopupWindow *new_popup, SDL_bool is_menu) +{ + SDL_Window *focus; + SDL_Window *new_win; + SDL_Renderer *new_renderer; + const int w = is_menu ? MENU_WIDTH : TOOLTIP_WIDTH; + const int h = is_menu ? MENU_HEIGHT : TOOLTIP_HEIGHT; + const int v_off = is_menu ? 0 : 32; + const Uint32 flags = is_menu ? SDL_WINDOW_POPUP_MENU : SDL_WINDOW_TOOLTIP; + float x, y; + + focus = SDL_GetMouseFocus(); + + SDL_GetMouseState(&x, &y); + new_win = SDL_CreatePopupWindow(focus, + (int)x, (int)y + v_off, w, h, flags); + + if (new_win) { + new_renderer = SDL_CreateRenderer(new_win, state->renderdriver, state->render_flags); + + new_popup->win = new_win; + new_popup->renderer = new_renderer; + new_popup->parent = focus; + + return SDL_TRUE; + } + + SDL_zerop(new_popup); + return SDL_FALSE; +} + +static void close_popups() +{ + int i; + + for (i = 0; i < num_menus; ++i) { + /* Destroying the lowest level window recursively destroys the child windows */ + if (window_is_root(menus[i].parent)) { + SDL_DestroyWindow(menus[i].win); + } + } + SDL_free(menus); + menus = NULL; + num_menus = 0; + + /* If the tooltip was a child of a popup, it was recursively destroyed with the popup */ + if (!window_is_root(tooltip.parent)) { + SDL_zero(tooltip); + } +} + +static void loop() +{ + int i; + char fmt_str[128]; + SDL_Event event; + + /* Check for events */ + while (SDL_PollEvent(&event)) { + SDLTest_CommonEvent(state, &event, &done); + + if (event.type == SDL_EVENT_MOUSE_MOTION) { + /* Hide the tooltip and restart the timer if the mouse is moved */ + if (tooltip.win) { + SDL_DestroyWindow(tooltip.win); + SDL_zero(tooltip); + } + tooltip_timer = SDL_GetTicks() + TOOLTIP_DELAY; + } else if (event.type == SDL_EVENT_MOUSE_BUTTON_DOWN) { + /* Left click closes the popup menus */ + if (event.button.button == SDL_BUTTON_LEFT) { + close_popups(); + } else if (event.button.button == SDL_BUTTON_RIGHT) { + /* Create a new popup menu */ + menus = SDL_realloc(menus, sizeof(struct PopupWindow) * (num_menus + 1)); + if (create_popup(&menus[num_menus], SDL_TRUE)) { + ++num_menus; + } + } + } + } + + /* Show the tooltip if the delay period has elapsed */ + if (SDL_GetTicks() > tooltip_timer) { + if (tooltip.win == NULL) { + create_popup(&tooltip, SDL_FALSE); + } + } + + /* Draw the window */ + for (i = 0; i < state->num_windows; ++i) { + SDL_Renderer *renderer = state->renderers[i]; + SDL_RenderClear(renderer); + SDL_RenderPresent(renderer); + } + + /* Draw the menus in alternating colors */ + for (i = 0; i < num_menus; ++i) { + const SDL_Color *c = &colors[i % SDL_arraysize(colors)]; + SDL_Renderer *renderer = menus[i].renderer; + + SDL_SetRenderDrawColor(renderer, c->r, c->g, c->b, c->a); + SDL_RenderClear(renderer); + SDL_SetRenderDrawColor(renderer, 0xFF, 0xFF, 0xFF, 0xFF); + SDL_snprintf(fmt_str, sizeof(fmt_str), "Popup Menu %i", i); + SDLTest_DrawString(renderer, 10.0f, 10.0f, fmt_str); + SDL_RenderPresent(renderer); + } + + /* Draw the tooltip */ + if (tooltip.win) { + int menu_idx; + + SDL_SetRenderDrawColor(tooltip.renderer, 0x00, 0x00, 0x00, 0xFF); + SDL_RenderClear(tooltip.renderer); + SDL_SetRenderDrawColor(tooltip.renderer, 0xFF, 0xFF, 0xFF, 0xFF); + + menu_idx = get_menu_index_by_window(tooltip.parent); + if (menu_idx >= 0) { + SDL_snprintf(fmt_str, sizeof(fmt_str), "Tooltip for popup %i", menu_idx); + } else { + SDL_snprintf(fmt_str, sizeof(fmt_str), "Toplevel tooltip"); + } + SDLTest_DrawString(tooltip.renderer, 10.0f, TOOLTIP_HEIGHT / 2, fmt_str); + SDL_RenderPresent(tooltip.renderer); + } +} + +int main(int argc, char *argv[]) +{ + int i; + + /* Initialize test framework */ + state = SDLTest_CommonCreateState(argv, SDL_INIT_VIDEO); + if (state == NULL) { + return 1; + } + + if (!SDLTest_CommonInit(state)) { + SDLTest_CommonQuit(state); + quit(2); + } + + for (i = 0; i < state->num_windows; ++i) { + SDL_Renderer *renderer = state->renderers[i]; + SDL_SetRenderDrawColor(renderer, 0xA0, 0xA0, 0xA0, 0xFF); + SDL_RenderClear(renderer); + } + + /* Main render loop */ + done = 0; +#ifdef __EMSCRIPTEN__ + emscripten_set_main_loop(loop, 0, 1); +#else + while (!done) { + loop(); + } +#endif + quit(0); + /* keep the compiler happy ... */ + return 0; +} \ No newline at end of file