Skip to content

File syn_menu.h

File List > src > syntropic > ui > syn_menu.h

Go to the documentation of this file

#ifndef SYN_MENU_H
#define SYN_MENU_H

#include "../common/syn_defs.h"

#include <stdbool.h>
#include <stdint.h>

#ifdef __cplusplus
extern "C" {
#endif

/* ── Menu item types ────────────────────────────────────────────────────── */

typedef enum {
    SYN_MENU_ACTION_SUBMENU = 0,  
    SYN_MENU_ACTION_CALLBACK = 1, 
    SYN_MENU_ACTION_TOGGLE = 2,   
    SYN_MENU_ACTION_VALUE = 3,    
} SYN_MenuAction;

/* ── Value config (for ACTION_VALUE items) ──────────────────────────────── */

typedef struct {
    int32_t *value; 
    int32_t min;    
    int32_t max;    
    int32_t step;   
} SYN_MenuValueCfg;

/* ── Menu item ──────────────────────────────────────────────────────────── */

typedef struct SYN_MenuItem {
    const char *label; 
    uint8_t action;    
    union {
        struct {
            const struct SYN_MenuItem *children; 
            uint8_t count;                       
        } submenu;                               
        struct {
            void (*func)(void *ctx); 
            void *ctx;               
        } callback;                  
        bool *toggle;                
        SYN_MenuValueCfg value_cfg;  
    } u;                             
} SYN_MenuItem;

#define SYN_MENU_SUBMENU(lbl, items)                                                      \
    {                                                                                     \
        .label = (lbl), .action = SYN_MENU_ACTION_SUBMENU, .u = {                         \
            .submenu = {.children = (items), .count = sizeof(items) / sizeof((items)[0])} \
        }                                                                                 \
    }

#define SYN_MENU_CALLBACK(lbl, fn, c)                              \
    {                                                              \
        .label = (lbl), .action = SYN_MENU_ACTION_CALLBACK, .u = { \
            .callback = {.func = (fn), .ctx = (c)}                 \
        }                                                          \
    }

#define SYN_MENU_TOGGLE(lbl, ptr)                                                 \
    {                                                                             \
        .label = (lbl), .action = SYN_MENU_ACTION_TOGGLE, .u = {.toggle = (ptr) } \
    }

#define SYN_MENU_VALUE(lbl, ptr, mn, mx, st)                                      \
    {                                                                             \
        .label = (lbl), .action = SYN_MENU_ACTION_VALUE, .u = {                   \
            .value_cfg = {.value = (ptr), .min = (mn), .max = (mx), .step = (st)} \
        }                                                                         \
    }

#define SYN_MENU_ROOT(name, items) static const SYN_MenuItem name = SYN_MENU_SUBMENU(#name, items)

/* ── Render callback ────────────────────────────────────────────────────── */

struct SYN_Menu;

typedef void (*SYN_MenuRenderFn)(const struct SYN_Menu *menu, void *ctx);

/* ── Menu state ─────────────────────────────────────────────────────────── */

#ifndef SYN_MENU_MAX_DEPTH
#define SYN_MENU_MAX_DEPTH 8 
#endif

typedef struct SYN_Menu {
    const SYN_MenuItem *root; 
    /* Navigation stack */
    const SYN_MenuItem *stack[SYN_MENU_MAX_DEPTH]; 
    uint8_t stack_sel[SYN_MENU_MAX_DEPTH];         
    uint8_t depth;                                 
    /* Current view */
    const SYN_MenuItem *current; 
    uint8_t selected;            
    bool editing;                
    /* Render */
    SYN_MenuRenderFn render; 
    void *render_ctx;        
} SYN_Menu;

/* ── API ────────────────────────────────────────────────────────────────── */

void syn_menu_init(SYN_Menu *menu, const SYN_MenuItem *root, SYN_MenuRenderFn render, void *ctx);

void syn_menu_up(SYN_Menu *menu);

void syn_menu_down(SYN_Menu *menu);

void syn_menu_enter(SYN_Menu *menu);

void syn_menu_back(SYN_Menu *menu);

static inline uint8_t syn_menu_item_count(const SYN_Menu *menu)
{
    return menu->current->u.submenu.count;
}

static inline const SYN_MenuItem *syn_menu_selected_item(const SYN_Menu *menu)
{
    return &menu->current->u.submenu.children[menu->selected];
}

void syn_menu_render(const SYN_Menu *menu);

#ifdef __cplusplus
}
#endif

#endif /* SYN_MENU_H */