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}