1 /**
2 * \file
3 *
4 * \brief Spin control widget
5 * Copyright (c) 2011-2015 Atmel Corporation. All rights reserved.
6 *
7 * \asf_license_start
8 *
9 * \page License
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions are met:
13 *
14 * 1. Redistributions of source code must retain the above copyright notice,
15 * this list of conditions and the following disclaimer.
16 *
17 * 2. Redistributions in binary form must reproduce the above copyright notice,
18 * this list of conditions and the following disclaimer in the documentation
19 * and/or other materials provided with the distribution.
20 *
21 * 3. The name of Atmel may not be used to endorse or promote products derived
22 * from this software without specific prior written permission.
23 *
24 * 4. This software may only be redistributed and used in connection with an
25 * Atmel microcontroller product.
26 *
27 * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
28 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
29 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
30 * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR
31 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
35 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
36 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37 * POSSIBILITY OF SUCH DAMAGE.
38 *
39 * \asf_license_stop
40 *
41 */
42 /*
43 * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a>
44 */
45
46 #include "sysfont.h"
47 #include "stdio.h"
48 #include "gfx_mono.h"
49 #include "gfx_mono_spinctrl.h"
50
51 PROGMEM_DECLARE(gfx_mono_color_t, spin_indicator_data[]) = {
52 GFX_MONO_SPINCTRL_SPIN_INDICATOR_BITMAP
53 };
54
55 PROGMEM_DECLARE(gfx_mono_color_t, indicator_data[]) = {
56 GFX_MONO_SPINCTRL_INDICATOR_BITMAP
57 };
58
59 struct gfx_mono_bitmap gfx_mono_spinctrl_bitmap_spin_indicator = {
60 .height = GFX_MONO_SPINCTRL_SPIN_INDICATOR_HEIGHT,
61 .width = GFX_MONO_SPINCTRL_SPIN_INDICATOR_WIDTH,
62 .type = GFX_MONO_BITMAP_PROGMEM,
63 .data.progmem = spin_indicator_data
64 };
65
66 struct gfx_mono_bitmap gfx_mono_spinctrl_bitmap_indicator = {
67 .height = GFX_MONO_SPINCTRL_INDICATOR_HEIGHT,
68 .width = GFX_MONO_SPINCTRL_INDICATOR_WIDTH,
69 .type = GFX_MONO_BITMAP_PROGMEM,
70 .data.progmem = indicator_data
71 };
72
73 /**
74 * \brief Draw or delete indicator arrow in front of spinner
75 *
76 * \param[in] *spinner initialized gfx_mono_spinctrl struct
77 * \param[in] draw true on draw, false on delete
78 */
gfx_mono_spinctrl_draw_indicator(struct gfx_mono_spinctrl * spinner,bool draw)79 static void gfx_mono_spinctrl_draw_indicator(struct gfx_mono_spinctrl *spinner,
80 bool draw)
81 {
82 if (draw) {
83 gfx_mono_put_bitmap(&gfx_mono_spinctrl_bitmap_indicator, 0,
84 spinner->y);
85 } else {
86 gfx_mono_draw_filled_rect(0, spinner->y,
87 GFX_MONO_SPINCTRL_INDICATOR_WIDTH,
88 GFX_MONO_SPINCTRL_INDICATOR_HEIGHT,
89 GFX_PIXEL_CLR);
90 }
91 }
92
93 /**
94 * \brief Draw or delete indicator in front of spinner data
95 *
96 * \param[in] spinner pointer to initialized gfx_mono_spinctrl struct
97 * \param[in] draw true on draw, false on delete
98 */
gfx_mono_spinctrl_draw_spin_indicator(struct gfx_mono_spinctrl * spinner,bool draw)99 static void gfx_mono_spinctrl_draw_spin_indicator(struct gfx_mono_spinctrl
100 *spinner, bool draw)
101 {
102 if (draw) {
103 gfx_mono_put_bitmap(&gfx_mono_spinctrl_bitmap_spin_indicator,
104 GFX_MONO_LCD_WIDTH - GFX_MONO_SPINCTRL_SPIN_INDICATOR_WIDTH,
105 spinner->y);
106 } else {
107 gfx_mono_draw_filled_rect(GFX_MONO_LCD_WIDTH -
108 GFX_MONO_SPINCTRL_SPIN_INDICATOR_WIDTH,
109 spinner->y,
110 GFX_MONO_SPINCTRL_SPIN_INDICATOR_WIDTH,
111 GFX_MONO_SPINCTRL_SPIN_INDICATOR_HEIGHT,
112 GFX_PIXEL_CLR);
113 }
114 }
115
116 /**
117 * \brief Draw OK button at bottom of screen
118 *
119 * This function draws an OK button at the bottom of the screen. It will
120 * also draw an indicator arrow in front of the button if the indicator bool
121 * is true. If the draw bool is false, the OK button will be deleted, and if
122 * the indicator bool is false, the indicator will be deleted.
123 *
124 * \param[in] draw true on draw, false on delete
125 * \param[in] indicator true on draw indicator, false on delete
126 */
gfx_mono_spinctrl_draw_button(bool draw,bool indicator)127 static void gfx_mono_spinctrl_draw_button(bool draw, bool indicator)
128 {
129 uint8_t width;
130 uint8_t height;
131 uint8_t offset;
132 char string_buf[22];
133
134 /* Clear bottom line */
135 gfx_mono_draw_filled_rect(0,
136 (SYSFONT_HEIGHT + 1) *
137 GFX_MONO_SPINCTRL_MAX_ELEMENTS_IN_SPINCOLLECTION,
138 GFX_MONO_LCD_WIDTH, SYSFONT_HEIGHT, GFX_PIXEL_CLR);
139
140 snprintf(string_buf, sizeof(string_buf), "OK");
141 gfx_mono_get_string_bounding_box(string_buf, &sysfont, &width, &height);
142 offset = (GFX_MONO_LCD_WIDTH - width) / 2;
143
144 if (draw) {
145 /* Draw OK button in the middle of the last line */
146 gfx_mono_draw_string(string_buf, offset,
147 (SYSFONT_HEIGHT + 1) *
148 GFX_MONO_SPINCTRL_MAX_ELEMENTS_IN_SPINCOLLECTION,
149 &sysfont);
150 if (indicator) {
151 /* Draw indicator arrow in front of button */
152 gfx_mono_put_bitmap(&gfx_mono_spinctrl_bitmap_indicator,
153 offset - GFX_MONO_SPINCTRL_INDICATOR_WIDTH,
154 (SYSFONT_HEIGHT + 1) *
155 GFX_MONO_SPINCTRL_MAX_ELEMENTS_IN_SPINCOLLECTION);
156 } else {
157 /* Delete indicator */
158 gfx_mono_draw_filled_rect(offset -
159 GFX_MONO_SPINCTRL_INDICATOR_WIDTH,
160 (SYSFONT_HEIGHT + 1) *
161 GFX_MONO_SPINCTRL_MAX_ELEMENTS_IN_SPINCOLLECTION,
162 GFX_MONO_SPINCTRL_INDICATOR_WIDTH,
163 GFX_MONO_SPINCTRL_INDICATOR_HEIGHT,
164 GFX_PIXEL_CLR);
165 }
166 } else {
167 /* Delete OK button */
168 gfx_mono_draw_filled_rect(
169 offset - GFX_MONO_SPINCTRL_INDICATOR_WIDTH,
170 (SYSFONT_HEIGHT + 1) *
171 GFX_MONO_SPINCTRL_MAX_ELEMENTS_IN_SPINCOLLECTION, 20,
172 SYSFONT_HEIGHT, GFX_PIXEL_CLR);
173 }
174 }
175
176 /**
177 * \brief Draw spinner at its position.
178 *
179 * This function draws a spinner at its position.
180 * The title of the spinner is only drawn if the redraw option is set.
181 * If the spinner is in focus, arrows will be drawn next to the spinner value
182 * to indicate that it is spinnable.
183 *
184 * \param[in] spinner pointer to initialized gfx_mono_spinctrl struct
185 * \param[in] redraw true if title of spinner should be drawn
186 */
gfx_mono_spinctrl_draw(struct gfx_mono_spinctrl * spinner,bool redraw)187 void gfx_mono_spinctrl_draw(struct gfx_mono_spinctrl *spinner, bool redraw)
188 {
189 char string_buf[GFX_MONO_SPINCTRL_INT_SPINNER_WIDTH];
190 uint8_t index;
191 uint8_t offset;
192
193 if (redraw) {
194 /* Clear line */
195 gfx_mono_draw_filled_rect(0, spinner->y, GFX_MONO_LCD_WIDTH,
196 SYSFONT_HEIGHT, GFX_PIXEL_CLR);
197 /* Draw title */
198 gfx_mono_draw_progmem_string((char PROGMEM_PTR_T)spinner->title,
199 GFX_MONO_SPINCTRL_INDICATOR_WIDTH + 1,
200 spinner->y, &sysfont);
201 }
202
203 if (spinner->in_focus) {
204 gfx_mono_spinctrl_draw_spin_indicator(spinner, true);
205 } else {
206 gfx_mono_spinctrl_draw_spin_indicator(spinner, false);
207 }
208
209 if (spinner->datatype == SPINTYPE_INTEGER) {
210 offset = GFX_MONO_LCD_WIDTH -
211 (SYSFONT_WIDTH *
212 GFX_MONO_SPINCTRL_INT_SPINNER_WIDTH);
213 snprintf(string_buf, sizeof(string_buf), "%d",
214 spinner->integer_data);
215 /* Delete previous spinner data */
216 gfx_mono_draw_filled_rect(offset, spinner->y,
217 GFX_MONO_LCD_WIDTH - offset -
218 GFX_MONO_SPINCTRL_SPIN_INDICATOR_WIDTH,
219 SYSFONT_HEIGHT,
220 GFX_PIXEL_CLR);
221 /* Draw integer data */
222 gfx_mono_draw_string(string_buf, offset, spinner->y, &sysfont);
223 } else if (spinner->datatype == SPINTYPE_STRING) {
224 index = spinner->strings.index;
225 offset = GFX_MONO_LCD_WIDTH -
226 (SYSFONT_WIDTH *
227 GFX_MONO_SPINCTRL_STRING_SPINNER_WIDTH);
228
229 /* Delete previous spinner data */
230 gfx_mono_draw_filled_rect(offset, spinner->y,
231 GFX_MONO_LCD_WIDTH - offset -
232 GFX_MONO_SPINCTRL_SPIN_INDICATOR_WIDTH,
233 SYSFONT_HEIGHT,
234 GFX_PIXEL_CLR);
235
236 /* Draw string data */
237 gfx_mono_draw_progmem_string(
238 (char PROGMEM_PTR_T)spinner->strings.data[index], offset,
239 spinner->y, &sysfont);
240 }
241 }
242
243 /**
244 * \brief Initialize a spinner.
245 *
246 * This function initializes a spinner to either
247 * \ref gfx_mono_spinctrl_type_t "SPINTYPE_STRING" spinner that spins through
248 * the strings in a \ref PROGMEM_STRING_T or a
249 * \ref gfx_mono_spinctrl_type_t "SPINTYPE_INTEGER" spinner that spins through
250 * integers.
251 * If the spinner type is SPINTYPE_INTEGER, it will spin from lower_limit to
252 * upper_limit.
253 * If the spinner type is SPINTYPE_STRING, a pointer to a PROGMEM_STRING_T must
254 * be provided, and the spinner will spin through the strings starting at
255 * index lower_limit ending at index upper_limit.
256 * The y parameter specifies where to place the spinner on the screen, but is
257 * overwritten if the spinner is put in a spincollection.
258 *
259 * \param[out] spinner pointer to initialized gfx_mono_spinctrl struct
260 * \param[in] datatype typer of spinner, integer or string
261 * \param[in] title title of spinner
262 * \param[in] data pointer to progmem string array if datatype is string.
263 * NULL if datatype is integer.
264 * \param[in] lower_limit lower limit and start value of spinner's data
265 * \param[in] upper_limit upper limit of spinner's data
266 * \param[in] y y position of spinner
267 */
gfx_mono_spinctrl_init(struct gfx_mono_spinctrl * spinner,gfx_mono_spinctrl_type_t datatype,PROGMEM_STRING_T title,PROGMEM_STRING_T * data,int16_t lower_limit,int16_t upper_limit,gfx_coord_t y)268 void gfx_mono_spinctrl_init(struct gfx_mono_spinctrl *spinner,
269 gfx_mono_spinctrl_type_t datatype, PROGMEM_STRING_T title,
270 PROGMEM_STRING_T *data, int16_t lower_limit,
271 int16_t upper_limit,
272 gfx_coord_t y)
273 {
274 /* Initialization of spinner parameters */
275 spinner->title = title;
276 spinner->datatype = datatype;
277 spinner->lower_limit = lower_limit;
278 spinner->upper_limit = upper_limit;
279 spinner->y = y;
280 spinner->in_focus = false;
281 spinner->last_saved_value = spinner->lower_limit;
282
283 if (datatype == SPINTYPE_STRING) {
284 spinner->strings.data = data;
285 spinner->strings.index = lower_limit;
286 } else {
287 spinner->integer_data = lower_limit;
288 }
289 }
290
291 /**
292 * \brief Initialize a spincollection.
293 *
294 * This function initializes a spincollection to which spinners can be added.
295 *
296 * \param[out] collection pointer to gfx_mono_spinctrl_spincollection to Initialize
297 */
gfx_mono_spinctrl_spincollection_init(struct gfx_mono_spinctrl_spincollection * collection)298 void gfx_mono_spinctrl_spincollection_init(struct
299 gfx_mono_spinctrl_spincollection *collection)
300 {
301 collection->active_spinner = false;
302 collection->current_selection = 0;
303 collection->number_of_spinners = 0;
304 collection->init = true;
305 }
306
307 /**
308 * \brief Add spinner to spincollection
309 *
310 * This function adds an initialized spinner to a spincollection and positions
311 * it below any other spinners in the spincollection on the screen. The
312 * spinners in the spincollection are linked together and number of spinners is
313 * updated.
314 * It is not possible to add more spinners than
315 * \ref GFX_MONO_SPINCTRL_MAX_ELEMENTS_IN_SPINCOLLECTION "maximum number of
316 * spinners in a spincollection".
317 *
318 * \param[in] spinner pointer to initialized gfx_mono_spinctrl struct to add
319 * to collection
320 * \param[in] spinners pointer to initialized gfx_mono_spinctrl_spincollection
321 * struct
322 *
323 */
gfx_mono_spinctrl_spincollection_add_spinner(struct gfx_mono_spinctrl * spinner,struct gfx_mono_spinctrl_spincollection * spinners)324 void gfx_mono_spinctrl_spincollection_add_spinner(struct
325 gfx_mono_spinctrl *spinner,
326 struct gfx_mono_spinctrl_spincollection *spinners)
327 {
328 uint8_t i;
329 struct gfx_mono_spinctrl *lastspinner;
330
331 /* Do not add more spinner elements than maximum number of spinners */
332 if (spinners->number_of_spinners >=
333 GFX_MONO_SPINCTRL_MAX_ELEMENTS_IN_SPINCOLLECTION) {
334 return;
335 }
336
337 /* Place new spinner below previous spinners on screen */
338 spinner->y = (SYSFONT_HEIGHT + 1) * spinners->number_of_spinners;
339
340 /* Add pointer to the spinner in spincollection if empty */
341 if (spinners->number_of_spinners == 0) {
342 spinners->collection = spinner;
343 } else {
344 lastspinner = spinners->collection;
345 for (i = 1; i < spinners->number_of_spinners; i++) {
346 lastspinner = lastspinner->next;
347 }
348 /* Link the new spinner to the current last spinner in the
349 * collection */
350 lastspinner->next = spinner;
351 /* Link the current last spinner as previous spinner for new
352 * spinner */
353 spinner->prev = lastspinner;
354 }
355
356 /* Set added spinner as last spinner in collection */
357 spinners->collection_last = spinner;
358 /* Update number of spinners in collection */
359 spinners->number_of_spinners++;
360 }
361
362 /**
363 * \brief Show spincollection
364 *
365 * This function draws all the spinners in a spincollection to the screen,
366 * together with an OK button at the bottom. It also draws an indicator arrow
367 * in front of the top spinner.
368 *
369 * \param[in] spinners pointer to initialized spincollection to display
370 */
gfx_mono_spinctrl_spincollection_show(struct gfx_mono_spinctrl_spincollection * spinners)371 void gfx_mono_spinctrl_spincollection_show(struct
372 gfx_mono_spinctrl_spincollection *spinners)
373 {
374 uint8_t i;
375 struct gfx_mono_spinctrl *iterator;
376
377 /* Clear screen */
378 gfx_mono_draw_filled_rect(0, 0, GFX_MONO_LCD_WIDTH, GFX_MONO_LCD_HEIGHT,
379 GFX_PIXEL_CLR);
380
381 /* Make sure there are spinners in the collection */
382 if (spinners->number_of_spinners == 0) {
383 return;
384 }
385
386 /* Draw spinners on screen */
387 iterator = spinners->collection;
388 for (i = 0; i < spinners->number_of_spinners; i++) {
389 gfx_mono_spinctrl_draw(iterator, true);
390 iterator = iterator->next;
391 }
392 /* Draw OK button at bottom of screen */
393 gfx_mono_spinctrl_draw_button(true, false);
394 /* Draw indicator arrow in front of first spinner */
395 gfx_mono_spinctrl_draw_indicator(spinners->collection, true);
396 }
397
398 /**
399 * \brief Step up spinner data
400 *
401 * This function steps up the data, making sure it does not go
402 * beyond the upper limit. Wraps around if it does.
403 *
404 * \param[in] spinner pointer to initialized spinner.
405 */
gfx_mono_spinctrl_step_up(struct gfx_mono_spinctrl * spinner)406 static void gfx_mono_spinctrl_step_up(struct gfx_mono_spinctrl *spinner)
407 {
408 /* Check if spinner type is integer or string, increment integer data or
409 * move to next string index.
410 */
411 if (spinner->datatype == SPINTYPE_INTEGER) {
412 if (spinner->integer_data < spinner->upper_limit) {
413 spinner->integer_data++;
414 } else {
415 spinner->integer_data = spinner->lower_limit;
416 }
417 } else if (spinner->datatype == SPINTYPE_STRING) {
418 if (spinner->strings.index < spinner->upper_limit) {
419 spinner->strings.index++;
420 } else {
421 (spinner->strings.index) = spinner->lower_limit;
422 }
423 }
424 }
425
426 /**
427 * \brief Step down spinner data
428 *
429 * * This function steps down the data, making sure it does not go
430 * below the lower limit. Wraps around if it does.
431 *
432 * \param[in] spinner pointer to initialized spinner.
433 */
gfx_mono_spinctrl_step_down(struct gfx_mono_spinctrl * spinner)434 static void gfx_mono_spinctrl_step_down(struct gfx_mono_spinctrl *spinner)
435 {
436 /* Check if spinner type is integer or string, increment integer data,
437 * move to next string index.
438 */
439 if (spinner->datatype == SPINTYPE_INTEGER) {
440 if (spinner->integer_data > spinner->lower_limit) {
441 spinner->integer_data--;
442 } else {
443 spinner->integer_data = spinner->upper_limit;
444 }
445 } else if (spinner->datatype == SPINTYPE_STRING) {
446 if (spinner->strings.index > (spinner->lower_limit)) {
447 spinner->strings.index--;
448 } else {
449 spinner->strings.index = spinner->upper_limit;
450 }
451 }
452 }
453
454 /**
455 * \brief Update single spinner depending on input.
456 *
457 * \param[in] spinner pointer to initialized spinner.
458 * \param[in] keycode keycode to process
459 *
460 * \retval selected selected spinner value
461 * \retval GFX_MONO_SPINCTRL_EVENT_IDLE spinner spinning
462 * \retval GFX_MONO_SPINCTRL_EVENT_BACK spinner deselected
463 */
gfx_mono_spinctrl_process_key(struct gfx_mono_spinctrl * spinner,uint8_t keycode)464 int16_t gfx_mono_spinctrl_process_key(struct gfx_mono_spinctrl *spinner,
465 uint8_t keycode)
466 {
467 switch (keycode) {
468 case GFX_MONO_SPINCTRL_KEYCODE_DOWN:
469 if (spinner->in_focus) {
470 gfx_mono_spinctrl_step_down(spinner);
471 /* Update spinner on display */
472 gfx_mono_spinctrl_draw(spinner, false);
473 }
474
475 /* Nothing selected yet */
476 return GFX_MONO_SPINCTRL_EVENT_IDLE;
477
478 case GFX_MONO_SPINCTRL_KEYCODE_UP:
479 if (spinner->in_focus) {
480 gfx_mono_spinctrl_step_up(spinner);
481 /* Update spinner on display */
482 gfx_mono_spinctrl_draw(spinner, false);
483 }
484
485 /* Nothing selected yet */
486 return GFX_MONO_SPINCTRL_EVENT_IDLE;
487
488 case GFX_MONO_SPINCTRL_KEYCODE_ENTER:
489 if (spinner->in_focus) {
490 if (spinner->datatype == SPINTYPE_INTEGER) {
491 spinner->in_focus = false;
492 gfx_mono_spinctrl_draw(spinner, false);
493 /* Store saved value in case of aborting spinner
494 * later */
495 spinner->last_saved_value
496 = spinner->integer_data;
497 /* Got what we want. Return selection. */
498 return spinner->integer_data;
499 } else if (spinner->datatype == SPINTYPE_STRING) {
500 spinner->in_focus = false;
501 gfx_mono_spinctrl_draw(spinner, false);
502 /* Store saved value in case of aborting spinner
503 * later */
504 spinner->last_saved_value
505 = spinner->strings.index;
506 /* Got what we want. Return selection. */
507 return spinner->strings.index;
508 }
509 } else {
510 /* Spinner selected */
511 spinner->in_focus = true;
512 gfx_mono_spinctrl_draw(spinner, false);
513 return GFX_MONO_SPINCTRL_EVENT_IDLE;
514 }
515
516 case GFX_MONO_SPINCTRL_KEYCODE_BACK:
517 /* User pressed "back" key, */
518 spinner->in_focus = false;
519 /* Spinner choice aborted, show last saved value instead */
520 if (spinner->datatype == SPINTYPE_INTEGER) {
521 spinner->integer_data = spinner->last_saved_value;
522 } else if (spinner->datatype == SPINTYPE_STRING) {
523 spinner->strings.index = spinner->last_saved_value;
524 }
525
526 gfx_mono_spinctrl_draw(spinner, false);
527 return GFX_MONO_SPINCTRL_EVENT_BACK;
528
529 default:
530 /* Unknown key event */
531 return GFX_MONO_SPINCTRL_EVENT_IDLE;
532 }
533 }
534
535 /**
536 * \brief Update spincollection on screen depending on input
537 *
538 * This function returns \ref GFX_MONO_SPINCTRL_EVENT_FINISH if user has
539 * pressed the OK button. The spinner choices can then be extracted from the
540 * results array. If a spinner is of type SPINTYPE_STRING, the index of the
541 * progmem string will be stored in the results array, else the selected
542 * integer value will be stored.
543 * The choice from the first spinner added to the spincollection will be stored
544 * at index 0 in the results array, the second at index 1 and so on.
545 * If user has pressed the back button, \ref GFX_MONO_SPINCTRL_EVENT_BACK is
546 * returned, signalling that the application should be cancelled.
547 *
548 * \param[in] spinners pointer to initialized
549 * gfx_mono_spinctrl_spincollection
550 * \param[in] keycode keycode to process
551 * \param[in] results array to store results from the spinners, must be of
552 * same length as number of spinners
553 *
554 * \retval GFX_MONO_SPINCTRL_EVENT_FINISH user pressed ok button
555 * \retval GFX_MONO_SPINCTRL_EVENT_BACK user cancelled
556 * \retval GFX_MONO_SPINCTRL_EVENT_IDLE user is navigating in spincollection
557 */
gfx_mono_spinctrl_spincollection_process_key(struct gfx_mono_spinctrl_spincollection * spinners,uint8_t keycode,int16_t results[])558 int16_t gfx_mono_spinctrl_spincollection_process_key(struct
559 gfx_mono_spinctrl_spincollection *spinners, uint8_t keycode,
560 int16_t results[])
561 {
562 uint8_t i;
563 struct gfx_mono_spinctrl *iterator;
564
565 /* Make sure there are spinners in the collection, if not, cancel */
566 if (spinners->number_of_spinners == 0) {
567 return GFX_MONO_SPINCTRL_EVENT_BACK;
568 }
569
570 /* Store initial values in results array first time function is run */
571 if (spinners->init) {
572 iterator = spinners->collection;
573 for (i = 0; i < spinners->number_of_spinners; i++) {
574 if (iterator->datatype == SPINTYPE_INTEGER) {
575 results[i] = iterator->integer_data;
576 } else {
577 results[i] = iterator->strings.index;
578 }
579
580 iterator = iterator->next;
581 }
582 spinners->init = false;
583 }
584
585 /* Find current spinner selection */
586 iterator = spinners->collection;
587 if (spinners->current_selection != GFX_MONO_SPINCTRL_BUTTON) {
588 for (i = 0; i < spinners->current_selection; i++) {
589 iterator = iterator->next;
590 }
591 }
592
593 if (spinners->active_spinner) {
594 /* Process chosen spinner */
595 spinners->selection = gfx_mono_spinctrl_process_key(iterator,
596 keycode);
597 if (spinners->selection == GFX_MONO_SPINCTRL_EVENT_BACK) {
598 /* User has exited spinner without saving the result */
599 spinners->active_spinner = false;
600 } else if (spinners->selection !=
601 GFX_MONO_SPINCTRL_EVENT_IDLE) {
602 /* Value selected, store in array */
603 results[spinners->current_selection]
604 = spinners->selection;
605 /* Step out of spinner and into spincollection */
606 spinners->active_spinner = false;
607 }
608
609 return GFX_MONO_SPINCTRL_EVENT_IDLE;
610 } else {
611 switch (keycode) {
612 case GFX_MONO_SPINCTRL_KEYCODE_DOWN:
613 if (spinners->current_selection ==
614 GFX_MONO_SPINCTRL_BUTTON) {
615 spinners->current_selection = 0;
616 /* Delete indicator arrow in front of button */
617 gfx_mono_spinctrl_draw_button(true, false);
618 /* Draw indicator arrow in front of first
619 * spinner */
620 gfx_mono_spinctrl_draw_indicator(iterator,
621 true);
622 } else if (spinners->current_selection <
623 spinners->number_of_spinners - 1) {
624 /* Delete indicator arrow */
625 gfx_mono_spinctrl_draw_indicator(iterator,
626 false);
627 spinners->current_selection++;
628 /* Draw indicator arrow in front of new spinner */
629 gfx_mono_spinctrl_draw_indicator(iterator->next,
630 true);
631 } else {
632 /* Delete indicator arrow */
633 gfx_mono_spinctrl_draw_indicator(iterator,
634 false);
635 spinners->current_selection
636 = GFX_MONO_SPINCTRL_BUTTON;
637 /* Draw indicator arrow in front of button */
638 gfx_mono_spinctrl_draw_button(true, true);
639 }
640
641 return GFX_MONO_SPINCTRL_EVENT_IDLE;
642
643 case GFX_MONO_SPINCTRL_KEYCODE_UP:
644 if (spinners->current_selection ==
645 GFX_MONO_SPINCTRL_BUTTON) {
646 /* Delete indicator arrow in front of button */
647 gfx_mono_spinctrl_draw_button(true, false);
648 spinners->current_selection
649 = spinners->number_of_spinners - 1;
650 /* Draw indicator arrow in front of new spinner */
651 gfx_mono_spinctrl_draw_indicator(
652 spinners->collection_last,
653 true);
654 } else if (spinners->current_selection > 0) {
655 /* Delete indicator arrow */
656 gfx_mono_spinctrl_draw_indicator(iterator,
657 false);
658 spinners->current_selection--;
659 /* Draw indicator arrow in front of new spinner */
660 gfx_mono_spinctrl_draw_indicator(iterator->prev,
661 true);
662 } else {
663 /* Delete indicator arrow */
664 gfx_mono_spinctrl_draw_indicator(iterator,
665 false);
666 spinners->current_selection
667 = GFX_MONO_SPINCTRL_BUTTON;
668 /* Draw indicator arrow in front of button */
669 gfx_mono_spinctrl_draw_button(true, true);
670 }
671
672 return GFX_MONO_SPINCTRL_EVENT_IDLE;
673
674 case GFX_MONO_SPINCTRL_KEYCODE_ENTER:
675 if (spinners->current_selection ==
676 GFX_MONO_SPINCTRL_BUTTON) {
677 /* Finished with all selections, return */
678 return GFX_MONO_SPINCTRL_EVENT_FINISH;
679 } else {
680 /* Spinner selected, send next keycode directly
681 * to spinner */
682 gfx_mono_spinctrl_process_key(iterator,
683 keycode);
684 spinners->active_spinner = true;
685 return GFX_MONO_SPINCTRL_EVENT_IDLE;
686 }
687
688 case GFX_MONO_SPINCTRL_KEYCODE_BACK:
689 /* User pressed "back" key, */
690 return GFX_MONO_SPINCTRL_EVENT_BACK;
691
692 default:
693 /* Unknown key event */
694 return GFX_MONO_SPINCTRL_EVENT_IDLE;
695 }
696 }
697 }
698