all repos — mgba @ bb5ef9246cb604f1ecf3c57c77be4d72b2d75153

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