File syn_menu.c¶
File List > src > syntropic > ui > syn_menu.c
Go to the documentation of this file
#if __has_include("syn_config.h")
#include "syn_config.h"
#endif
#if !defined(SYN_USE_MENU) || SYN_USE_MENU
#include "../util/syn_assert.h"
#include "syn_menu.h"
#include <string.h>
void syn_menu_init(SYN_Menu *menu, const SYN_MenuItem *root, SYN_MenuRenderFn render, void *ctx)
{
SYN_ASSERT(menu != NULL);
SYN_ASSERT(root != NULL);
SYN_ASSERT(root->action == SYN_MENU_ACTION_SUBMENU);
memset(menu, 0, sizeof(*menu));
menu->root = root;
menu->current = root;
menu->selected = 0;
menu->depth = 0;
menu->render = render;
menu->render_ctx = ctx;
}
void syn_menu_up(SYN_Menu *menu)
{
SYN_ASSERT(menu != NULL);
if (menu->editing) {
/* Increment value */
const SYN_MenuItem *item = syn_menu_selected_item(menu);
if (item != NULL && item->action == SYN_MENU_ACTION_VALUE &&
item->u.value_cfg.value != NULL) {
int32_t *val = item->u.value_cfg.value;
*val += item->u.value_cfg.step;
if (*val > item->u.value_cfg.max)
*val = item->u.value_cfg.max;
}
} else {
/* Move selection up (with wrap) */
if (menu->selected == 0) {
menu->selected = menu->current->u.submenu.count - 1;
} else {
menu->selected--;
}
}
syn_menu_render(menu);
}
void syn_menu_down(SYN_Menu *menu)
{
SYN_ASSERT(menu != NULL);
if (menu->editing) {
/* Decrement value */
const SYN_MenuItem *item = syn_menu_selected_item(menu);
if (item != NULL && item->action == SYN_MENU_ACTION_VALUE &&
item->u.value_cfg.value != NULL) {
int32_t *val = item->u.value_cfg.value;
*val -= item->u.value_cfg.step;
if (*val < item->u.value_cfg.min)
*val = item->u.value_cfg.min;
}
} else {
/* Move selection down (with wrap) */
menu->selected++;
if (menu->selected >= menu->current->u.submenu.count) {
menu->selected = 0;
}
}
syn_menu_render(menu);
}
void syn_menu_enter(SYN_Menu *menu)
{
SYN_ASSERT(menu != NULL);
if (menu->editing) {
/* Exit edit mode */
menu->editing = false;
syn_menu_render(menu);
return;
}
const SYN_MenuItem *item = syn_menu_selected_item(menu);
switch ((SYN_MenuAction)item->action) {
case SYN_MENU_ACTION_SUBMENU:
if (menu->depth < SYN_MENU_MAX_DEPTH - 1) {
/* Push current onto stack */
menu->stack[menu->depth] = menu->current;
menu->stack_sel[menu->depth] = menu->selected;
menu->depth++;
menu->current = item;
menu->selected = 0;
}
break;
case SYN_MENU_ACTION_CALLBACK:
if (item->u.callback.func != NULL) {
item->u.callback.func(item->u.callback.ctx);
}
break;
case SYN_MENU_ACTION_TOGGLE:
if (item->u.toggle != NULL) {
*item->u.toggle = !(*item->u.toggle);
}
break;
case SYN_MENU_ACTION_VALUE:
menu->editing = true;
break;
}
syn_menu_render(menu);
}
void syn_menu_back(SYN_Menu *menu)
{
SYN_ASSERT(menu != NULL);
if (menu->editing) {
menu->editing = false;
syn_menu_render(menu);
return;
}
if (menu->depth > 0) {
menu->depth--;
menu->current = menu->stack[menu->depth];
menu->selected = menu->stack_sel[menu->depth];
}
syn_menu_render(menu);
}
void syn_menu_render(const SYN_Menu *menu)
{
if (menu->render != NULL) {
menu->render(menu, menu->render_ctx);
}
}
#endif /* SYN_USE_MENU */