all repos — mgba @ 2254fc68c272f3b010c28707bb87fd74715194f0

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 "menu.h"
  7
  8#include "util/gui.h"
  9#include "util/gui/font.h"
 10
 11DEFINE_VECTOR(GUIMenuItemList, struct GUIMenuItem);
 12
 13enum GUIMenuExitReason GUIShowMenu(struct GUIParams* params, struct GUIMenu* menu, struct GUIMenuItem** item) {
 14	size_t start = 0;
 15	size_t lineHeight = GUIFontHeight(params->font);
 16	size_t pageSize = params->height / lineHeight;
 17	if (pageSize > 4) {
 18		pageSize -= 4;
 19	} else {
 20		pageSize = 1;
 21	}
 22	int cursorOverItem = 0;
 23
 24	GUIInvalidateKeys(params);
 25	while (true) {
 26		uint32_t newInput = 0;
 27		GUIPollInput(params, &newInput, 0);
 28		int cx, cy;
 29		enum GUICursorState cursor = GUIPollCursor(params, &cx, &cy);
 30
 31		if (newInput & (1 << GUI_INPUT_UP) && menu->index > 0) {
 32			--menu->index;
 33		}
 34		if (newInput & (1 << GUI_INPUT_DOWN) && menu->index < GUIMenuItemListSize(&menu->items) - 1) {
 35			++menu->index;
 36		}
 37		if (newInput & (1 << GUI_INPUT_LEFT)) {
 38			struct GUIMenuItem* item = GUIMenuItemListGetPointer(&menu->items, menu->index);
 39			if (item->validStates) {
 40				if (item->state > 0) {
 41					--item->state;
 42				}
 43			} else if (menu->index >= pageSize) {
 44				menu->index -= pageSize;
 45			} else {
 46				menu->index = 0;
 47			}
 48		}
 49		if (newInput & (1 << GUI_INPUT_RIGHT)) {
 50			struct GUIMenuItem* item = GUIMenuItemListGetPointer(&menu->items, menu->index);
 51			if (item->validStates) {
 52				if (item->validStates[item->state + 1]) {
 53					++item->state;
 54				}
 55			} else if (menu->index + pageSize < GUIMenuItemListSize(&menu->items)) {
 56				menu->index += pageSize;
 57			} else {
 58				menu->index = GUIMenuItemListSize(&menu->items) - 1;
 59			}
 60		}
 61		if (cursor != GUI_CURSOR_NOT_PRESENT) {
 62			int index = (cy / lineHeight) - 2;
 63			if (index >= 0 && index + start < GUIMenuItemListSize(&menu->items)) {
 64				if (menu->index != index + start || !cursorOverItem) {
 65					cursorOverItem = 1;
 66				}
 67				menu->index = index + start;
 68			} else {
 69				cursorOverItem = 0;
 70			}
 71		}
 72
 73		if (menu->index < start) {
 74			start = menu->index;
 75		}
 76		while ((menu->index - start + 4) * lineHeight > params->height) {
 77			++start;
 78		}
 79		if (newInput & (1 << GUI_INPUT_CANCEL)) {
 80			break;
 81		}
 82		if (newInput & (1 << GUI_INPUT_SELECT) || (cursorOverItem == 2 && cursor == GUI_CURSOR_CLICKED)) {
 83			*item = GUIMenuItemListGetPointer(&menu->items, menu->index);
 84			if ((*item)->submenu) {
 85				enum GUIMenuExitReason reason = GUIShowMenu(params, (*item)->submenu, item);
 86				if (reason != GUI_MENU_EXIT_BACK) {
 87					return reason;
 88				}
 89			} else {
 90				return GUI_MENU_EXIT_ACCEPT;
 91			}
 92		}
 93		if (cursorOverItem == 1 && (cursor == GUI_CURSOR_UP || cursor == GUI_CURSOR_NOT_PRESENT)) {
 94			cursorOverItem = 2;
 95		}
 96		if (newInput & (1 << GUI_INPUT_BACK)) {
 97			return GUI_MENU_EXIT_BACK;
 98		}
 99
100		params->drawStart();
101		if (menu->background) {
102			menu->background->draw(menu->background, GUIMenuItemListGetPointer(&menu->items, menu->index)->data);
103		}
104		if (params->guiPrepare) {
105			params->guiPrepare();
106		}
107		unsigned y = lineHeight;
108		GUIFontPrint(params->font, 0, y, GUI_TEXT_LEFT, 0xFFFFFFFF, menu->title);
109		if (menu->subtitle) {
110			GUIFontPrint(params->font, 0, y * 2, GUI_TEXT_LEFT, 0xFFFFFFFF, menu->subtitle);
111		}
112		y += 2 * lineHeight;
113		size_t i;
114		for (i = start; i < GUIMenuItemListSize(&menu->items); ++i) {
115			int color = 0xE0A0A0A0;
116			char bullet = ' ';
117			if (i == menu->index) {
118				color = 0xFFFFFFFF;
119				bullet = '>';
120			}
121			struct GUIMenuItem* item = GUIMenuItemListGetPointer(&menu->items, i);
122			GUIFontPrintf(params->font, 0, y, GUI_TEXT_LEFT, color, "%c %s", bullet, item->title);
123			if (item->validStates) {
124				GUIFontPrintf(params->font, params->width, y, GUI_TEXT_RIGHT, color, "%s ", item->validStates[item->state]);
125			}
126			y += lineHeight;
127			if (y + lineHeight > params->height) {
128				break;
129			}
130		}
131
132		GUIDrawBattery(params);
133		GUIDrawClock(params);
134
135		if (params->guiFinish) {
136			params->guiFinish();
137		}
138		params->drawEnd();
139	}
140	return GUI_MENU_EXIT_CANCEL;
141}
142
143enum GUICursorState GUIPollCursor(struct GUIParams* params, int* x, int* y) {
144	if (!params->pollCursor) {
145		return GUI_CURSOR_NOT_PRESENT;
146	}
147	enum GUICursorState state = params->pollCursor(x, y);
148	if (params->cursorState == GUI_CURSOR_DOWN) {
149		int dragX = *x - params->cx;
150		int dragY = *y - params->cy;
151		if (dragX * dragX + dragY * dragY > 25) {
152			params->cursorState = GUI_CURSOR_DRAGGING;
153			return GUI_CURSOR_DRAGGING;
154		}
155		if (state == GUI_CURSOR_UP || state == GUI_CURSOR_NOT_PRESENT) {
156			params->cursorState = GUI_CURSOR_UP;
157			return GUI_CURSOR_CLICKED;
158		}
159	} else {
160		params->cx = *x;
161		params->cy = *y;
162	}
163	if (params->cursorState == GUI_CURSOR_DRAGGING) {
164		if (state == GUI_CURSOR_UP || state == GUI_CURSOR_NOT_PRESENT) {
165			params->cursorState = GUI_CURSOR_UP;
166			return GUI_CURSOR_UP;
167		}
168		return GUI_CURSOR_DRAGGING;
169	}
170	params->cursorState = state;
171	return params->cursorState;
172}
173
174void GUIInvalidateKeys(struct GUIParams* params) {
175	for (int i = 0; i < GUI_INPUT_MAX; ++i) {
176		params->inputHistory[i] = 0;
177	}
178}
179
180void GUIDrawBattery(struct GUIParams* params) {
181	if (!params->batteryState) {
182		return;
183	}
184	int state = params->batteryState();
185	uint32_t color = 0xFF000000;
186	if (state == (BATTERY_CHARGING | BATTERY_FULL)) {
187		color |= 0xFFC060;
188	} else if (state & BATTERY_CHARGING) {
189		color |= 0x60FF60;
190	} else if (state >= BATTERY_HALF) {
191		color |= 0xFFFFFF;
192	} else if (state == BATTERY_LOW) {
193		color |= 0x30FFFF;
194	} else {
195		color |= 0x3030FF;
196	}
197
198	const char* batteryText;
199	switch (state & ~BATTERY_CHARGING) {
200	case BATTERY_EMPTY:
201		batteryText = "[    ]";
202		break;
203	case BATTERY_LOW:
204		batteryText = "[I   ]";
205		break;
206	case BATTERY_HALF:
207		batteryText = "[II  ]";
208		break;
209	case BATTERY_HIGH:
210		batteryText = "[III ]";
211		break;
212	case BATTERY_FULL:
213		batteryText = "[IIII]";
214		break;
215	default:
216		batteryText = "[????]";
217		break;
218	}
219
220	GUIFontPrint(params->font, params->width, GUIFontHeight(params->font), GUI_TEXT_RIGHT, color, batteryText);
221}
222
223void GUIDrawClock(struct GUIParams* params) {
224	char buffer[32];
225	time_t t = time(0);
226	struct tm tm;
227	localtime_r(&t, &tm);
228	strftime(buffer, sizeof(buffer), "%H:%M:%S", &tm);
229	GUIFontPrint(params->font, params->width / 2, GUIFontHeight(params->font), GUI_TEXT_CENTER, 0xFFFFFFFF, buffer);
230}