Files
menu/api/menu_api.c
2025-12-23 09:06:47 +08:00

217 lines
6.7 KiB
C

#include "menu_api.h"
#include "../src/core/menu_core.h"
#include "../src/core/menu_event.h"
#include "../src/core/menu_stack.h"
#include <stddef.h>
static MenuCoreCtx g_menu_ctx;
MenuErrCode menu_init(void) {
return menu_core_init(&g_menu_ctx);
}
void menu_deinit(void) {
// Cleanup if needed
}
void menu_main_loop(uint32_t tick) {
menu_core_loop(&g_menu_ctx, tick);
}
MenuNodeId menu_register_node(
MenuNodeId id,
MenuNodeId parent_id,
const char* name,
MenuCallback enter_cb,
MenuCallback exit_cb
) {
return menu_register_node_ex(id, parent_id, name, enter_cb, exit_cb, NULL, NULL);
}
MenuNodeId menu_register_node_ex(
MenuNodeId id,
MenuNodeId parent_id,
const char* name,
MenuCallback enter_cb,
MenuCallback exit_cb,
MenuCallback render_cb,
void* user_data
) {
MenuNode node = {0};
node.id = id;
node.parent_id = parent_id;
node.name = name;
node.enter_cb = enter_cb;
node.exit_cb = exit_cb;
node.render_cb = render_cb;
node.user_data = user_data;
node.flags.is_enabled = true;
node.flags.is_visible = true;
node.flags.is_registered = false; // Will be set by core
node.param_id = 0;
node.permission_level = 0;
MenuNodeId new_id = 0;
if (menu_core_register_node(&g_menu_ctx, &node, &new_id) == MENU_ERR_OK) {
return new_id;
}
return 0;
}
MenuErrCode menu_register_nodes(
const MenuNode* nodes,
size_t count,
MenuNodeId* out_ids
) {
if (!nodes || count == 0) return MENU_ERR_INVALID_PARAM;
for (size_t i = 0; i < count; i++) {
MenuNode node = nodes[i];
MenuNodeId new_id = 0;
node.flags.is_enabled = true;
node.flags.is_visible = true;
node.flags.is_registered = false;
node.param_id = node.param_id ? node.param_id : 0;
node.permission_level = node.permission_level ? node.permission_level : 0;
MenuErrCode err = menu_core_register_node(&g_menu_ctx, &node, &new_id);
if (err != MENU_ERR_OK) {
return err;
}
if (out_ids) {
out_ids[i] = new_id;
}
}
return MENU_ERR_OK;
}
MenuErrCode menu_post_event(MenuEventType type, uint32_t param) {
return menu_event_post(&g_menu_ctx.event_queue, type, param);
}
MenuErrCode menu_enter(void) {
// We try to enter the first available root node.
// We can trigger an event or just let the loop handle it.
// If we rely on handle_event to enter root on NULL current,
// we just need to ensure loop is called.
// But explicitly:
if (g_menu_ctx.current_node_id == 0) {
// Force finding a root
// Since we don't have public access to ctx nodes from here easily without exposing everything,
// we can just call handle_event with a dummy event or NONE event if handle_event checks NULL current.
// But handle_event checks event type.
// We can add MENU_EVENT_NONE handling in handle_event to check entry?
// Or just manually find root here.
// We have access to g_menu_ctx.
for(int i=0; i<MENU_CONFIG_MAX_NODES; i++) {
if(g_menu_ctx.nodes[i].flags.is_registered && g_menu_ctx.nodes[i].parent_id == 0) {
// Enter root
MenuErrCode ret = menu_core_enter_node(&g_menu_ctx, g_menu_ctx.nodes[i].id);
if (ret != MENU_ERR_OK) return ret;
// Optimization: Automatically enter first child if root has children
// This makes the menu start with the first item selected
if (g_menu_ctx.nodes[i].first_child_id != 0) {
// We need to simulate Enter key or just manually transition
// Manual transition:
// Push root to stack
// But menu_core_enter_node logic doesn't push to stack automatically unless via handle_event
// So we mimic handle_event logic
menu_stack_push(&g_menu_ctx.stack, g_menu_ctx.nodes[i].id);
// Exit root (deselect)
menu_core_exit_node(&g_menu_ctx, g_menu_ctx.nodes[i].id);
// Enter child
return menu_core_enter_node(&g_menu_ctx, g_menu_ctx.nodes[i].first_child_id);
}
return MENU_ERR_OK;
}
}
}
return MENU_ERR_OK;
}
MenuErrCode menu_back(void) {
return menu_post_event(MENU_EVENT_KEY_ESC, 0);
}
MenuErrCode menu_node_bind_param(MenuNodeId node_id, uint16_t param_id) {
MenuNode* node = menu_core_get_node(&g_menu_ctx, node_id);
if (!node) return MENU_ERR_NOT_FOUND;
node->param_id = param_id;
return MENU_ERR_OK;
}
// Permission management functions
MenuErrCode menu_permission_register_role(uint8_t role_id, const char* name, MenuPermissionLevel level) {
if (!name) return MENU_ERR_INVALID_PARAM;
if (g_menu_ctx.permission.role_count >= 8) return MENU_ERR_NO_MEM;
for (uint8_t i = 0; i < g_menu_ctx.permission.role_count; i++) {
if (g_menu_ctx.permission.roles[i].id == role_id) {
return MENU_ERR_FAIL;
}
}
MenuRole* role = &g_menu_ctx.permission.roles[g_menu_ctx.permission.role_count];
role->id = role_id;
role->name = name;
role->level = level;
g_menu_ctx.permission.role_count++;
return MENU_ERR_OK;
}
MenuErrCode menu_permission_set_current_role(uint8_t role_id) {
g_menu_ctx.permission.current_role_id = role_id;
return MENU_ERR_OK;
}
MenuErrCode menu_permission_update_node_level(MenuNodeId node_id, MenuPermissionLevel level) {
MenuNode* node = menu_core_get_node(&g_menu_ctx, node_id);
if (!node) return MENU_ERR_NOT_FOUND;
node->permission_level = level;
return MENU_ERR_OK;
}
bool menu_permission_check_node_access(MenuNodeId node_id) {
MenuNode* node = menu_core_get_node(&g_menu_ctx, node_id);
if (!node) return false;
#if MENU_CONFIG_ENABLE_PERMISSION
MenuRole* role_node = NULL;
for (uint8_t i = 0; i < g_menu_ctx.permission.role_count; i++) {
if (g_menu_ctx.permission.roles[i].id == g_menu_ctx.permission.current_role_id) {
role_node = &g_menu_ctx.permission.roles[i];
break;
}
}
if (!role_node || role_node->level < node->permission_level) {
return false;
}
#endif
return true;
}
// Persistence functions
MenuErrCode menu_persistence_save(void) {
return menu_core_save_state(&g_menu_ctx);
}
MenuErrCode menu_persistence_load(void) {
return menu_core_load_state(&g_menu_ctx);
}
MenuErrCode menu_refresh(void) {
MenuErrCode err = menu_post_event(MENU_EVENT_RENDER, 0);
if (err != MENU_ERR_OK) {
return err;
}
return MENU_ERR_OK;
}