2. Core Menu Mechanics

The main norns interface is based around a simple menu selection idea. These components are implemented here.

2.1. typedef + struct

<<norns_typedefs>>=
typedef struct norns_menu norns_menu;
<<norns_menu_item>>
<<norns_structs>>=
struct norns_menu {
<<norns_menu_contents>>
};

2.2. Initialization

The norns menu is initialized with norns_menu_init. Since it doesn't allocate anything internally, no cleanup is required.

<<norns_funcdefs>>=
void norns_menu_init(norns_menu *menu,
                     const char *name,
                     norns_videobuf *buf,
                     norns_menu_item *items,
                     int nitems,
                     norns_poll_d *poll,
                     void *ud);
<<norns_functions>>=
void norns_menu_init(norns_menu *menu,
                     const char *name,
                     norns_videobuf *buf,
                     norns_menu_item *items,
                     int nitems,
                     norns_poll_d *poll,
                     void *ud)
{
<<norns_menu_init>>
    norns_menu_callbacks(menu, poll);
}
<<norns_funcdefs>>=
static void norns_menu_callbacks(norns_menu *menu,
                                 norns_poll_d *poll);
<<norns_functions>>=
static void norns_menu_callbacks(norns_menu *menu,
                                 norns_poll_d *poll)
{
<<norns_menu_callbacks>>
}
<<norns_menu_init>>=
<<norns_menu_init_reinit>>

2.3. Re-Initialization

<<norns_funcdefs>>=
void norns_menu_reinit(norns_menu *menu,
                       const char *name,
                       norns_menu_item *items,
                       int nitems,
                       void *ud);
<<norns_functions>>=
void norns_menu_reinit(norns_menu *menu,
                       const char *name,
                       norns_menu_item *items,
                       int nitems,
                       void *ud)
{
<<norns_menu_init_reinit>>
}

2.4. Core Menu Data

2.4.1. DONE Video Buffer

CLOSED: [2019-11-22 Fri 15:04] A norns_videobuf instance is supplied to at runtime. This is what the norns will draw to.

<<norns_menu_contents>>=
norns_videobuf *buf;
<<norns_menu_init>>=
menu->buf = buf;

2.4.2. DONE Menu Name

CLOSED: [2019-11-22 Fri 15:04] Every menu has a "name", displayed at the top of the screen

<<norns_menu_contents>>=
const char *name;
<<norns_menu_init_reinit>>=
menu->name = name;

2.4.3. DONE Menu Items

CLOSED: [2019-11-22 Fri 15:04] Every menu has an array of zero or more items. This is assigned at initialization, and must be allocated externally. The size must be known as well.

<<norns_menu_contents>>=
norns_menu_item *items;
int nitems;
<<norns_menu_init_reinit>>=
menu->items = items;
menu->nitems = nitems;

2.4.4. DONE Selected Item

CLOSED: [2019-11-22 Fri 15:04] Selected item starts at 1, so that 0 means nothing is selected. It is zero by default.

<<norns_menu_contents>>=
int selected;
<<norns_menu_init_reinit>>=
menu->selected = 0;

2.4.5. User Data

<<norns_menu_contents>>=
void *ud;
<<norns_menu_init>>=
menu->ud = ud;

2.4.6. Counter + Speed

Used to limit the speed of the knob turn. The weight variable changes the speed of this knob. The larger the value, the slower it gets.

<<norns_menu_contents>>=
int counter;
int weight;
<<norns_menu_init>>=
menu->counter = 0;
menu->weight = 2;

2.4.7. Offset

Display offset, for when values go over.

<<norns_menu_contents>>=
int offset;
<<norns_menu_init_reinit>>=
menu->offset = 0;

2.5. A single menu item

A menu item has a "name", and an action of what to do when selected. Presumably, these are stored in a constant, which is why the struct is exposed in the header.

<<norns_menu_item>>=
typedef struct {
    const char *name;
    void (*select)(norns_menu *, int);
} norns_menu_item;

2.6. Drawing the Menu

2.6.1. DONE Top Level Draw

CLOSED: [2019-11-22 Fri 14:59] Call this to (re)draw the menu to the video buffer. This does not copy to the framebuffer.

