#include #include #include #include "panic.h" #include "font.h" #include "main.h" #include "menu.h" #define PADDING 4 static int option_at(menu *m, int x, int y) { float cur_y = PADDING; for (int i = 0; i < m->count; i++) { float tw, th; must(SDL_GetTextureSize(m->textures[i], &tw, &th)); cur_y += th; if (y < cur_y) return i; } return -1; } menu *create_menu(menu_entry *entries, int count, int x, int y) { menu *m = calloc(1, sizeof(menu)); if (!m) panic("out of memory"); x += 2; y -= 8; m->window = must(SDL_CreatePopupWindow(window, x, y, 0, 0, SDL_WINDOW_POPUP_MENU | SDL_WINDOW_BORDERLESS )); m->renderer = must(SDL_CreateRenderer(m->window, NULL)); m->entries = entries; m->count = count; m->textures = calloc(count, sizeof(SDL_Texture *)); if (!m->textures) panic("out of memory"); int w = PADDING, h = PADDING; for (int i = 0; i < count; i++) { SDL_Color color = {0xff, 0xff, 0xff, 0xff}; SDL_Surface *text = must(TTF_RenderText_Blended(font, m->entries[i].name, 0, color)); m->textures[i] = must(SDL_CreateTextureFromSurface(m->renderer, text)); SDL_DestroySurface(text); float tw, th; must(SDL_GetTextureSize(m->textures[i], &tw, &th)); w = SDL_max(w, tw); h += th; } w += PADDING; h += PADDING; m->height = h; int px, py; must(SDL_GetWindowPosition(window, &px, &py)); SDL_Rect disp; must( SDL_GetDisplayUsableBounds(SDL_GetDisplayForWindow(m->window), &disp) ); if (py + y + h > disp.h) { y = disp.h - h - py; y = SDL_max(y, disp.y - py); } if (px + x + w > disp.w) { x = disp.w - w - px; x = SDL_max(x, disp.x - px); } h = SDL_min(h, disp.h); SDL_WarpMouseInWindow(window, x - 2, y + 8); SDL_SetWindowPosition(m->window, x, y); SDL_SetWindowSize(m->window, w, h); SDL_RaiseWindow(m->window); SDL_SyncWindow(m->window); return m; } bool menu_handle_event(menu *m, SDL_Event e) { switch (e.type) { case SDL_EVENT_MOUSE_WHEEL: if (e.wheel.windowID != SDL_GetWindowID(m->window)) { break; } int w, h; SDL_GetWindowSize(m->window, &w, &h); m->scroll += -e.wheel.y * 15; m->scroll = SDL_max(m->scroll, 0); m->scroll = SDL_min(m->scroll, m->height - h); break; case SDL_EVENT_WINDOW_CLOSE_REQUESTED: if (e.window.windowID == SDL_GetWindowID(m->window)) return false; break; case SDL_EVENT_MOUSE_BUTTON_DOWN: if (e.button.windowID != SDL_GetWindowID(m->window)) { return false; } case SDL_EVENT_MOUSE_BUTTON_UP: if (e.button.windowID != SDL_GetWindowID(m->window)) { break; } int option = option_at(m, e.button.x, e.button.y + m->scroll); if (option != -1) { m->entries[option].action(m->entries[option].data); return false; } break; case SDL_EVENT_KEY_DOWN: if (e.key.key == SDLK_ESCAPE) return false; break; case SDL_EVENT_QUIT: return false; default: } return true; } void menu_render(menu *m) { int ww, wh; SDL_GetWindowSize(m->window, &ww, &wh); float mx, my; SDL_GetMouseState(&mx, &my); int hovered = -1; if (SDL_GetWindowFlags(m->window) & SDL_WINDOW_MOUSE_FOCUS) hovered = option_at(m, mx, my + m->scroll); SDL_SetRenderDrawColor(m->renderer, 0x00, 0x00, 0x00, 0xff); SDL_RenderClear(m->renderer); float x = PADDING, y = PADDING - SDL_floor(m->scroll); for (int i = 0; i < m->count; i++) { SDL_FRect dst; dst.x = x; dst.y = y; must(SDL_GetTextureSize(m->textures[i], &dst.w, &dst.h)); if (i == hovered) { SDL_FRect highlight_rect = {0, y, ww, dst.h}; SDL_SetRenderDrawColor(m->renderer, 0x50, 0x50, 0x50, 0xff); SDL_RenderFillRect(m->renderer, &highlight_rect); } SDL_SetRenderDrawColor(m->renderer, 0x00, 0x00, 0x00, 0xff); SDL_RenderTexture(m->renderer, m->textures[i], NULL, &dst); y += dst.h; } SDL_FRect border_rect = {0, 0, ww, wh}; SDL_SetRenderDrawColor(m->renderer, 0x50, 0x50, 0x50, 0xff); SDL_RenderRect(m->renderer, &border_rect); SDL_RenderPresent(m->renderer); } void close_menu(menu *m) { for (int i = 0; i < m->count; i++) { SDL_DestroyTexture(m->textures[i]); } free(m->textures); SDL_DestroyRenderer(m->renderer); SDL_DestroyWindow(m->window); free(m); }