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 |= 0xFF2020;
188 } else if (state & BATTERY_CHARGING) {
189 color |= 0x20FF20;
190 } else if (state >= BATTERY_HALF) {
191 color |= 0xFFFFFF;
192 } else if (state == BATTERY_LOW) {
193 color |= 0x20FFFF;
194 } else {
195 color |= 0x2020FF;
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 = localtime(&t);
227 strftime(buffer, sizeof(buffer), "%H:%M:%S", tm);
228 GUIFontPrint(params->font, params->width / 2, GUIFontHeight(params->font), GUI_TEXT_CENTER, 0xFFFFFFFF, buffer);
229}