From: Wintermute Date: Sun, 20 Oct 2013 19:02:23 +0000 (+0200) Subject: MenĂ¼. X-Git-Url: https://git.rohieb.name/hackover2013-badge-firmware.git/commitdiff_plain/c63dbfcb347e10f9406053545eaf44dd4d9d9bdb MenĂ¼. --- diff --git a/badge/ui/menu.c b/badge/ui/menu.c new file mode 100644 index 0000000..e4eaac7 --- /dev/null +++ b/badge/ui/menu.c @@ -0,0 +1,99 @@ +#include "menu.h" + +#include "event.h" +#include "font.h" +#include "sprite.h" + +#include + +enum { + MENU_ARROW_UP, + MENU_ARROW_DOWN +}; + +static badge_sprite const arrows[] = { + { 5, 7, (uint8_t const *) "\x04\xc3\xdf\x40" }, + { 5, 7, (uint8_t const *) "\x10\xd8\x1f\x06\x01" } +}; + +enum { + MENU_MARGIN_TOP = 2, + MENU_MARGIN_BOTTOM = 3, + MENU_MARGIN_LEFT = 3, + MENU_ENTRIES_HEIGHT = 1 + BADGE_FONT_HEIGHT, + MENU_ENTRIES_VISIBLE = (BADGE_DISPLAY_HEIGHT - MENU_MARGIN_TOP - MENU_MARGIN_BOTTOM) / MENU_ENTRIES_HEIGHT +}; + +static void badge_menu_show(char const *const *menu, + size_t n, + size_t first_visible, + size_t selected) +{ + badge_framebuffer fb = { { { 0 } } }; + bool arrow_up = true; + bool arrow_down = true; + + size_t first_used_row = 0; + size_t used_rows = MENU_ENTRIES_VISIBLE; + + if(n <= MENU_ENTRIES_VISIBLE) { + first_visible = 0; + used_rows = n; + first_used_row = (MENU_ENTRIES_VISIBLE - used_rows) / 2; + } + + if(first_visible == 0) { + arrow_up = false; + } + + if(first_visible + MENU_ENTRIES_VISIBLE >= n) { + arrow_down = false; + } + + for(size_t i = 0; i < used_rows; ++i) { + badge_framebuffer_render_text(&fb, + (int8_t) (MENU_MARGIN_LEFT + BADGE_FONT_WIDTH), + (int8_t) (MENU_MARGIN_TOP + (first_used_row + i) * MENU_ENTRIES_HEIGHT), + menu[first_visible + i]); + } + + badge_framebuffer_render_char(&fb, MENU_MARGIN_LEFT, MENU_MARGIN_TOP + MENU_ENTRIES_HEIGHT * (selected - first_visible + first_used_row), '*'); + if(arrow_up ) { badge_framebuffer_blt(&fb, MENU_MARGIN_LEFT, MENU_MARGIN_TOP, &arrows[MENU_ARROW_UP ], 0); } + if(arrow_down) { badge_framebuffer_blt(&fb, MENU_MARGIN_LEFT, MENU_MARGIN_TOP + (MENU_ENTRIES_VISIBLE - 1) * MENU_ENTRIES_HEIGHT, &arrows[MENU_ARROW_DOWN], 0); } + + badge_framebuffer_flush(&fb); +} + +size_t badge_menu(char const *const *menu, + size_t n, + size_t first_visible, + size_t selected) +{ + for(;;) { + badge_menu_show(menu, n, first_visible, selected); + + badge_event_t ev; + + do { + ev = badge_event_wait(); + } while(badge_event_type(ev) != BADGE_EVENT_USER_INPUT); + + uint8_t old_state = badge_event_old_input_state(ev); + uint8_t new_state = badge_event_new_input_state(ev); + uint8_t new_buttons = new_state & (old_state ^ new_state); + + if(new_buttons & (BADGE_EVENT_KEY_BTN_A | BADGE_EVENT_KEY_BTN_B)) { + return selected; + } else if((new_buttons & BADGE_EVENT_KEY_UP ) && selected != 0) { + --selected; + if(first_visible != 0 && selected <= first_visible) { + first_visible = selected - 1; + } + } else if(new_buttons & BADGE_EVENT_KEY_DOWN && selected + 1 < n) { + ++selected; + if(first_visible + (MENU_ENTRIES_VISIBLE - 2 + (selected + 1 == n)) < selected) { + first_visible = selected - (MENU_ENTRIES_VISIBLE - 2 + (selected + 1 == n)); + } + } + } +} diff --git a/badge/ui/menu.h b/badge/ui/menu.h new file mode 100644 index 0000000..712618d --- /dev/null +++ b/badge/ui/menu.h @@ -0,0 +1,12 @@ +#ifndef INCLUDED_BADGE_UI_MENU_H +#define INCLUDED_BADGE_UI_MENU_H + +#include +#include + +size_t badge_menu(char const *const * menu, + size_t n, + size_t first_visible, + size_t preselected); + +#endif diff --git a/mock/Makefile b/mock/Makefile index 969b2b4..de9d367 100644 --- a/mock/Makefile +++ b/mock/Makefile @@ -17,6 +17,7 @@ BADGE_CXXSRCS = mock-main.cc \ BADGE_CSRCS = badge_main_loop.c BADGE_FAROBJS = ui/font.o \ + ui/menu.o \ ui/sprite.o \ jumpnrun/collision.o \ jumpnrun/enemies.o \ diff --git a/mock/badge_main_loop.c b/mock/badge_main_loop.c index 0f8b880..3a1c3d2 100644 --- a/mock/badge_main_loop.c +++ b/mock/badge_main_loop.c @@ -3,6 +3,7 @@ #include "jumpnrun/jumpnrun.h" #include "ui/display.h" #include "ui/event.h" +#include "ui/menu.h" #include "ui/sprite.h" #include @@ -11,6 +12,23 @@ #include void badge_main_loop(void) { + char const *const menu[] = { + "smb", + "skynet", + "wrongturn", + "lubiXOXO", + "lubilove", + "gnobbel", + "foo", + "mean", + "xyzzy", + "abc", + "nonsense" + }; + + size_t choice = badge_menu(menu, ARRAY_SIZE(menu), 0, 0); + + printf("%zu\n", choice); for(;;) { FILE *fd = fopen("../badge/jumpnrun/levels.txt", "r");