src/util/gui/menu.c (view raw)
1/* Copyright (c) 2013-2015 Jeffrey Pfau
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6#include <mgba-util/gui/menu.h>
7
8#include <mgba-util/gui.h>
9#include <mgba-util/gui/font.h>
10
11#ifdef _3DS
12#include <3ds.h>
13#endif
14
15DEFINE_VECTOR(GUIMenuItemList, struct GUIMenuItem);
16
17enum GUIMenuExitReason GUIShowMenu(struct GUIParams* params, struct GUIMenu* menu, struct GUIMenuItem** item) {
18 size_t start = 0;
19 size_t lineHeight = GUIFontHeight(params->font);
20 size_t pageSize = params->height / lineHeight;
21 if (pageSize > 4) {
22 pageSize -= 4;
23 } else {
24 pageSize = 1;
25 }
26 int cursorOverItem = 0;
27
28 GUIInvalidateKeys(params);
29 while (true) {
30#ifdef _3DS
31 if (!aptMainLoop()) {
32 return GUI_MENU_EXIT_CANCEL;
33 }
34#endif
35 uint32_t newInput = 0;
36 GUIPollInput(params, &newInput, 0);
37 unsigned cx, cy;
38 enum GUICursorState cursor = GUIPollCursor(params, &cx, &cy);
39
40 if (newInput & (1 << GUI_INPUT_UP) && menu->index > 0) {
41 --menu->index;
42 }
43 if (newInput & (1 << GUI_INPUT_DOWN) && menu->index < GUIMenuItemListSize(&menu->items) - 1) {
44 ++menu->index;
45 }
46 if (newInput & (1 << GUI_INPUT_LEFT)) {
47 struct GUIMenuItem* item = GUIMenuItemListGetPointer(&menu->items, menu->index);
48 if (item->validStates) {
49 if (item->state > 0) {
50 unsigned oldState = item->state;
51 do {
52 --item->state;
53 } while (!item->validStates[item->state] && item->state > 0);
54 if (!item->validStates[item->state]) {
55 item->state = oldState;
56 }
57 }
58 } else if (menu->index >= pageSize) {
59 menu->index -= pageSize;
60 } else {
61 menu->index = 0;
62 }
63 }
64 if (newInput & (1 << GUI_INPUT_RIGHT)) {
65 struct GUIMenuItem* item = GUIMenuItemListGetPointer(&menu->items, menu->index);
66 if (item->validStates) {
67 if (item->state < item->nStates - 1) {
68 unsigned oldState = item->state;
69 do {
70 ++item->state;
71 } while (!item->validStates[item->state] && item->state < item->nStates - 1);
72 if (!item->validStates[item->state]) {
73 item->state = oldState;
74 }
75 }
76 } else if (menu->index + pageSize < GUIMenuItemListSize(&menu->items)) {
77 menu->index += pageSize;
78 } else {
79 menu->index = GUIMenuItemListSize(&menu->items) - 1;
80 }
81 }
82 if (cursor != GUI_CURSOR_NOT_PRESENT) {
83 if (cx < params->width - 16) {
84 int index = (cy / lineHeight) - 2;
85 if (index >= 0 && index + start < GUIMenuItemListSize(&menu->items)) {
86 if (menu->index != index + start || !cursorOverItem) {
87 cursorOverItem = 1;
88 }
89 menu->index = index + start;
90 } else {
91 cursorOverItem = 0;
92 }
93 } else if (cursor == GUI_CURSOR_DOWN || cursor == GUI_CURSOR_DRAGGING) {
94 if (cy <= 2 * lineHeight && cy > lineHeight && menu->index > 0) {
95 --menu->index;
96 } else if (cy <= params->height && cy > params->height - lineHeight && menu->index < GUIMenuItemListSize(&menu->items) - 1) {
97 ++menu->index;
98 } else if (cy <= params->height - lineHeight && cy > 2 * lineHeight) {
99 size_t location = cy - 2 * lineHeight;
100 location *= GUIMenuItemListSize(&menu->items) - 1;
101 menu->index = location / (params->height - 3 * lineHeight);
102 }
103 }
104 }
105
106 if (menu->index < start) {
107 start = menu->index;
108 }
109 while ((menu->index - start + 4) * lineHeight > params->height) {
110 ++start;
111 }
112 if (newInput & (1 << GUI_INPUT_CANCEL)) {
113 break;
114 }
115 if (newInput & (1 << GUI_INPUT_SELECT) || (cursorOverItem == 2 && cursor == GUI_CURSOR_CLICKED)) {
116 *item = GUIMenuItemListGetPointer(&menu->items, menu->index);
117 if ((*item)->submenu) {
118 enum GUIMenuExitReason reason = GUIShowMenu(params, (*item)->submenu, item);
119 if (reason != GUI_MENU_EXIT_BACK) {
120 return reason;
121 }
122 } else {
123 return GUI_MENU_EXIT_ACCEPT;
124 }
125 }
126 if (cursorOverItem == 1 && (cursor == GUI_CURSOR_UP || cursor == GUI_CURSOR_NOT_PRESENT)) {
127 cursorOverItem = 2;
128 }
129 if (newInput & (1 << GUI_INPUT_BACK)) {
130 return GUI_MENU_EXIT_BACK;
131 }
132
133 params->drawStart();
134 if (menu->background) {
135 menu->background->draw(menu->background, GUIMenuItemListGetPointer(&menu->items, menu->index)->data);
136 }
137 if (params->guiPrepare) {
138 params->guiPrepare();
139 }
140 unsigned y = lineHeight;
141 GUIFontPrint(params->font, 0, y, GUI_ALIGN_LEFT, 0xFFFFFFFF, menu->title);
142 if (menu->subtitle) {
143 GUIFontPrint(params->font, 0, y * 2, GUI_ALIGN_LEFT, 0xFFFFFFFF, menu->subtitle);
144 }
145 y += 2 * lineHeight;
146 size_t itemsPerScreen = (params->height - y) / lineHeight;
147 size_t i;
148 for (i = start; i < GUIMenuItemListSize(&menu->items); ++i) {
149 int color = 0xE0A0A0A0;
150 if (i == menu->index) {
151 color = 0xFFFFFFFF;
152 GUIFontDrawIcon(params->font, lineHeight * 0.8f, y, GUI_ALIGN_BOTTOM | GUI_ALIGN_RIGHT, GUI_ORIENT_0, 0xFFFFFFFF, GUI_ICON_POINTER);
153 }
154 struct GUIMenuItem* item = GUIMenuItemListGetPointer(&menu->items, i);
155 GUIFontPrint(params->font, lineHeight, y, GUI_ALIGN_LEFT, color, item->title);
156 if (item->validStates && item->validStates[item->state]) {
157 GUIFontPrintf(params->font, params->width, y, GUI_ALIGN_RIGHT, color, "%s ", item->validStates[item->state]);
158 }
159 y += lineHeight;
160 if (y + lineHeight > params->height) {
161 break;
162 }
163 }
164
165 if (itemsPerScreen < GUIMenuItemListSize(&menu->items)) {
166 size_t top = 2 * lineHeight;
167 size_t bottom = params->height - 8;
168 unsigned w;
169 unsigned right;
170 GUIFontIconMetrics(params->font, GUI_ICON_SCROLLBAR_BUTTON, &right, 0);
171 GUIFontIconMetrics(params->font, GUI_ICON_SCROLLBAR_TRACK, &w, 0);
172 right = (right - w) / 2;
173 GUIFontDrawIcon(params->font, params->width - 8, top, GUI_ALIGN_HCENTER | GUI_ALIGN_BOTTOM, GUI_ORIENT_VMIRROR, 0xFFFFFFFF, GUI_ICON_SCROLLBAR_BUTTON);
174 GUIFontDrawIconSize(params->font, params->width - right - 8, top, 0, bottom - top, 0xFFFFFFFF, GUI_ICON_SCROLLBAR_TRACK);
175 GUIFontDrawIcon(params->font, params->width - 8, bottom, GUI_ALIGN_HCENTER | GUI_ALIGN_TOP, GUI_ORIENT_0, 0xFFFFFFFF, GUI_ICON_SCROLLBAR_BUTTON);
176
177 y = menu->index * (bottom - top - 16) / GUIMenuItemListSize(&menu->items);
178 GUIFontDrawIcon(params->font, params->width - 8, top + y, GUI_ALIGN_HCENTER | GUI_ALIGN_TOP, GUI_ORIENT_0, 0xFFFFFFFF, GUI_ICON_SCROLLBAR_THUMB);
179 }
180
181 GUIDrawBattery(params);
182 GUIDrawClock(params);
183
184 if (cursor != GUI_CURSOR_NOT_PRESENT) {
185 GUIFontDrawIcon(params->font, cx, cy, GUI_ALIGN_HCENTER | GUI_ALIGN_TOP, GUI_ORIENT_0, 0xFFFFFFFF, GUI_ICON_CURSOR);
186 }
187
188 if (params->guiFinish) {
189 params->guiFinish();
190 }
191 params->drawEnd();
192 }
193 return GUI_MENU_EXIT_CANCEL;
194}
195
196enum GUIMenuExitReason GUIShowMessageBox(struct GUIParams* params, int buttons, int frames, const char* format, ...) {
197 va_list args;
198 va_start(args, format);
199 char message[256] = {0};
200 vsnprintf(message, sizeof(message) - 1, format, args);
201 va_end(args);
202
203 while (true) {
204 if (frames) {
205 --frames;
206 if (!frames) {
207 break;
208 }
209 }
210 params->drawStart();
211 if (params->guiPrepare) {
212 params->guiPrepare();
213 }
214 GUIFontPrint(params->font, params->width / 2, (GUIFontHeight(params->font) + params->height) / 2, GUI_ALIGN_HCENTER, 0xFFFFFFFF, message);
215 if (params->guiFinish) {
216 params->guiFinish();
217 }
218 params->drawEnd();
219
220 uint32_t input = 0;
221 GUIPollInput(params, &input, 0);
222 if (input) {
223 if (input & (1 << GUI_INPUT_SELECT)) {
224 if (buttons & GUI_MESSAGE_BOX_OK) {
225 return GUI_MENU_EXIT_ACCEPT;
226 }
227 if (buttons & GUI_MESSAGE_BOX_CANCEL) {
228 return GUI_MENU_EXIT_CANCEL;
229 }
230 }
231 if (input & (1 << GUI_INPUT_BACK)) {
232 if (buttons & GUI_MESSAGE_BOX_CANCEL) {
233 return GUI_MENU_EXIT_BACK;
234 }
235 if (buttons & GUI_MESSAGE_BOX_OK) {
236 return GUI_MENU_EXIT_ACCEPT;
237 }
238 }
239 if (input & (1 << GUI_INPUT_CANCEL)) {
240 if (buttons & GUI_MESSAGE_BOX_CANCEL) {
241 return GUI_MENU_EXIT_CANCEL;
242 }
243 if (buttons & GUI_MESSAGE_BOX_OK) {
244 return GUI_MENU_EXIT_ACCEPT;
245 }
246 }
247 }
248 }
249 return GUI_MENU_EXIT_CANCEL;
250}
251
252void GUIDrawBattery(struct GUIParams* params) {
253 if (!params->batteryState) {
254 return;
255 }
256 int state = params->batteryState();
257 uint32_t color = 0xFF000000;
258 if (state == (BATTERY_CHARGING | BATTERY_FULL)) {
259 color |= 0xFFC060;
260 } else if (state & BATTERY_CHARGING) {
261 color |= 0x60FF60;
262 } else if (state >= BATTERY_HALF) {
263 color |= 0xFFFFFF;
264 } else if (state == BATTERY_LOW) {
265 color |= 0x30FFFF;
266 } else {
267 color |= 0x3030FF;
268 }
269
270 enum GUIIcon batteryIcon;
271 switch (state & ~BATTERY_CHARGING) {
272 case BATTERY_EMPTY:
273 batteryIcon = GUI_ICON_BATTERY_EMPTY;
274 break;
275 case BATTERY_LOW:
276 batteryIcon = GUI_ICON_BATTERY_LOW;
277 break;
278 case BATTERY_HALF:
279 batteryIcon = GUI_ICON_BATTERY_HALF;
280 break;
281 case BATTERY_HIGH:
282 batteryIcon = GUI_ICON_BATTERY_HIGH;
283 break;
284 case BATTERY_FULL:
285 batteryIcon = GUI_ICON_BATTERY_FULL;
286 break;
287 default:
288 batteryIcon = GUI_ICON_BATTERY_EMPTY;
289 break;
290 }
291
292 GUIFontDrawIcon(params->font, params->width, 0, GUI_ALIGN_RIGHT, GUI_ORIENT_0, color, batteryIcon);
293}
294
295void GUIDrawClock(struct GUIParams* params) {
296 char buffer[32];
297 time_t t = time(0);
298 struct tm tm;
299 localtime_r(&t, &tm);
300 strftime(buffer, sizeof(buffer), "%H:%M:%S", &tm);
301 GUIFontPrint(params->font, params->width / 2, GUIFontHeight(params->font), GUI_ALIGN_HCENTER, 0xFFFFFFFF, buffer);
302}