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 unsigned 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 if (cx < params->width - 16) {
75 int index = (cy / lineHeight) - 2;
76 if (index >= 0 && index + start < GUIMenuItemListSize(&menu->items)) {
77 if (menu->index != index + start || !cursorOverItem) {
78 cursorOverItem = 1;
79 }
80 menu->index = index + start;
81 } else {
82 cursorOverItem = 0;
83 }
84 } else if (cursor == GUI_CURSOR_DOWN || cursor == GUI_CURSOR_DRAGGING) {
85 if (cy <= 2 * lineHeight && cy > lineHeight && menu->index > 0) {
86 --menu->index;
87 } else if (cy <= params->height && cy > params->height - lineHeight && menu->index < GUIMenuItemListSize(&menu->items) - 1) {
88 ++menu->index;
89 } else if (cy <= params->height - lineHeight && cy > 2 * lineHeight) {
90 size_t location = cy - 2 * lineHeight;
91 location *= GUIMenuItemListSize(&menu->items);
92 menu->index = location / (params->height - 3 * lineHeight);
93 }
94 }
95 }
96
97 if (menu->index < start) {
98 start = menu->index;
99 }
100 while ((menu->index - start + 4) * lineHeight > params->height) {
101 ++start;
102 }
103 if (newInput & (1 << GUI_INPUT_CANCEL)) {
104 break;
105 }
106 if (newInput & (1 << GUI_INPUT_SELECT) || (cursorOverItem == 2 && cursor == GUI_CURSOR_CLICKED)) {
107 *item = GUIMenuItemListGetPointer(&menu->items, menu->index);
108 if ((*item)->submenu) {
109 enum GUIMenuExitReason reason = GUIShowMenu(params, (*item)->submenu, item);
110 if (reason != GUI_MENU_EXIT_BACK) {
111 return reason;
112 }
113 } else {
114 return GUI_MENU_EXIT_ACCEPT;
115 }
116 }
117 if (cursorOverItem == 1 && (cursor == GUI_CURSOR_UP || cursor == GUI_CURSOR_NOT_PRESENT)) {
118 cursorOverItem = 2;
119 }
120 if (newInput & (1 << GUI_INPUT_BACK)) {
121 return GUI_MENU_EXIT_BACK;
122 }
123
124 params->drawStart();
125 if (menu->background) {
126 menu->background->draw(menu->background, GUIMenuItemListGetPointer(&menu->items, menu->index)->data);
127 }
128 if (params->guiPrepare) {
129 params->guiPrepare();
130 }
131 unsigned y = lineHeight;
132 GUIFontPrint(params->font, 0, y, GUI_ALIGN_LEFT, 0xFFFFFFFF, menu->title);
133 if (menu->subtitle) {
134 GUIFontPrint(params->font, 0, y * 2, GUI_ALIGN_LEFT, 0xFFFFFFFF, menu->subtitle);
135 }
136 y += 2 * lineHeight;
137 size_t itemsPerScreen = (params->height - y) / lineHeight;
138 size_t i;
139 for (i = start; i < GUIMenuItemListSize(&menu->items); ++i) {
140 int color = 0xE0A0A0A0;
141 if (i == menu->index) {
142 color = 0xFFFFFFFF;
143 GUIFontDrawIcon(params->font, 2, y, GUI_ALIGN_BOTTOM | GUI_ALIGN_LEFT, GUI_ORIENT_0, 0xFFFFFFFF, GUI_ICON_POINTER);
144 }
145 struct GUIMenuItem* item = GUIMenuItemListGetPointer(&menu->items, i);
146 GUIFontPrintf(params->font, 0, y, GUI_ALIGN_LEFT, color, " %s", item->title);
147 if (item->validStates && item->validStates[item->state]) {
148 GUIFontPrintf(params->font, params->width, y, GUI_ALIGN_RIGHT, color, "%s ", item->validStates[item->state]);
149 }
150 y += lineHeight;
151 if (y + lineHeight > params->height) {
152 break;
153 }
154 }
155
156 if (itemsPerScreen < GUIMenuItemListSize(&menu->items)) {
157 y = 2 * lineHeight;
158 GUIFontDrawIcon(params->font, params->width - 8, y, GUI_ALIGN_HCENTER | GUI_ALIGN_BOTTOM, GUI_ORIENT_VMIRROR, 0xFFFFFFFF, GUI_ICON_SCROLLBAR_BUTTON);
159 for (; y < params->height - 16; y += 16) {
160 GUIFontDrawIcon(params->font, params->width - 8, y, GUI_ALIGN_HCENTER | GUI_ALIGN_TOP, GUI_ORIENT_0, 0xFFFFFFFF, GUI_ICON_SCROLLBAR_TRACK);
161 }
162 GUIFontDrawIcon(params->font, params->width - 8, y, GUI_ALIGN_HCENTER | GUI_ALIGN_TOP, GUI_ORIENT_0, 0xFFFFFFFF, GUI_ICON_SCROLLBAR_BUTTON);
163
164 size_t top = 2 * lineHeight;
165 y = menu->index * (y - top - 16) / GUIMenuItemListSize(&menu->items);
166 GUIFontDrawIcon(params->font, params->width - 8, top + y, GUI_ALIGN_HCENTER | GUI_ALIGN_TOP, GUI_ORIENT_0, 0xFFFFFFFF, GUI_ICON_SCROLLBAR_THUMB);
167 }
168
169 GUIDrawBattery(params);
170 GUIDrawClock(params);
171
172 if (cursor != GUI_CURSOR_NOT_PRESENT) {
173 GUIFontDrawIcon(params->font, cx, cy, GUI_ALIGN_HCENTER | GUI_ALIGN_TOP, GUI_ORIENT_0, 0xFFFFFFFF, GUI_ICON_CURSOR);
174 }
175
176 if (params->guiFinish) {
177 params->guiFinish();
178 }
179 params->drawEnd();
180 }
181 return GUI_MENU_EXIT_CANCEL;
182}
183
184enum GUICursorState GUIPollCursor(struct GUIParams* params, unsigned* x, unsigned* y) {
185 if (!params->pollCursor) {
186 return GUI_CURSOR_NOT_PRESENT;
187 }
188 enum GUICursorState state = params->pollCursor(x, y);
189 if (params->cursorState == GUI_CURSOR_DOWN) {
190 int dragX = *x - params->cx;
191 int dragY = *y - params->cy;
192 if (dragX * dragX + dragY * dragY > 25) {
193 params->cursorState = GUI_CURSOR_DRAGGING;
194 return GUI_CURSOR_DRAGGING;
195 }
196 if (state == GUI_CURSOR_UP || state == GUI_CURSOR_NOT_PRESENT) {
197 params->cursorState = GUI_CURSOR_UP;
198 return GUI_CURSOR_CLICKED;
199 }
200 } else {
201 params->cx = *x;
202 params->cy = *y;
203 }
204 if (params->cursorState == GUI_CURSOR_DRAGGING) {
205 if (state == GUI_CURSOR_UP || state == GUI_CURSOR_NOT_PRESENT) {
206 params->cursorState = GUI_CURSOR_UP;
207 return GUI_CURSOR_UP;
208 }
209 return GUI_CURSOR_DRAGGING;
210 }
211 params->cursorState = state;
212 return params->cursorState;
213}
214
215void GUIInvalidateKeys(struct GUIParams* params) {
216 for (int i = 0; i < GUI_INPUT_MAX; ++i) {
217 params->inputHistory[i] = 0;
218 }
219}
220
221void GUIDrawBattery(struct GUIParams* params) {
222 if (!params->batteryState) {
223 return;
224 }
225 int state = params->batteryState();
226 uint32_t color = 0xFF000000;
227 if (state == (BATTERY_CHARGING | BATTERY_FULL)) {
228 color |= 0xFFC060;
229 } else if (state & BATTERY_CHARGING) {
230 color |= 0x60FF60;
231 } else if (state >= BATTERY_HALF) {
232 color |= 0xFFFFFF;
233 } else if (state == BATTERY_LOW) {
234 color |= 0x30FFFF;
235 } else {
236 color |= 0x3030FF;
237 }
238
239 enum GUIIcon batteryIcon;
240 switch (state & ~BATTERY_CHARGING) {
241 case BATTERY_EMPTY:
242 batteryIcon = GUI_ICON_BATTERY_EMPTY;
243 break;
244 case BATTERY_LOW:
245 batteryIcon = GUI_ICON_BATTERY_LOW;
246 break;
247 case BATTERY_HALF:
248 batteryIcon = GUI_ICON_BATTERY_HALF;
249 break;
250 case BATTERY_HIGH:
251 batteryIcon = GUI_ICON_BATTERY_HIGH;
252 break;
253 case BATTERY_FULL:
254 batteryIcon = GUI_ICON_BATTERY_FULL;
255 break;
256 default:
257 batteryIcon = GUI_ICON_BATTERY_EMPTY;
258 break;
259 }
260
261 GUIFontDrawIcon(params->font, params->width, 0, GUI_ALIGN_RIGHT, GUI_ORIENT_0, color, batteryIcon);
262}
263
264void GUIDrawClock(struct GUIParams* params) {
265 char buffer[32];
266 time_t t = time(0);
267 struct tm tm;
268 localtime_r(&t, &tm);
269 strftime(buffer, sizeof(buffer), "%H:%M:%S", &tm);
270 GUIFontPrint(params->font, params->width / 2, GUIFontHeight(params->font), GUI_ALIGN_HCENTER, 0xFFFFFFFF, buffer);
271}