<<norns_funcdefs>>=
void norns_menu_draw(norns_menu *menu);
<<norns_functions>>=
void norns_menu_draw(norns_menu *menu)
{
    norns_videobuf *vb;

    vb = menu->buf;
    norns_videobuf_clear(vb);
<<draw_the_header>>
<<draw_menu_items>>
}

2.6.2. DONE Write the header

CLOSED: [2019-11-22 Fri 21:08] Write the header at the top. Write a line separting the header and the menu items.

<<norns_funcdefs>>=
static void norns_menu_header(norns_menu *menu);
<<norns_functions>>=
static void norns_menu_header(norns_menu *menu)
{
    int i;
    norns_draw_string(menu->buf,
                    0, 0,
                    0xff, 0x00,
                    menu->name);
    for (i = 0; i < 128; i++) {
        norns_videobuf_write(menu->buf, i, 11, 0xff);
    }
}
<<draw_the_header>>=
norns_menu_header(menu);

2.6.3. DONE Write items

CLOSED: [2019-11-22 Fri 21:14] Draw the items based on the current top position. Make sure the currently selected item is inverted. (black text on white instead of white text on black).

<<draw_menu_items>>=
{
    int i;
    int item;
    int x, y;
    int selected;
    unsigned char bg;
    unsigned char fg;
    int nrows;

    selected = menu->selected;

    nrows = menu->nitems - menu->offset;

    if (nrows > 6) nrows = 6;
    if (nrows < 0) nrows = 0;

    for (i = 0; i < nrows; i++) {
        item = menu->offset + i;
        if ((item + 1) == selected) {
            fg = 0x00;
            bg = 0xff;
            for (y = 0; y < 10; y++) {
                for (x = 0; x < 128; x++) {
                    norns_videobuf_write(menu->buf,
                                         x, (14 + 9*i) + y,
                                         0xff);
                }
            }
        } else {
            fg = 0xff;
            bg = 0x00;
        }
        norns_draw_string(vb,
                        0, 15 + 9*i,
                        fg, bg,
                        menu->items[item].name);
    }
}

2.7. Peripheral callbacks

2.7.1. DONE Setting Peripheral Callbacks

CLOSED: [2019-11-23 Sat 13:01]

<<norns_menu_callbacks>>=
{
<<set_key_callback>>
<<set_knob_callback>>
}

2.7.2. DONE Key

CLOSED: [2019-11-23 Sat 13:01] Key 0 is used as a selection button.

<<norns_static_funcdefs>>=
static void menu_key(void *ud, int state);
<<norns_functions>>=
static void menu_key(void *ud, int state)
{
    norns_menu *menu;
    int selected;

    if (state == 0) return;
    menu = ud;

    selected = menu->selected;

    if (selected > 0) {
        void (*f)(norns_menu *, int);
        selected--;
        f = menu->items[selected].select;
        if (f != NULL) {
            f(menu, selected);
        }
    }
}
<<set_key_callback>>=
norns_poll_cb_key(poll, 1, menu_key, menu);

2.7.3. DONE Knob

CLOSED: [2019-11-23 Sat 13:01] Knob is used to scroll up + down the menu.

<<norns_static_funcdefs>>=
static void menu_knob(void *ud, int pos);
<<norns_functions>>=
void monolith_norns_draw(monolith_d *m);
static void menu_knob(void *ud, int pos)
{
    norns_menu *menu;

    menu = ud;

    norns_menu_step(menu, pos);
    norns_menu_draw(menu);

    monolith_norns_draw(monolith_data_get());
}
<<norns_funcdefs>>=
static void norns_menu_step(norns_menu *menu, int pos);
<<norns_functions>>=
static void norns_menu_step(norns_menu *menu, int pos)
{
    int selected;
    int offset;

    menu->counter = (menu->counter + 1) % menu->weight;

    if (menu->counter != 0) return;

    selected = menu->selected;
    offset = menu->offset;
    selected += pos;

    if (selected < 1) {
        selected = 1;
        offset = 0;
    }

    if (selected > menu->nitems) {
        selected = menu->nitems;
    }

    if ((selected - offset) > 5) {
        offset++;
    } else if ((selected - offset) <= 0) {
        offset--;
    }

    menu->selected = selected;
    menu->offset = offset;
}
<<set_knob_callback>>=
norns_poll_cb_knob(poll, 1, menu_knob, menu);



prev | home | next