6. Additional Menu Items (via Janet)
In order to promote additional functionality, such as loading/saving state data, a customizable menu will be created. This functionality will allow additional items to appear in their own submenu, which can be selected from the main menu.
This aux menu is populated with a Janet array. This array is
stored as a reference in the C struct, and must be set via
a janet function monolith/norns-menu-aux-set
.
JanetArray *aux_items;
menu->aux_items = NULL;
{
"monolith/norns-menu-aux-set",
j_norns_menu_aux_set,
"Sets the array."
},
static Janet j_norns_menu_aux_set(int32_t argc, Janet *argv)
{
const char *str;
monolith_d *m;
monolith_dict *dict;
monolith_page *pg;
int rc;
JanetArray *a;
norns_main_menu *menu;
janet_fixarity(argc, 2);
m = monolith_data_get();
dict = monolith_dict_get(m);
str = (const char *)janet_unwrap_string(argv[0]);
a = janet_unwrap_array(argv[1]);
rc = monolith_dict_lookup(dict, &pg, str, strlen(str));
if (!rc) {
fprintf(stderr, "could not find page '%s'\n", str);
return janet_wrap_nil();
}
menu = monolith_page_data_get(pg);
norns_menu_aux_set(menu, a);
return janet_wrap_nil();
}
static void norns_menu_aux_set(norns_main_menu *m,
JanetArray *a);
TODO: implement.
static void norns_menu_aux_set(norns_main_menu *m,
JanetArray *a)
{
m->aux_items = a;
}
There is a risk that the garbage collector in Janet could free this prematurely, but the flexibility of using a Janet array makes it worth trying.
The Janet array is expected to be an array of tuples. Each tuple contains the menu item name as a string, as well as a zero-parameter function that gets called when it is selected.
Because this menu is dynamically populated using Janet structures, drawing and selection behavior happen differently. When the aux menu is selected, a special flag is turned on to channel this new behavior. Returning to the main menu causes this flag to be switched off.
mm->aux_menu = 0;
int aux_menu;
menu->aux_menu = 0;
Selection. Even though the aux menu plays by its own rules,
it is still fundamentally using the norns_menu
interface.
When selected, the aux menu page will call on
norns_menu_reinit
similar to how the main and pages menu
work. It will also flip on the aux_menu
flag.
void norns_aux_menu_select(norns_main_menu *menu);
It is only when the aux menu is selected that the number of items is populated from the array. This keeps things simple and fast.
<<aux_menu_callbacks>>
void norns_aux_menu_select(norns_main_menu *menu)
{
norns_menu_reinit(&menu->menu,
"Aux",
NULL,
1,
menu);
menu->aux_menu = 1;
<<set_aux_menu_callbacks>>
if (menu->aux_items != NULL) {
menu->menu.nitems += menu->aux_items->count;
}
}
When norns_menu_reinit
is called, it sets a callback for
for what to do when a knob is called. This particular
callback needs to be replaced because this is where the
redrawing occurs.
{
norns_poll_d *poll;
poll = monolith_norns_poll(menu->m);
norns_poll_cb_knob(poll, 1, aux_menu_knob, menu);
norns_poll_cb_key(poll, 1, aux_menu_key, menu);
}
void monolith_norns_draw(monolith_d *m);
static void aux_menu_knob(void *ud, int pos)
{
norns_main_menu *mm;
norns_menu *menu;
mm = ud;
menu = &mm->menu;
norns_menu_step(menu, pos);
norns_menu_janet_draw(menu, mm->aux_items);
monolith_norns_draw(monolith_data_get());
}
The button also needs to be changed to due to the behavior required.
static void aux_menu_key(void *ud, int state);
static void aux_menu_key(void *ud, int state)
{
norns_main_menu *mm;
norns_menu *menu;
int selected;
if (state == 0) return;
mm = ud;
menu = &mm->menu;
selected = menu->selected;
if (selected == 1) {
/* void (*f)(norns_menu *, int); */
/* selected--; */
/* f = menu->items[selected].select; */
/* if (f != NULL) { */
/* f(menu, selected); */
/* } */
return_to_main(menu, selected);
} else if (selected > 1 && mm->aux_items != NULL) {
JanetArray *tuple;
JanetFunction *fun;
JanetArray *a;
a = mm->aux_items;
tuple = janet_unwrap_array(a->data[selected - 2]);
fun = janet_unwrap_function(tuple->data[1]);
janet_call(fun, 0, NULL);
}
}
Drawing. Menu items are read from a Janet array instead of
the norns_menu_item
struct. Item names are extracted from
the array as a C string, and then drawn onto the screen.
The first menu item is hardcoded to go back to the main menu
screen.
Regrettably, it would seem that the current drawing function does lend itself well for introducing a "Janet mode" for aux functions. Attempting to do so now would involving a lot of duplicate code and headache.
Currently, a menu is drawn out using a self-contained
function called norns_menu_draw
. To draw things the new
way, function called norns_menu_janet_draw
will be used.
As arguments, it will take in the Janet Array, in addition
to the norns menu type.
The norns_menu_janet_draw
function will be built up of
similar components as norns_menu_draw
for things like
the header, but replacing item drawing with the janet
array instead of the internal array.
void norns_menu_janet_draw(norns_menu *menu, JanetArray *a);
For now, just call norns_menu_draw
as a placeholder.
void norns_menu_janet_draw(norns_menu *menu, JanetArray *a)
{
norns_videobuf_clear(menu->buf);
norns_menu_header(menu);
<<draw_aux_menu_items>>
}
Selecting. Selecting an item is a matter of finding correct item in the array, extracting the function, and calling that function. If the selection is the first menu item, it will return to the main menu screen.
{
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;
}
if (item == 0) {
norns_draw_string(menu->buf,
0, 15 + 9*0,
fg, bg,
"_ Main Menu");
} else {
JanetArray *tuple;
const char *str;
tuple = janet_unwrap_array(a->data[item - 1]);
str = (const char *)janet_unwrap_string(tuple->data[0]);
norns_draw_string(menu->buf,
0, 15 + 9*i,
fg, bg,
str);
}
}
}
prev | home | next