all repos — mgba @ 6882339f625b97f344c6464915c2038bc453ea3c

mGBA Game Boy Advance Emulator

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}