diff options
Diffstat (limited to 'apps')
-rw-r--r-- | apps/plugins/rockboy/menu.c | 303 |
1 files changed, 303 insertions, 0 deletions
diff --git a/apps/plugins/rockboy/menu.c b/apps/plugins/rockboy/menu.c new file mode 100644 index 0000000000..0d21fca867 --- /dev/null +++ b/apps/plugins/rockboy/menu.c | |||
@@ -0,0 +1,303 @@ | |||
1 | /*********************************************************************/ | ||
2 | /* menu.c - user menu for rockboy */ | ||
3 | /* */ | ||
4 | /* Note: this file only exposes one function: do_user_menu(). */ | ||
5 | /*********************************************************************/ | ||
6 | |||
7 | #include "stdlib.h" | ||
8 | #include "string.h" | ||
9 | #include "button.h" | ||
10 | #include "rockmacros.h" | ||
11 | |||
12 | /* load/save state function declarations */ | ||
13 | static void do_slot_menu(bool is_load); | ||
14 | |||
15 | #define MENU_CANCEL (-1) | ||
16 | static int do_menu(char *title, char **items, size_t num_items, int sel_item); | ||
17 | |||
18 | /* main menu items */ | ||
19 | #define MAIN_MENU_TITLE "RockBoy Menu" | ||
20 | typedef enum { | ||
21 | ITEM_BACK, | ||
22 | ITEM_LOAD, | ||
23 | ITEM_SAVE, | ||
24 | ITEM_OPT, | ||
25 | ITEM_QUIT, | ||
26 | ITEM_LAST | ||
27 | } MainMenuItem; | ||
28 | |||
29 | /* strings for the main menu */ | ||
30 | static const char *main_menu[] = { | ||
31 | "Back to Game", | ||
32 | "Load State...", | ||
33 | "Save State...", | ||
34 | "Options...", | ||
35 | "Quit RockBoy" | ||
36 | }; | ||
37 | |||
38 | typedef enum { | ||
39 | ITEM_SLOT1, | ||
40 | ITEM_SLOT2, | ||
41 | ITEM_SLOT3, | ||
42 | ITEM_SLOT4, | ||
43 | ITEM_SLOT5 | ||
44 | } SlotMenuItem; | ||
45 | |||
46 | /* this is evil, but we snprintf() into it later :( */ | ||
47 | static const char *slot_menu[] = { | ||
48 | "1. ", | ||
49 | "2. ", | ||
50 | "3. ", | ||
51 | "4. ", | ||
52 | "5. " | ||
53 | }; | ||
54 | |||
55 | /* | ||
56 | * do_user_menu - create the user menu on the screen. | ||
57 | * | ||
58 | * Returns USER_MENU_QUIT if the user selected "quit", otherwise | ||
59 | * returns zero. | ||
60 | * | ||
61 | * Note: this is the only non-static function in this file at the | ||
62 | * moment. In the future I may turn do_menu/etc into a proper API, in | ||
63 | * which case they'll be exposed as well. | ||
64 | * | ||
65 | */ | ||
66 | int do_user_menu(void) { | ||
67 | int mi, ret, num_items; | ||
68 | bool done = false; | ||
69 | |||
70 | /* set defaults */ | ||
71 | ret = 0; /* return value */ | ||
72 | mi = 0; /* initial menu selection */ | ||
73 | num_items = sizeof(main_menu) / sizeof(char*); | ||
74 | |||
75 | /* loop until we should exit menu */ | ||
76 | while (!done) { | ||
77 | /* get item selection */ | ||
78 | mi = do_menu(MAIN_MENU_TITLE, (char**) main_menu, num_items, mi); | ||
79 | |||
80 | /* handle selected menu item */ | ||
81 | switch (mi) { | ||
82 | case ITEM_QUIT: | ||
83 | ret = USER_MENU_QUIT; | ||
84 | case MENU_CANCEL: | ||
85 | case ITEM_BACK: | ||
86 | done = true; | ||
87 | break; | ||
88 | case ITEM_LOAD: | ||
89 | do_slot_menu(1); | ||
90 | break; | ||
91 | case ITEM_SAVE: | ||
92 | do_slot_menu(0); | ||
93 | break; | ||
94 | } | ||
95 | } | ||
96 | |||
97 | /* return somethin' */ | ||
98 | return ret; | ||
99 | } | ||
100 | |||
101 | /* | ||
102 | * do_load_menu - prompt the user for a memory slot | ||
103 | * | ||
104 | * TOOD | ||
105 | * | ||
106 | */ | ||
107 | static void do_slot_menu(bool is_load) { | ||
108 | int i, mi, ret, num_items; | ||
109 | bool done = false; | ||
110 | char *title; | ||
111 | |||
112 | /* set defaults */ | ||
113 | ret = 0; /* return value */ | ||
114 | mi = 0; /* initial menu selection */ | ||
115 | num_items = sizeof(slot_menu) / sizeof(char*); | ||
116 | |||
117 | /* create menu items */ | ||
118 | for (i = 0; i < num_items; i++) { | ||
119 | /* TODO: get slot info here */ | ||
120 | snprintf((char*) slot_menu[i], sizeof(slot_menu[i]), "%2d. %s", i + 1, "<empty>"); | ||
121 | } | ||
122 | |||
123 | /* set menu title */ | ||
124 | title = is_load ? "Load State" : "Save State"; | ||
125 | |||
126 | /* loop until we should exit menu */ | ||
127 | while (!done) { | ||
128 | /* get item selection */ | ||
129 | mi = do_menu(title, (char**) slot_menu, num_items, mi); | ||
130 | |||
131 | /* handle selected menu item */ | ||
132 | if (mi == MENU_CANCEL) { | ||
133 | done = true; | ||
134 | break; | ||
135 | } else { | ||
136 | if (is_load) { | ||
137 | /* TODO: load slot here */ | ||
138 | } else { | ||
139 | /* TODO: save slot here */ | ||
140 | } | ||
141 | } | ||
142 | } | ||
143 | } | ||
144 | |||
145 | /*********************************************************************/ | ||
146 | /* MENU FUNCTIONS */ | ||
147 | /*********************************************************************/ | ||
148 | /* at some point i'll make this a generic menu interface, but for now, | ||
149 | * these defines will suffice */ | ||
150 | #define MENU_X 10 | ||
151 | #define MENU_Y 10 | ||
152 | #define MENU_WIDTH (LCD_WIDTH - 2 * MENU_X) | ||
153 | #define MENU_HEIGHT (LCD_HEIGHT - 2 * MENU_Y) | ||
154 | #define MENU_RECT MENU_X, MENU_Y, MENU_WIDTH, MENU_HEIGHT | ||
155 | #define SHADOW_RECT MENU_X + 1, MENU_Y + 1, MENU_WIDTH, MENU_HEIGHT | ||
156 | #define MENU_ITEM_PAD 2 | ||
157 | |||
158 | /* | ||
159 | * select_item - select menu item (after deselecting current item) | ||
160 | */ | ||
161 | static void select_item(char *title, int curr_item, size_t item_i) { | ||
162 | int x, y, w, h; | ||
163 | |||
164 | /* get size of title, use that as height ofr all lines */ | ||
165 | rb->lcd_getstringsize(title, &w, &h); | ||
166 | h += MENU_ITEM_PAD * 2; | ||
167 | |||
168 | /* calc x and width */ | ||
169 | x = MENU_X + MENU_ITEM_PAD; | ||
170 | w = MENU_WIDTH - 2 * MENU_ITEM_PAD; | ||
171 | |||
172 | /* if there is a current item, then deselect it */ | ||
173 | if (curr_item >= 0) { | ||
174 | /* deselect old item */ | ||
175 | y = MENU_Y + h + MENU_ITEM_PAD * 2; /* account for title */ | ||
176 | y += h * curr_item; | ||
177 | rb->lcd_invertrect(x, y, w, h); | ||
178 | } | ||
179 | |||
180 | /* select new item */ | ||
181 | curr_item = item_i; | ||
182 | |||
183 | /* select new item */ | ||
184 | y = MENU_Y + h + MENU_ITEM_PAD * 2; /* account for title */ | ||
185 | y += h * curr_item; | ||
186 | rb->lcd_invertrect(x, y, w, h); | ||
187 | |||
188 | /* update the menu window */ | ||
189 | rb->lcd_update_rect(MENU_RECT); | ||
190 | } | ||
191 | |||
192 | /* | ||
193 | * draw_menu - draw menu on screen | ||
194 | * | ||
195 | * Returns MENU_CANCEL if the user cancelled, or the item number of the | ||
196 | * selected item. | ||
197 | * | ||
198 | */ | ||
199 | static void draw_menu(char *title, char **items, size_t num_items) { | ||
200 | size_t i; | ||
201 | int x, y, w, h, by; | ||
202 | |||
203 | /* set to default? font */ | ||
204 | /* rb->lcd_setfont(0); */ | ||
205 | |||
206 | /* draw the outline */ | ||
207 | rb->lcd_fillrect(SHADOW_RECT); | ||
208 | rb->lcd_clearrect(MENU_RECT); | ||
209 | rb->lcd_drawrect(MENU_RECT); | ||
210 | |||
211 | /* calculate x/y */ | ||
212 | x = MENU_X + MENU_ITEM_PAD; | ||
213 | y = MENU_Y + MENU_ITEM_PAD * 2; | ||
214 | rb->lcd_getstringsize(title, &w, &h); | ||
215 | h += MENU_ITEM_PAD * 2; | ||
216 | |||
217 | /* draw menu stipple */ | ||
218 | for (i = MENU_Y; i < (size_t) y + h; i += 2) | ||
219 | rb->lcd_drawline(MENU_X, i, MENU_X + MENU_WIDTH, i); | ||
220 | |||
221 | /* clear title rect */ | ||
222 | rb->lcd_clearrect((LCD_WIDTH - w) / 2 - 2, y - 2, w + 4, h); | ||
223 | |||
224 | /* draw centered title on screen */ | ||
225 | rb->lcd_putsxy((LCD_WIDTH - w)/2, y, title); | ||
226 | |||
227 | /* calculate base Y for items */ | ||
228 | by = y + h + MENU_ITEM_PAD; | ||
229 | |||
230 | /* iterate over each item and draw it on the screen */ | ||
231 | for (i = 0; i < num_items; i++) | ||
232 | rb->lcd_putsxy(x, by + h * i, items[i]); | ||
233 | |||
234 | /* update the screen */ | ||
235 | rb->lcd_update(); | ||
236 | } | ||
237 | |||
238 | /* | ||
239 | * do_menu - draw menu on screen. | ||
240 | * | ||
241 | * Draw a menu titled @title on the screen, with @num_items elements | ||
242 | * from @items, and select the @sel element. If in doubt, set @sel to | ||
243 | * -1 :). | ||
244 | * | ||
245 | */ | ||
246 | static int do_menu(char *title, char **items, size_t num_items, int sel) { | ||
247 | int btn, sel_item, ret, curr_item; | ||
248 | bool done = false; | ||
249 | ret = MENU_CANCEL; | ||
250 | |||
251 | /* draw menu on screen and select the first item */ | ||
252 | draw_menu(title, items, num_items); | ||
253 | curr_item = -1; | ||
254 | select_item(title, curr_item, sel); | ||
255 | curr_item = sel; | ||
256 | |||
257 | /* make sure button state is empty */ | ||
258 | while (rb->button_get(false) != BUTTON_NONE) | ||
259 | rb->yield(); | ||
260 | |||
261 | /* loop until the menu is finished */ | ||
262 | while (!done) { | ||
263 | /* grab a button */ | ||
264 | btn = rb->button_get(true); | ||
265 | |||
266 | /* handle the button */ | ||
267 | switch (btn) { | ||
268 | case BUTTON_DOWN: | ||
269 | /* select next item in list */ | ||
270 | sel_item = curr_item + 1; | ||
271 | if (sel_item >= (int) num_items) | ||
272 | sel_item = 0; | ||
273 | select_item(title, curr_item, sel_item); | ||
274 | curr_item = sel_item; | ||
275 | break; | ||
276 | case BUTTON_UP: | ||
277 | /* select prev item in list */ | ||
278 | sel_item = curr_item - 1; | ||
279 | if (sel_item < 0) | ||
280 | sel_item = num_items - 1; | ||
281 | select_item(title, curr_item, sel_item); | ||
282 | curr_item = sel_item; | ||
283 | break; | ||
284 | case BUTTON_RIGHT: | ||
285 | /* select current item */ | ||
286 | ret = curr_item; | ||
287 | done = true; | ||
288 | break; | ||
289 | case BUTTON_LEFT: | ||
290 | case BUTTON_SELECT: | ||
291 | /* cancel out of menu */ | ||
292 | ret = MENU_CANCEL; | ||
293 | done = true; | ||
294 | break; | ||
295 | } | ||
296 | |||
297 | /* give the OS some love */ | ||
298 | rb->yield(); | ||
299 | } | ||
300 | |||
301 | /* return selected item */ | ||
302 | return ret; | ||
303 | } | ||