1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
|
#include <stdlib.h>
#include <SDL3/SDL.h>
#include <SDL3_ttf/SDL_ttf.h>
#include "panic.h"
#include "font.h"
#include "main.h"
#include "menu.h"
#define MENU_PADDING 4
static int option_at(menu *m, int x, int y) {
float cur_y = MENU_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");
m->window = must(SDL_CreatePopupWindow(window,
x + 2, y - 8, 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 = 0, h = 0;
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));
float tw, th;
must(SDL_GetTextureSize(m->textures[i], &tw, &th));
w = SDL_max(w, tw);
h += th;
}
SDL_SetWindowSize(m->window, w + MENU_PADDING * 2, h + MENU_PADDING * 2);
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_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);
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);
SDL_RenderClear(m->renderer);
float x = MENU_PADDING, y = MENU_PADDING;
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_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);
}
|