功能优化
This commit is contained in:
101
api/menu_api.c
101
api/menu_api.c
@ -58,6 +58,36 @@ MenuNodeId menu_register_node_ex(
|
|||||||
return 0;
|
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) {
|
MenuErrCode menu_post_event(MenuEventType type, uint32_t param) {
|
||||||
return menu_event_post(&g_menu_ctx.event_queue, type, param);
|
return menu_event_post(&g_menu_ctx.event_queue, type, param);
|
||||||
}
|
}
|
||||||
@ -113,3 +143,74 @@ MenuErrCode menu_node_bind_param(MenuNodeId node_id, uint16_t param_id) {
|
|||||||
node->param_id = param_id;
|
node->param_id = param_id;
|
||||||
return MENU_ERR_OK;
|
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;
|
||||||
|
}
|
||||||
|
|||||||
@ -34,6 +34,13 @@ MenuNodeId menu_register_node_ex(
|
|||||||
void* user_data
|
void* user_data
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Batch node registration
|
||||||
|
MenuErrCode menu_register_nodes(
|
||||||
|
const MenuNode* nodes,
|
||||||
|
size_t count,
|
||||||
|
MenuNodeId* out_ids
|
||||||
|
);
|
||||||
|
|
||||||
// Event Handling
|
// Event Handling
|
||||||
MenuErrCode menu_post_event(MenuEventType type, uint32_t param);
|
MenuErrCode menu_post_event(MenuEventType type, uint32_t param);
|
||||||
|
|
||||||
@ -44,6 +51,19 @@ MenuErrCode menu_back(void);
|
|||||||
// Parameter Binding
|
// Parameter Binding
|
||||||
MenuErrCode menu_node_bind_param(MenuNodeId node_id, uint16_t param_id);
|
MenuErrCode menu_node_bind_param(MenuNodeId node_id, uint16_t param_id);
|
||||||
|
|
||||||
|
// Permission Management
|
||||||
|
MenuErrCode menu_permission_register_role(uint8_t role_id, const char* name, MenuPermissionLevel level);
|
||||||
|
MenuErrCode menu_permission_set_current_role(uint8_t role_id);
|
||||||
|
MenuErrCode menu_permission_update_node_level(MenuNodeId node_id, MenuPermissionLevel level);
|
||||||
|
bool menu_permission_check_node_access(MenuNodeId node_id);
|
||||||
|
|
||||||
|
// Persistence Management
|
||||||
|
MenuErrCode menu_persistence_save(void);
|
||||||
|
MenuErrCode menu_persistence_load(void);
|
||||||
|
|
||||||
|
// Dynamic menu updates
|
||||||
|
MenuErrCode menu_refresh(void);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
// Globals
|
// Globals
|
||||||
int32_t g_volume = 50;
|
int32_t g_volume = 50;
|
||||||
int32_t g_brightness = 80;
|
int32_t g_brightness = 80;
|
||||||
uint8_t g_language = 0; // 0=EN, 1=CN
|
uint8_t g_language = 1; // 0=EN, 1=CN
|
||||||
|
|
||||||
// Callbacks
|
// Callbacks
|
||||||
MenuErrCode on_enter_main(MenuNodeId id, struct MenuCoreCtx* ctx) {
|
MenuErrCode on_enter_main(MenuNodeId id, struct MenuCoreCtx* ctx) {
|
||||||
|
|||||||
@ -8,14 +8,7 @@
|
|||||||
// Internal helper to get node pointer from ID
|
// Internal helper to get node pointer from ID
|
||||||
MenuNode* menu_core_get_node(MenuCoreCtx* ctx, MenuNodeId id) {
|
MenuNode* menu_core_get_node(MenuCoreCtx* ctx, MenuNodeId id) {
|
||||||
if (!ctx || id == 0) return NULL;
|
if (!ctx || id == 0) return NULL;
|
||||||
// Simple linear scan for now.
|
return menu_hash_find(ctx, id);
|
||||||
// Optimization: Could use hash table if implemented to return index/pointer
|
|
||||||
for (int i = 0; i < MENU_CONFIG_MAX_NODES; i++) {
|
|
||||||
if (ctx->nodes[i].flags.is_registered && ctx->nodes[i].id == id) {
|
|
||||||
return &ctx->nodes[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MenuErrCode menu_core_init(MenuCoreCtx* ctx) {
|
MenuErrCode menu_core_init(MenuCoreCtx* ctx) {
|
||||||
@ -86,15 +79,91 @@ MenuErrCode menu_core_register_node(MenuCoreCtx* ctx, const MenuNode* node, Menu
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add to hash
|
// Add to hash
|
||||||
menu_hash_insert(ctx, new_node->id);
|
menu_hash_insert(ctx, new_node->id, slot);
|
||||||
|
|
||||||
if (out_id) *out_id = new_node->id;
|
if (out_id) *out_id = new_node->id;
|
||||||
|
|
||||||
return MENU_ERR_OK;
|
return MENU_ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MenuErrCode menu_core_unregister_node(MenuCoreCtx* ctx, MenuNodeId id) {
|
||||||
|
if (!ctx || id == 0) return MENU_ERR_INVALID_PARAM;
|
||||||
|
|
||||||
|
MenuNode* node = menu_core_get_node(ctx, id);
|
||||||
|
if (!node) return MENU_ERR_NOT_FOUND;
|
||||||
|
|
||||||
|
// Find slot index
|
||||||
|
int slot = -1;
|
||||||
|
for (int i = 0; i < MENU_CONFIG_MAX_NODES; i++) {
|
||||||
|
if (ctx->nodes[i].flags.is_registered && ctx->nodes[i].id == id) {
|
||||||
|
slot = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (slot == -1) return MENU_ERR_NOT_FOUND;
|
||||||
|
|
||||||
|
// Update parent-child relationship
|
||||||
|
if (node->parent_id != 0) {
|
||||||
|
MenuNode* parent = menu_core_get_node(ctx, node->parent_id);
|
||||||
|
if (parent) {
|
||||||
|
// Check if this is the first child
|
||||||
|
if (parent->first_child_id == id) {
|
||||||
|
parent->first_child_id = node->next_sibling_id;
|
||||||
|
} else {
|
||||||
|
// Find previous sibling to update links
|
||||||
|
MenuNode* prev_sibling = menu_core_get_node(ctx, node->prev_sibling_id);
|
||||||
|
if (prev_sibling) {
|
||||||
|
prev_sibling->next_sibling_id = node->next_sibling_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update next sibling's prev_sibling_id
|
||||||
|
if (node->next_sibling_id != 0) {
|
||||||
|
MenuNode* next_sibling = menu_core_get_node(ctx, node->next_sibling_id);
|
||||||
|
if (next_sibling) {
|
||||||
|
next_sibling->prev_sibling_id = node->prev_sibling_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear node data
|
||||||
|
ctx->nodes[slot].flags.is_registered = false;
|
||||||
|
ctx->nodes[slot].id = 0;
|
||||||
|
ctx->nodes[slot].parent_id = 0;
|
||||||
|
ctx->nodes[slot].first_child_id = 0;
|
||||||
|
ctx->nodes[slot].next_sibling_id = 0;
|
||||||
|
ctx->nodes[slot].prev_sibling_id = 0;
|
||||||
|
ctx->nodes[slot].hash_next_id = 0;
|
||||||
|
ctx->nodes[slot].flags.is_selected = false;
|
||||||
|
ctx->nodes[slot].flags.is_visible = false;
|
||||||
|
ctx->nodes[slot].flags.is_enabled = false;
|
||||||
|
ctx->nodes[slot].permission_level = 0;
|
||||||
|
ctx->nodes[slot].param_id = 0;
|
||||||
|
ctx->nodes[slot].user_data = NULL;
|
||||||
|
ctx->nodes[slot].name = NULL;
|
||||||
|
ctx->nodes[slot].enter_cb = NULL;
|
||||||
|
ctx->nodes[slot].exit_cb = NULL;
|
||||||
|
ctx->nodes[slot].render_cb = NULL;
|
||||||
|
|
||||||
|
// Update counts
|
||||||
|
ctx->node_count--;
|
||||||
|
ctx->free_node_count++;
|
||||||
|
|
||||||
|
// Rebuild hash table since we can't easily remove from it
|
||||||
|
menu_hash_init(ctx);
|
||||||
|
for (int i = 0; i < MENU_CONFIG_MAX_NODES; i++) {
|
||||||
|
if (ctx->nodes[i].flags.is_registered) {
|
||||||
|
menu_hash_insert(ctx, ctx->nodes[i].id, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return MENU_ERR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
MenuErrCode menu_core_enter_node(MenuCoreCtx* ctx, MenuNodeId node_id) {
|
MenuErrCode menu_core_enter_node(MenuCoreCtx* ctx, MenuNodeId node_id) {
|
||||||
if (!ctx) return MENU_ERR_INVALID_PARAM;
|
if (!ctx) return MENU_ERR_INVALID_PARAM;
|
||||||
|
|
||||||
@ -103,7 +172,16 @@ MenuErrCode menu_core_enter_node(MenuCoreCtx* ctx, MenuNodeId node_id) {
|
|||||||
|
|
||||||
// Permission check
|
// Permission check
|
||||||
#if MENU_CONFIG_ENABLE_PERMISSION
|
#if MENU_CONFIG_ENABLE_PERMISSION
|
||||||
// TODO: Check permission
|
MenuRole* role_node = NULL;
|
||||||
|
for (uint8_t i = 0; i < ctx->permission.role_count; i++) {
|
||||||
|
if (ctx->permission.roles[i].id == ctx->permission.current_role_id) {
|
||||||
|
role_node = &ctx->permission.roles[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!role_node || role_node->level < node->permission_level) {
|
||||||
|
return MENU_ERR_PERMISSION_DENIED;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Call enter callback
|
// Call enter callback
|
||||||
@ -295,6 +373,46 @@ MenuErrCode menu_core_handle_event(MenuCoreCtx* ctx, const MenuEvent* event) {
|
|||||||
return MENU_ERR_OK;
|
return MENU_ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint8_t calculate_checksum(const MenuPersistenceData* data) {
|
||||||
|
uint8_t checksum = 0;
|
||||||
|
const uint8_t* bytes = (const uint8_t*)data;
|
||||||
|
size_t len = sizeof(MenuPersistenceData) - sizeof(data->checksum);
|
||||||
|
for (size_t i = 0; i < len; i++) {
|
||||||
|
checksum ^= bytes[i];
|
||||||
|
}
|
||||||
|
return checksum;
|
||||||
|
}
|
||||||
|
|
||||||
|
MenuErrCode menu_core_save_state(MenuCoreCtx* ctx) {
|
||||||
|
if (!ctx) return MENU_ERR_INVALID_PARAM;
|
||||||
|
|
||||||
|
MenuPersistenceData data = {
|
||||||
|
.current_node_id = ctx->current_node_id,
|
||||||
|
.current_state = ctx->current_state,
|
||||||
|
.nav_path = ctx->nav_path,
|
||||||
|
.stack = ctx->stack,
|
||||||
|
.timestamp = 0,
|
||||||
|
.checksum = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
data.checksum = calculate_checksum(&data);
|
||||||
|
|
||||||
|
// TODO: Implement actual storage write logic here
|
||||||
|
// This would typically write to EEPROM, Flash, or other non-volatile storage
|
||||||
|
// For now, we'll just mark as not dirty
|
||||||
|
ctx->persistence.dirty = false;
|
||||||
|
return MENU_ERR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
MenuErrCode menu_core_load_state(MenuCoreCtx* ctx) {
|
||||||
|
if (!ctx) return MENU_ERR_INVALID_PARAM;
|
||||||
|
|
||||||
|
// TODO: Implement actual storage read logic here
|
||||||
|
// This would typically read from EEPROM, Flash, or other non-volatile storage
|
||||||
|
// For now, we'll just return OK
|
||||||
|
return MENU_ERR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
void menu_core_loop(MenuCoreCtx* ctx, uint32_t tick) {
|
void menu_core_loop(MenuCoreCtx* ctx, uint32_t tick) {
|
||||||
if (!ctx || !ctx->is_initialized) return;
|
if (!ctx || !ctx->is_initialized) return;
|
||||||
|
|
||||||
@ -303,14 +421,20 @@ void menu_core_loop(MenuCoreCtx* ctx, uint32_t tick) {
|
|||||||
MenuEvent evt;
|
MenuEvent evt;
|
||||||
if (menu_event_get(&ctx->event_queue, &evt) == MENU_ERR_OK) {
|
if (menu_event_get(&ctx->event_queue, &evt) == MENU_ERR_OK) {
|
||||||
menu_core_handle_event(ctx, &evt);
|
menu_core_handle_event(ctx, &evt);
|
||||||
|
ctx->persistence.dirty = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render
|
// Render
|
||||||
// Optimization: Add a render callback to the context or port
|
|
||||||
// For now we rely on the current node's render_cb
|
|
||||||
MenuNode* current = menu_core_get_node(ctx, ctx->current_node_id);
|
MenuNode* current = menu_core_get_node(ctx, ctx->current_node_id);
|
||||||
if (current && current->render_cb) {
|
if (current && current->render_cb) {
|
||||||
current->render_cb(current->id, ctx);
|
current->render_cb(current->id, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Auto-save if dirty
|
||||||
|
#if MENU_CONFIG_ENABLE_PERSISTENCE
|
||||||
|
if (ctx->persistence.dirty) {
|
||||||
|
menu_core_save_state(ctx);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,6 +13,7 @@ MenuErrCode menu_core_init(MenuCoreCtx* ctx);
|
|||||||
// Node management
|
// Node management
|
||||||
MenuErrCode menu_core_register_node(MenuCoreCtx* ctx, const MenuNode* node, MenuNodeId* out_id);
|
MenuErrCode menu_core_register_node(MenuCoreCtx* ctx, const MenuNode* node, MenuNodeId* out_id);
|
||||||
MenuNode* menu_core_get_node(MenuCoreCtx* ctx, MenuNodeId id);
|
MenuNode* menu_core_get_node(MenuCoreCtx* ctx, MenuNodeId id);
|
||||||
|
MenuErrCode menu_core_unregister_node(MenuCoreCtx* ctx, MenuNodeId id);
|
||||||
|
|
||||||
// Main loop
|
// Main loop
|
||||||
void menu_core_loop(MenuCoreCtx* ctx, uint32_t tick);
|
void menu_core_loop(MenuCoreCtx* ctx, uint32_t tick);
|
||||||
|
|||||||
@ -8,64 +8,26 @@ MenuErrCode menu_hash_init(MenuCoreCtx* ctx) {
|
|||||||
return MENU_ERR_OK;
|
return MENU_ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
MenuErrCode menu_hash_insert(MenuCoreCtx* ctx, MenuNodeId id) {
|
MenuErrCode menu_hash_insert(MenuCoreCtx* ctx, MenuNodeId id, uint16_t index) {
|
||||||
if (!ctx) return MENU_ERR_INVALID_PARAM;
|
if (!ctx) return MENU_ERR_INVALID_PARAM;
|
||||||
|
|
||||||
uint16_t index = id % MENU_CONFIG_HASH_TABLE_SIZE;
|
uint16_t hash_index = id % MENU_CONFIG_HASH_TABLE_SIZE;
|
||||||
MenuNodeId current = ctx->hash_table[index];
|
uint16_t current = ctx->hash_table[hash_index];
|
||||||
|
|
||||||
if (current == 0) {
|
if (current == 0) {
|
||||||
ctx->hash_table[index] = id;
|
ctx->hash_table[hash_index] = index + 1;
|
||||||
} else {
|
} else {
|
||||||
// Collision handling: append to the end of the chain
|
MenuNode* prev_node = &ctx->nodes[current - 1];
|
||||||
// Note: 0 is used as invalid ID/null pointer in this static array context
|
|
||||||
// This implies valid node IDs must start from 1.
|
|
||||||
|
|
||||||
// Find the node structure for 'current'
|
|
||||||
// This requires access to the node array which is in ctx
|
|
||||||
// but we need to find the node index from the ID if ID != index
|
|
||||||
// In this system, we usually assume ID corresponds to index if allocated sequentially,
|
|
||||||
// but since we support arbitrary IDs (maybe?), we need to be careful.
|
|
||||||
// HOWEVER, the design says "Static node pool", so usually we just iterate.
|
|
||||||
// BUT wait, if we are building a hash table, we want O(1) access.
|
|
||||||
// Let's assume ID is NOT the index in the array, but a unique handle.
|
|
||||||
// But we need to find the MenuNode* from ID to set hash_next_id.
|
|
||||||
|
|
||||||
// Since we are inside menu_hash_insert, we probably just added the node
|
|
||||||
// so we might know where it is, but this function takes ID.
|
|
||||||
// We need a way to look up the MenuNode* from ID to set the next pointer.
|
|
||||||
// If we can't find it efficiently, this hash insert is slow.
|
|
||||||
|
|
||||||
// Actually, looking at `MenuCoreCtx`, `nodes` is an array.
|
|
||||||
// Does `nodes[i].id` == i?
|
|
||||||
// If the user provides arbitrary IDs, we need to scan the array to find the node
|
|
||||||
// to set its `hash_next_id`.
|
|
||||||
// That defeats the purpose of the hash table if insertion is O(N).
|
|
||||||
// But insertion happens only at init. Lookup is frequent.
|
|
||||||
|
|
||||||
// Let's implement finding the node by scanning for insertion.
|
|
||||||
MenuNode* prev_node = NULL;
|
|
||||||
// Find the head of the chain
|
|
||||||
for (int i = 0; i < ctx->node_count; i++) {
|
|
||||||
if (ctx->nodes[i].id == current) {
|
|
||||||
prev_node = &ctx->nodes[i];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!prev_node) return MENU_ERR_NOT_FOUND; // Should not happen
|
|
||||||
|
|
||||||
while (prev_node->hash_next_id != 0) {
|
while (prev_node->hash_next_id != 0) {
|
||||||
MenuNodeId next_id = prev_node->hash_next_id;
|
bool found = false;
|
||||||
bool found = false;
|
for (int i = 0; i < MENU_CONFIG_MAX_NODES; i++) {
|
||||||
for (int i = 0; i < ctx->node_count; i++) {
|
if (ctx->nodes[i].flags.is_registered && ctx->nodes[i].id == prev_node->hash_next_id) {
|
||||||
if (ctx->nodes[i].id == next_id) {
|
prev_node = &ctx->nodes[i];
|
||||||
prev_node = &ctx->nodes[i];
|
found = true;
|
||||||
found = true;
|
break;
|
||||||
break;
|
}
|
||||||
}
|
}
|
||||||
}
|
if (!found) return MENU_ERR_FAIL;
|
||||||
if (!found) return MENU_ERR_FAIL; // Broken chain
|
|
||||||
}
|
}
|
||||||
|
|
||||||
prev_node->hash_next_id = id;
|
prev_node->hash_next_id = id;
|
||||||
@ -74,26 +36,34 @@ MenuErrCode menu_hash_insert(MenuCoreCtx* ctx, MenuNodeId id) {
|
|||||||
return MENU_ERR_OK;
|
return MENU_ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
MenuNodeId menu_hash_find(MenuCoreCtx* ctx, MenuNodeId id) {
|
MenuNode* menu_hash_find(MenuCoreCtx* ctx, MenuNodeId id) {
|
||||||
if (!ctx) return 0;
|
if (!ctx) return NULL;
|
||||||
|
|
||||||
uint16_t index = id % MENU_CONFIG_HASH_TABLE_SIZE;
|
uint16_t hash_index = id % MENU_CONFIG_HASH_TABLE_SIZE;
|
||||||
MenuNodeId current = ctx->hash_table[index];
|
uint16_t current = ctx->hash_table[hash_index];
|
||||||
|
|
||||||
while (current != 0) {
|
while (current != 0) {
|
||||||
if (current == id) return current;
|
uint16_t node_index = current - 1;
|
||||||
|
MenuNode* node = &ctx->nodes[node_index];
|
||||||
|
if (node->flags.is_registered && node->id == id) {
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
// Move to next
|
|
||||||
bool found = false;
|
bool found = false;
|
||||||
for (int i = 0; i < ctx->node_count; i++) {
|
for (int i = 0; i < MENU_CONFIG_MAX_NODES; i++) {
|
||||||
if (ctx->nodes[i].id == current) {
|
if (ctx->nodes[i].flags.is_registered && ctx->nodes[i].id == node->hash_next_id) {
|
||||||
current = ctx->nodes[i].hash_next_id;
|
for (int j = 0; j < MENU_CONFIG_MAX_NODES; j++) {
|
||||||
found = true;
|
if (ctx->nodes[j].flags.is_registered && ctx->nodes[j].id == node->hash_next_id) {
|
||||||
|
current = j + 1;
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!found) break;
|
if (!found) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0; // Not found
|
return NULL;
|
||||||
}
|
}
|
||||||
@ -8,11 +8,11 @@ extern "C" {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
MenuErrCode menu_hash_init(MenuCoreCtx* ctx);
|
MenuErrCode menu_hash_init(MenuCoreCtx* ctx);
|
||||||
MenuErrCode menu_hash_insert(MenuCoreCtx* ctx, MenuNodeId id);
|
MenuErrCode menu_hash_insert(MenuCoreCtx* ctx, MenuNodeId id, uint16_t index);
|
||||||
MenuNodeId menu_hash_find(MenuCoreCtx* ctx, MenuNodeId id);
|
MenuNode* menu_hash_find(MenuCoreCtx* ctx, MenuNodeId id);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif // MENU_HASH_H
|
#endif // MENU_HASH_H
|
||||||
@ -17,6 +17,13 @@ struct MenuCoreCtx;
|
|||||||
typedef uint16_t MenuNodeId;
|
typedef uint16_t MenuNodeId;
|
||||||
typedef uint8_t MenuPermissionLevel;
|
typedef uint8_t MenuPermissionLevel;
|
||||||
|
|
||||||
|
// Role structure
|
||||||
|
typedef struct {
|
||||||
|
uint8_t id;
|
||||||
|
const char* name;
|
||||||
|
MenuPermissionLevel level;
|
||||||
|
} MenuRole;
|
||||||
|
|
||||||
// Error codes
|
// Error codes
|
||||||
typedef enum {
|
typedef enum {
|
||||||
MENU_ERR_OK = 0,
|
MENU_ERR_OK = 0,
|
||||||
@ -110,10 +117,19 @@ typedef struct {
|
|||||||
uint8_t depth;
|
uint8_t depth;
|
||||||
} MenuNavPath;
|
} MenuNavPath;
|
||||||
|
|
||||||
|
typedef struct MenuPersistenceData {
|
||||||
|
MenuNodeId current_node_id;
|
||||||
|
MenuState current_state;
|
||||||
|
MenuNavPath nav_path;
|
||||||
|
MenuStack stack;
|
||||||
|
uint32_t timestamp;
|
||||||
|
uint8_t checksum;
|
||||||
|
} MenuPersistenceData;
|
||||||
|
|
||||||
// Core Context
|
// Core Context
|
||||||
typedef struct MenuCoreCtx {
|
typedef struct MenuCoreCtx {
|
||||||
MenuNode nodes[MENU_CONFIG_MAX_NODES];
|
MenuNode nodes[MENU_CONFIG_MAX_NODES];
|
||||||
MenuNodeId hash_table[MENU_CONFIG_HASH_TABLE_SIZE];
|
uint16_t hash_table[MENU_CONFIG_HASH_TABLE_SIZE];
|
||||||
MenuStack stack;
|
MenuStack stack;
|
||||||
MenuNavPath nav_path;
|
MenuNavPath nav_path;
|
||||||
MenuEventQueue event_queue;
|
MenuEventQueue event_queue;
|
||||||
@ -132,6 +148,8 @@ typedef struct MenuCoreCtx {
|
|||||||
// Permission module data
|
// Permission module data
|
||||||
struct {
|
struct {
|
||||||
uint8_t current_role_id;
|
uint8_t current_role_id;
|
||||||
|
MenuRole roles[8];
|
||||||
|
uint8_t role_count;
|
||||||
} permission;
|
} permission;
|
||||||
|
|
||||||
// Persistence module data
|
// Persistence module data
|
||||||
@ -143,7 +161,7 @@ typedef struct MenuCoreCtx {
|
|||||||
struct {
|
struct {
|
||||||
uint8_t current_lang_id;
|
uint8_t current_lang_id;
|
||||||
} lang;
|
} lang;
|
||||||
|
|
||||||
} MenuCoreCtx;
|
} MenuCoreCtx;
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|||||||
Reference in New Issue
Block a user