mirror of
https://github.com/libsdl-org/SDL.git
synced 2024-11-27 05:43:29 +08:00
cfeb663c87
Right click to create new popup menus. Clicking over an existing popup menu will create a child menu. Left click to close all menus. Hover to display a tooltip that is the child of the window below it. Closing the application closes the root window, which should automatically destroy any open popup windows and exit gracefully.
248 lines
6.5 KiB
C
248 lines
6.5 KiB
C
/*
|
|
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
|
|
|
|
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 <stdlib.h>
|
|
#include <time.h>
|
|
|
|
#ifdef __EMSCRIPTEN__
|
|
#include <emscripten/emscripten.h>
|
|
#endif
|
|
|
|
#include <SDL3/SDL_main.h>
|
|
#include <SDL3/SDL_test_common.h>
|
|
#include <SDL3/SDL_test_font.h>
|
|
|
|
#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;
|
|
} |