mirror of
https://github.com/Polprzewodnikowy/N64FlashcartMenu.git
synced 2024-11-25 03:56:54 +01:00
Add menu lib
This commit is contained in:
parent
ffb6b3fd53
commit
3a2b908a4f
1
Makefile
1
Makefile
@ -17,6 +17,7 @@ SRCS = \
|
||||
menu/menu.c \
|
||||
utils/fs.c \
|
||||
libs/toml/toml.c \
|
||||
libs/menu_utils/menu.c \
|
||||
main.c
|
||||
|
||||
OBJS = $(addprefix $(BUILD_DIR)/, $(addsuffix .o,$(basename $(SRCS))))
|
||||
|
11
src/libs/menu_utils/README.md
Normal file
11
src/libs/menu_utils/README.md
Normal file
@ -0,0 +1,11 @@
|
||||
# Source
|
||||
https://github.com/stefanmielke/libdragon-extensions
|
||||
|
||||
# License
|
||||
MIT
|
||||
|
||||
# Description
|
||||
Used for generating the menu GUI
|
||||
|
||||
# Notes
|
||||
It might be preferable to change to a submodule.
|
42
src/libs/menu_utils/mem_pool.c
Normal file
42
src/libs/menu_utils/mem_pool.c
Normal file
@ -0,0 +1,42 @@
|
||||
#include "../include/mem_pool.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <libdragon.h>
|
||||
|
||||
void mem_zone_init(MemZone *z, size_t size) {
|
||||
disable_interrupts();
|
||||
|
||||
void *ptr = malloc(size);
|
||||
if (ptr == NULL) {
|
||||
abort(); // Put your error handling here.
|
||||
}
|
||||
z->pos = (char *)ptr;
|
||||
z->start = z->pos;
|
||||
z->end = z->start + size;
|
||||
|
||||
memset(z->start, 0, size);
|
||||
|
||||
enable_interrupts();
|
||||
}
|
||||
|
||||
void *mem_zone_alloc(MemZone *z, size_t size) {
|
||||
if (size == 0) {
|
||||
return NULL;
|
||||
}
|
||||
// Round up to multiple of 16 bytes.
|
||||
size = (size + 15) & ~(size_t)15;
|
||||
// How much free space remaining in zone?
|
||||
size_t rem = z->end - z->pos;
|
||||
if (rem < size) {
|
||||
return NULL; // Out of memory. Put your error handling here.
|
||||
}
|
||||
|
||||
void *ptr = (void *)z->pos;
|
||||
z->pos += size;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void mem_zone_free_all(MemZone *z) {
|
||||
z->pos = z->start;
|
||||
}
|
49
src/libs/menu_utils/mem_pool.h
Normal file
49
src/libs/menu_utils/mem_pool.h
Normal file
@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
|
||||
#include <malloc.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief A contiguous zone where memory can be allocated.
|
||||
*/
|
||||
typedef struct {
|
||||
/// Pointer to current free space position.
|
||||
char *pos;
|
||||
/// Pointer to start of zone.
|
||||
char *start;
|
||||
/// Pointer to end of zone.
|
||||
char *end;
|
||||
} MemZone;
|
||||
|
||||
/**
|
||||
* @brief Allocate a memory zone with the given size.
|
||||
*
|
||||
* @param memory_zone MemZone to use.
|
||||
* @param size Size in bytes that the MemZone will have. (eg.: '1*1024*1024' for 1MB)
|
||||
*/
|
||||
void mem_zone_init(MemZone *memory_zone, size_t size);
|
||||
|
||||
/**
|
||||
* @brief Allocate memory from the zone.
|
||||
*
|
||||
* @param memory_zone MemZone to use.
|
||||
* @param size Size in bytes of the memory. (eg.: sizeof(int))
|
||||
*
|
||||
* @return the memory allocated.
|
||||
*/
|
||||
void *mem_zone_alloc(MemZone *memory_zone, size_t size);
|
||||
|
||||
/**
|
||||
* @brief Free all objects in the zone.
|
||||
*
|
||||
* @param memory_zone MemZone to use.
|
||||
*/
|
||||
void mem_zone_free_all(MemZone *memory_zone);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
6
src/libs/menu_utils/memory_alloc.h
Normal file
6
src/libs/menu_utils/memory_alloc.h
Normal file
@ -0,0 +1,6 @@
|
||||
#include "mem_pool.h"
|
||||
|
||||
/**
|
||||
* @brief Used to allocate an object using either a MemZone or malloc according to the parameters.
|
||||
*/
|
||||
#define MEM_ALLOC(SIZE, MEMPOOL) ((MEMPOOL) ? mem_zone_alloc((MEMPOOL), (SIZE)) : malloc(SIZE))
|
415
src/libs/menu_utils/menu.c
Normal file
415
src/libs/menu_utils/menu.c
Normal file
@ -0,0 +1,415 @@
|
||||
#include "../include/menu.h"
|
||||
|
||||
#include "../include/memory_alloc.h"
|
||||
|
||||
void menu_scroll_up(Menu *menu);
|
||||
void menu_scroll_down(Menu *menu);
|
||||
|
||||
uint32_t MENU_C_SELECTED;
|
||||
uint32_t MENU_C_ENABLED;
|
||||
uint32_t MENU_C_DISABLED;
|
||||
uint32_t MENU_C_BACKGROUND;
|
||||
uint32_t MENU_C_MENU_BACKGROUND;
|
||||
uint32_t MENU_C_OUT_OF_BOUNDS;
|
||||
|
||||
sprite_t *menu_background_sprite;
|
||||
sprite_t *menu_hand_sprite;
|
||||
uint8_t menu_hand_sprite_offset;
|
||||
|
||||
void menu_global_init() {
|
||||
MENU_C_SELECTED = graphics_make_color(0, 0, 255, 255);
|
||||
MENU_C_ENABLED = graphics_make_color(255, 255, 255, 255);
|
||||
MENU_C_DISABLED = graphics_make_color(100, 100, 255, 255);
|
||||
MENU_C_BACKGROUND = graphics_make_color(0, 0, 0, 0);
|
||||
MENU_C_OUT_OF_BOUNDS = graphics_make_color(0, 0, 0, 255);
|
||||
MENU_C_MENU_BACKGROUND = graphics_make_color(255, 255, 255, 255);
|
||||
}
|
||||
|
||||
void menu_global_set_default_colors(uint32_t selected, uint32_t enabled, uint32_t disabled,
|
||||
uint32_t background, uint32_t out_of_bounds,
|
||||
uint32_t menu_background) {
|
||||
MENU_C_SELECTED = selected;
|
||||
MENU_C_ENABLED = enabled;
|
||||
MENU_C_DISABLED = disabled;
|
||||
MENU_C_BACKGROUND = background;
|
||||
MENU_C_OUT_OF_BOUNDS = out_of_bounds;
|
||||
MENU_C_MENU_BACKGROUND = menu_background;
|
||||
}
|
||||
|
||||
void menu_global_set_sprites(sprite_t *menu_sprite, sprite_t *hand_sprite,
|
||||
uint8_t hand_sprite_offset) {
|
||||
menu_background_sprite = menu_sprite;
|
||||
menu_hand_sprite = hand_sprite;
|
||||
menu_hand_sprite_offset = hand_sprite_offset;
|
||||
}
|
||||
|
||||
Menu *menu_init(MemZone *memory_pool, uint8_t total_items, uint8_t max_items, int top, int left,
|
||||
uint8_t item_height, fnMenuCallback callback) {
|
||||
Menu *menu = MEM_ALLOC(sizeof(Menu), memory_pool);
|
||||
menu->total_items = total_items;
|
||||
menu->items = MEM_ALLOC(sizeof(MenuItem) * total_items, memory_pool);
|
||||
menu->current_menu_option = 0;
|
||||
menu_reset_items(menu);
|
||||
|
||||
menu->active_submenu = -1;
|
||||
menu->submenus = NULL;
|
||||
|
||||
menu->display_when_on_submenu = false;
|
||||
|
||||
menu->display_background = false;
|
||||
menu->menu_width = 0;
|
||||
|
||||
menu->item_vertical_limit = max_items;
|
||||
menu->item_height = item_height;
|
||||
menu->top = top;
|
||||
menu->left = left;
|
||||
menu->cur_top_item = 0;
|
||||
menu->cur_bottom_item = 0;
|
||||
|
||||
menu->callback = callback;
|
||||
|
||||
menu->display_hand = false;
|
||||
menu->hand_position_x = 0;
|
||||
menu->hand_position_y_offset = 0;
|
||||
|
||||
return menu;
|
||||
}
|
||||
|
||||
void menu_set_background(Menu *menu, int menu_width) {
|
||||
menu->display_background = true;
|
||||
menu->menu_width = menu_width;
|
||||
}
|
||||
|
||||
void menu_reset_items(Menu *menu) {
|
||||
menu->current_add_index = 0;
|
||||
for (uint8_t i = 0; i < menu->total_items; ++i) {
|
||||
menu->items[i].enabled = false;
|
||||
menu->items[i].text = NULL;
|
||||
menu->items[i].x = 0;
|
||||
menu->items[i].y = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void menu_set_hand(Menu *menu, int hand_position_x, int hand_position_y_offset) {
|
||||
menu->display_hand = true;
|
||||
menu->hand_position_x = hand_position_x;
|
||||
menu->hand_position_y_offset = hand_position_y_offset;
|
||||
}
|
||||
|
||||
void menu_add_item(Menu *menu, char *text, bool enabled, void *object) {
|
||||
if (menu->current_add_index >= menu->total_items)
|
||||
return;
|
||||
|
||||
MenuItem *item = &menu->items[menu->current_add_index];
|
||||
item->text = text;
|
||||
item->x = menu->left;
|
||||
item->y = menu->top + (menu->item_height * menu->current_add_index);
|
||||
item->enabled = enabled;
|
||||
item->has_custom_colors = false;
|
||||
item->sprite_index = -1;
|
||||
item->object = object;
|
||||
|
||||
if (menu->current_add_index == menu->item_vertical_limit)
|
||||
item->state = MSIS_OnBorder;
|
||||
else if (menu->current_add_index > menu->item_vertical_limit)
|
||||
item->state = MSIS_Outside;
|
||||
else
|
||||
item->state = MSIS_Inside;
|
||||
|
||||
menu->current_add_index++;
|
||||
|
||||
menu->cur_bottom_item = menu->current_add_index >= menu->item_vertical_limit
|
||||
? menu->item_vertical_limit - 1
|
||||
: menu->current_add_index - 1;
|
||||
}
|
||||
|
||||
void menu_add_item_image(Menu *menu, sprite_t *sprite, int sprite_index, bool enabled,
|
||||
void *object) {
|
||||
if (menu->current_add_index >= menu->total_items)
|
||||
return;
|
||||
|
||||
MenuItem *item = &menu->items[menu->current_add_index];
|
||||
item->sprite = sprite;
|
||||
item->sprite_index = sprite_index;
|
||||
item->x = menu->left;
|
||||
item->y = menu->top + (menu->item_height * menu->current_add_index);
|
||||
item->enabled = enabled;
|
||||
item->has_custom_colors = false;
|
||||
item->object = object;
|
||||
|
||||
if (menu->current_add_index == menu->item_vertical_limit)
|
||||
item->state = MSIS_OnBorder;
|
||||
else if (menu->current_add_index > menu->item_vertical_limit)
|
||||
item->state = MSIS_Outside;
|
||||
else
|
||||
item->state = MSIS_Inside;
|
||||
|
||||
menu->current_add_index++;
|
||||
|
||||
menu->cur_bottom_item = menu->current_add_index >= menu->item_vertical_limit
|
||||
? menu->item_vertical_limit - 1
|
||||
: menu->current_add_index - 1;
|
||||
}
|
||||
|
||||
void menu_add_item_colored(Menu *menu, char *text, bool enabled, uint32_t color_selected,
|
||||
uint32_t color_enabled, uint32_t color_disabled, void *object) {
|
||||
if (menu->current_add_index >= menu->total_items)
|
||||
return;
|
||||
|
||||
MenuItem *item = &menu->items[menu->current_add_index];
|
||||
item->text = text;
|
||||
item->x = menu->left;
|
||||
item->y = menu->top + (menu->item_height * menu->current_add_index);
|
||||
item->enabled = enabled;
|
||||
item->has_custom_colors = true;
|
||||
item->sprite_index = -1;
|
||||
item->object = object;
|
||||
|
||||
item->color_selected = color_selected;
|
||||
item->color_enabled = color_enabled;
|
||||
item->color_disabled = color_disabled;
|
||||
|
||||
if (menu->current_add_index == menu->item_vertical_limit)
|
||||
item->state = MSIS_OnBorder;
|
||||
else if (menu->current_add_index > menu->item_vertical_limit)
|
||||
item->state = MSIS_Outside;
|
||||
else
|
||||
item->state = MSIS_Inside;
|
||||
|
||||
menu->current_add_index++;
|
||||
|
||||
menu->cur_bottom_item = menu->current_add_index >= menu->item_vertical_limit
|
||||
? menu->item_vertical_limit - 1
|
||||
: menu->current_add_index - 1;
|
||||
}
|
||||
|
||||
int menu_tick(Menu *menu, struct controller_data *keys_down) {
|
||||
if (menu->active_submenu >= 0 && menu->submenus) {
|
||||
Menu **menus = menu->submenus;
|
||||
Menu *active_submenu = menus[menu->active_submenu];
|
||||
int option = menu_tick(active_submenu, keys_down);
|
||||
|
||||
// go back from submenu
|
||||
if (option == -2) {
|
||||
menu->active_submenu = -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return option;
|
||||
}
|
||||
if (menu->current_add_index <= 0)
|
||||
return -1;
|
||||
|
||||
if (keys_down->c[0].up) {
|
||||
if (menu->current_menu_option > 0) {
|
||||
menu->current_menu_option -= 1;
|
||||
menu_scroll_up(menu);
|
||||
}
|
||||
} else if (keys_down->c[0].down) {
|
||||
if (menu->current_menu_option < menu->current_add_index - 1) {
|
||||
menu->current_menu_option += 1;
|
||||
menu_scroll_down(menu);
|
||||
}
|
||||
} else if (keys_down->c[0].A || keys_down->c[0].start) {
|
||||
if (menu->items[menu->current_menu_option].enabled) {
|
||||
if (menu->callback) {
|
||||
menu->callback(menu->current_menu_option, &menu->items[menu->current_menu_option]);
|
||||
}
|
||||
return menu->current_menu_option;
|
||||
}
|
||||
} else if (keys_down->c[0].B) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void menu_render(Menu *menu, display_context_t disp) {
|
||||
if (menu->active_submenu >= 0 && menu->submenus) {
|
||||
Menu *active_submenu = menu->submenus[menu->active_submenu];
|
||||
|
||||
menu_render(active_submenu, disp);
|
||||
|
||||
if (!menu->display_when_on_submenu)
|
||||
return;
|
||||
}
|
||||
|
||||
// render window background
|
||||
if (menu->display_background) {
|
||||
const int item_count = menu->current_add_index < menu->item_vertical_limit
|
||||
? menu->current_add_index
|
||||
: menu->item_vertical_limit;
|
||||
|
||||
const int menu_left = menu->left - 8;
|
||||
const int menu_top = menu->top - menu->item_height;
|
||||
const int menu_bottom = menu->top + (menu->item_height * item_count);
|
||||
const int menu_right = menu->left + menu->menu_width;
|
||||
|
||||
menu_draw_background_center(disp, menu_top, menu_left, menu_bottom, menu_right);
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < menu->current_add_index; ++i) {
|
||||
if (menu->items[i].state == MSIS_Outside)
|
||||
continue;
|
||||
|
||||
if (menu->items[i].sprite_index == -1) {
|
||||
if (menu->items[i].state == MSIS_Inside) {
|
||||
if (i == menu->current_menu_option) {
|
||||
if (menu->items[i].has_custom_colors) {
|
||||
graphics_set_color(menu->items[i].color_selected, MENU_C_BACKGROUND);
|
||||
} else {
|
||||
graphics_set_color(MENU_C_SELECTED, MENU_C_BACKGROUND);
|
||||
}
|
||||
} else if (menu->items[i].enabled) {
|
||||
if (menu->items[i].has_custom_colors) {
|
||||
graphics_set_color(menu->items[i].color_enabled, MENU_C_BACKGROUND);
|
||||
} else {
|
||||
graphics_set_color(MENU_C_ENABLED, MENU_C_BACKGROUND);
|
||||
}
|
||||
} else {
|
||||
if (menu->items[i].has_custom_colors) {
|
||||
graphics_set_color(menu->items[i].color_disabled, MENU_C_BACKGROUND);
|
||||
} else {
|
||||
graphics_set_color(MENU_C_DISABLED, MENU_C_BACKGROUND);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
graphics_set_color(MENU_C_OUT_OF_BOUNDS, MENU_C_BACKGROUND);
|
||||
}
|
||||
|
||||
graphics_draw_text(disp, menu->items[i].x, menu->items[i].y, menu->items[i].text);
|
||||
} else {
|
||||
graphics_draw_sprite_trans_stride(disp, menu->items[i].x, menu->items[i].y,
|
||||
menu->items[i].sprite, menu->items[i].sprite_index);
|
||||
}
|
||||
}
|
||||
|
||||
if (menu->display_background) {
|
||||
const int item_count = menu->current_add_index < menu->item_vertical_limit
|
||||
? menu->current_add_index
|
||||
: menu->item_vertical_limit;
|
||||
|
||||
const int menu_left = menu->left - 8;
|
||||
const int menu_top = menu->top - menu->item_height;
|
||||
const int menu_bottom = menu->top + (menu->item_height * item_count);
|
||||
const int menu_right = menu->left + menu->menu_width;
|
||||
|
||||
menu_draw_background_borders(disp, menu_top, menu_left, menu_bottom, menu_right);
|
||||
}
|
||||
|
||||
// render hand icon
|
||||
if (menu->display_hand && menu->current_add_index > 0) {
|
||||
int option = menu->current_menu_option;
|
||||
if (menu->current_add_index > 0)
|
||||
graphics_draw_sprite_trans_stride(disp, menu->hand_position_x,
|
||||
menu->items[option].y + menu->hand_position_y_offset,
|
||||
menu_hand_sprite, menu_hand_sprite_offset);
|
||||
}
|
||||
}
|
||||
|
||||
void menu_init_submenus(Menu *menu, MemZone *memory_pool, uint8_t total_submenus,
|
||||
bool display_when_on_submenu) {
|
||||
// do not recreate submenus
|
||||
if (menu->submenus)
|
||||
return;
|
||||
|
||||
menu->submenus = MEM_ALLOC(sizeof(Menu *) * total_submenus, memory_pool);
|
||||
menu->display_when_on_submenu = display_when_on_submenu;
|
||||
}
|
||||
|
||||
void menu_draw_background_borders(display_context_t disp, int top, int left, int bottom,
|
||||
int right) {
|
||||
graphics_draw_sprite_trans_stride(disp, left, top, menu_background_sprite,
|
||||
SPRITE_menu_top_left);
|
||||
|
||||
graphics_draw_sprite_trans_stride(disp, left, bottom, menu_background_sprite,
|
||||
SPRITE_menu_bottom_left);
|
||||
|
||||
graphics_draw_sprite_trans_stride(disp, right, top, menu_background_sprite,
|
||||
SPRITE_menu_top_right);
|
||||
|
||||
graphics_draw_sprite_trans_stride(disp, right, bottom, menu_background_sprite,
|
||||
SPRITE_menu_bottom_right);
|
||||
|
||||
const int repeat_x = (right - left - 8) / 8;
|
||||
for (size_t i = 0; i < repeat_x; ++i) {
|
||||
graphics_draw_sprite_trans_stride(disp, left + 8 + (i * 8), top, menu_background_sprite,
|
||||
SPRITE_menu_top);
|
||||
graphics_draw_sprite_trans_stride(disp, left + 8 + (i * 8), bottom, menu_background_sprite,
|
||||
SPRITE_menu_bottom);
|
||||
}
|
||||
const int repeat_y = ((bottom - top) / 8) - 1;
|
||||
for (size_t i = 0; i < repeat_y; ++i) {
|
||||
graphics_draw_sprite_trans_stride(disp, left, top + 8 + (i * 8), menu_background_sprite,
|
||||
SPRITE_menu_left);
|
||||
graphics_draw_sprite_trans_stride(disp, right, top + 8 + (i * 8), menu_background_sprite,
|
||||
SPRITE_menu_right);
|
||||
}
|
||||
}
|
||||
|
||||
void menu_draw_background_center(display_context_t disp, int top, int left, int bottom, int right) {
|
||||
// TODO: remove this "hack" and properly render the texture. Cause: libdragon issue with 32 bits
|
||||
left += 8;
|
||||
top += 8;
|
||||
|
||||
rdp_sync(SYNC_PIPE);
|
||||
|
||||
rdp_attach_display(disp);
|
||||
rdp_enable_blend_fill();
|
||||
rdp_set_default_clipping();
|
||||
|
||||
rdp_sync(SYNC_PIPE);
|
||||
rdp_set_blend_color(MENU_C_MENU_BACKGROUND);
|
||||
rdp_draw_filled_triangle(left, top, right, top, right, bottom);
|
||||
rdp_draw_filled_triangle(left, top, left, bottom, right, bottom);
|
||||
|
||||
rdp_detach_display();
|
||||
}
|
||||
|
||||
void menu_scroll_fix_y(Menu *menu) {
|
||||
if (menu->cur_top_item > 0) {
|
||||
menu->items[menu->cur_top_item - 1].y = menu->top - menu->item_height;
|
||||
}
|
||||
for (size_t ix = menu->cur_top_item, i = 0; i <= menu->cur_bottom_item; ++i, ++ix) {
|
||||
menu->items[ix].y = menu->top + (i * menu->item_height);
|
||||
}
|
||||
if (menu->cur_bottom_item + 1 < menu->current_add_index) {
|
||||
menu->items[menu->cur_bottom_item + 1].y = menu->top +
|
||||
(menu->item_vertical_limit * menu->item_height);
|
||||
}
|
||||
}
|
||||
|
||||
void menu_scroll_up(Menu *menu) {
|
||||
if (menu->current_menu_option < menu->cur_top_item) {
|
||||
if (menu->cur_bottom_item + 1 < menu->current_add_index)
|
||||
menu->items[menu->cur_bottom_item + 1].state = MSIS_Outside;
|
||||
menu->items[menu->cur_bottom_item].state = MSIS_OnBorder;
|
||||
|
||||
if (menu->cur_top_item - 2 >= 0)
|
||||
menu->items[menu->cur_top_item - 2].state = MSIS_OnBorder;
|
||||
menu->items[menu->current_menu_option].state = MSIS_Inside;
|
||||
|
||||
menu->cur_top_item--;
|
||||
menu->cur_bottom_item--;
|
||||
|
||||
menu_scroll_fix_y(menu);
|
||||
}
|
||||
}
|
||||
|
||||
void menu_scroll_down(Menu *menu) {
|
||||
if (menu->current_menu_option > menu->cur_bottom_item) {
|
||||
if (menu->cur_top_item > 0)
|
||||
menu->items[menu->cur_top_item - 1].state = MSIS_Outside;
|
||||
menu->items[menu->cur_top_item].state = MSIS_OnBorder;
|
||||
|
||||
menu->items[menu->current_menu_option].state = MSIS_Inside;
|
||||
|
||||
if (menu->current_menu_option + 1 < menu->current_add_index)
|
||||
menu->items[menu->current_menu_option + 1].state = MSIS_OnBorder;
|
||||
|
||||
menu->cur_top_item++;
|
||||
menu->cur_bottom_item++;
|
||||
|
||||
menu_scroll_fix_y(menu);
|
||||
}
|
||||
}
|
331
src/libs/menu_utils/menu.h
Normal file
331
src/libs/menu_utils/menu.h
Normal file
@ -0,0 +1,331 @@
|
||||
#pragma once
|
||||
|
||||
#include <libdragon.h>
|
||||
|
||||
#include "mem_pool.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Enum with the order of the background sprites in a texture. Any texture has to have this
|
||||
* order.
|
||||
*/
|
||||
typedef enum MenuBackgroundSprite {
|
||||
SPRITE_menu_bottom,
|
||||
SPRITE_menu_bottom_left,
|
||||
SPRITE_menu_bottom_right,
|
||||
SPRITE_menu_center,
|
||||
SPRITE_menu_left,
|
||||
SPRITE_menu_right,
|
||||
SPRITE_menu_top,
|
||||
SPRITE_menu_top_left,
|
||||
SPRITE_menu_top_right,
|
||||
SPRITE_menu_SPRITES_MAX
|
||||
} MenuBackgroundSprite;
|
||||
|
||||
/**
|
||||
* @brief Enum that contains the state of a given item in relation with the current selected item.
|
||||
*/
|
||||
typedef enum MenuItemState {
|
||||
/// The item is inside the menu.
|
||||
MSIS_Inside,
|
||||
/// The item is on the edge of the menu.
|
||||
MSIS_OnBorder,
|
||||
/// The item is outside of the range of displayed items and will not be rendered.
|
||||
MSIS_Outside,
|
||||
} MenuItemState;
|
||||
|
||||
/**
|
||||
* @brief Struct that represents an item inside the menu. Used internally when using
|
||||
* 'menu_add_item*' functions.
|
||||
*/
|
||||
typedef struct MenuItem {
|
||||
/// Text that will be rendered. Not used when is an image.
|
||||
char *text;
|
||||
/// Sprite that will be rendered. Used only when an image.
|
||||
sprite_t *sprite;
|
||||
/// Index of the sprite that will be rendered. Used only when an image.
|
||||
int sprite_index;
|
||||
|
||||
/// X position of this item. Calculated whenever the state of the menu changes.
|
||||
int x;
|
||||
/// Y position of this item. Calculated whenever the state of the menu changes.
|
||||
int y;
|
||||
/// If the item is enabled. An enabled item can be used.
|
||||
bool enabled;
|
||||
/// If the item has custom colors. If false, will use the default colors. Default colors can be
|
||||
/// changed using 'menu_global_set_default_colors'.
|
||||
bool has_custom_colors;
|
||||
/// Color for when this item is enabled. Used when 'has_custom_colors' is true.
|
||||
uint32_t color_enabled;
|
||||
/// Color for when this item is disabled ('enabled' == false). Used when 'has_custom_colors' is
|
||||
/// true.
|
||||
uint32_t color_disabled;
|
||||
/// Color for when this item is selected. Used when 'has_custom_colors' is true.
|
||||
uint32_t color_selected;
|
||||
|
||||
/// State of the item in relation to the menu. @see MenuItemState
|
||||
MenuItemState state;
|
||||
|
||||
/// Custom object that is set by the user when creating the item.
|
||||
void *object;
|
||||
} MenuItem;
|
||||
|
||||
/**
|
||||
* @brief Optional callback for when an item is used.
|
||||
*
|
||||
* @param[in] option
|
||||
* The index of the used item.
|
||||
* @param[in] menu_item
|
||||
* A reference to the used menu item. @see MenuItem
|
||||
*/
|
||||
typedef void (*fnMenuCallback)(int option, MenuItem *menu_item);
|
||||
|
||||
/**
|
||||
* @brief Struct used to hold a Menu. Created by the 'menu_init' function.
|
||||
*/
|
||||
typedef struct Menu {
|
||||
/// Array of items of this menu.
|
||||
MenuItem *items;
|
||||
/// Maximum amount of items this menu can have. Also the size of 'items'.
|
||||
uint8_t total_items;
|
||||
/// Current index when inserting new MenuItems. Has the current amount of used items.
|
||||
uint8_t current_add_index;
|
||||
/// Current selected menu item index.
|
||||
int current_menu_option;
|
||||
|
||||
/// Current active submenu. If '-1', no submenu is active.
|
||||
int active_submenu;
|
||||
/// Array with references to all submenus.
|
||||
struct Menu **submenus;
|
||||
|
||||
/// If this menu should be rendered even when a submenu is active.
|
||||
bool display_when_on_submenu;
|
||||
|
||||
/// If this menu should display a background. Configured using 'menu_global_set_sprites' and
|
||||
/// 'menu_set_background'.
|
||||
bool display_background;
|
||||
/// Width of the background. Configured using 'menu_set_background'.
|
||||
int menu_width;
|
||||
|
||||
/// If the hand should be displayed for this menu
|
||||
bool display_hand;
|
||||
/// X position of the hand cursor. Configured using 'menu_global_set_sprites' and
|
||||
/// 'menu_set_hand'.
|
||||
int hand_position_x;
|
||||
/// Y offset for the hand cursor. Configured using 'menu_global_set_sprites' and
|
||||
/// 'menu_set_hand'.
|
||||
int hand_position_y_offset;
|
||||
|
||||
/// Amount of items that can be displayed at once.
|
||||
uint8_t item_vertical_limit;
|
||||
/// Height of each item inside the menu. Used to calculate Y position of items.
|
||||
uint8_t item_height;
|
||||
/// Y position of the menu.
|
||||
int top;
|
||||
/// X position of the menu.
|
||||
int left;
|
||||
/// Index of the item currently at the top of rendered menu.
|
||||
uint8_t cur_top_item;
|
||||
/// Index of the item currently at the bottom of rendered menu.
|
||||
uint8_t cur_bottom_item;
|
||||
|
||||
/// Callback function for when an item is used. @see fnMenuCallback
|
||||
fnMenuCallback callback;
|
||||
} Menu;
|
||||
|
||||
/**
|
||||
* @brief Allocates and returns a new Menu object.
|
||||
*
|
||||
* @param memory_pool
|
||||
* The memory pool to be used. If NULL will use malloc.
|
||||
* @param total_items
|
||||
* Total items that can be created inside the Menu.
|
||||
* @param max_items
|
||||
* Count of items that will be displayed at once.
|
||||
* @param top
|
||||
* Y position of the menu.
|
||||
* @param left
|
||||
* X position of the menu.
|
||||
* @param item_height
|
||||
* Height of each item inside the menu.
|
||||
* @param callback
|
||||
* Callback function for when an item is used. @see fnMenuCallback
|
||||
*
|
||||
* @return A new Menu object.
|
||||
*/
|
||||
Menu *menu_init(MemZone *memory_pool, uint8_t total_items, uint8_t max_items, int top, int left,
|
||||
uint8_t item_height, fnMenuCallback callback);
|
||||
/**
|
||||
* @brief Set the background for the Menu. Should only be called after calling
|
||||
* 'menu_global_set_sprites' at least once in the game.
|
||||
*
|
||||
* @param menu
|
||||
* Reference to the menu object.
|
||||
* @param menu_width
|
||||
* Width of the background.
|
||||
*/
|
||||
void menu_set_background(Menu *menu, int menu_width);
|
||||
/**
|
||||
* @brief Resets the items inside a menu. After calling this method you can add items to refill it.
|
||||
*
|
||||
* @param menu
|
||||
* Reference to the menu object.
|
||||
*/
|
||||
void menu_reset_items(Menu *menu);
|
||||
/**
|
||||
* @brief Set the hand cursor for the Menu. Should only be called after calling
|
||||
* 'menu_global_set_sprites' at least once in the game.
|
||||
*
|
||||
* @param menu
|
||||
* Reference to the menu object.
|
||||
* @param hand_position_x
|
||||
* X position for the hand cursor.
|
||||
* @param hand_position_y_offset
|
||||
* Y position offset for the hand cursor.
|
||||
*/
|
||||
void menu_set_hand(Menu *menu, int hand_position_x, int hand_position_y_offset);
|
||||
|
||||
/**
|
||||
* @brief Ticks the menu. Should be called every frame.
|
||||
*
|
||||
* @param menu
|
||||
* Reference to the menu object.
|
||||
* @param keys_down
|
||||
* Reference to the controller data for the current frame. You can get this data using the
|
||||
* 'get_keys_down()' function.
|
||||
*
|
||||
* @return Index of the item used, or '-1' is none.
|
||||
*/
|
||||
int menu_tick(Menu *menu, struct controller_data *keys_down);
|
||||
|
||||
/**
|
||||
* @brief Add a new text item to the menu without any custom colors.
|
||||
*
|
||||
* @param menu
|
||||
* Reference to the menu object.
|
||||
* @param text
|
||||
* Text that will be rendered.
|
||||
* @param enabled
|
||||
* If the item will start enabled.
|
||||
* @param object
|
||||
* Reference to a custom object that can be used later by the user.
|
||||
*/
|
||||
void menu_add_item(Menu *menu, char *text, bool enabled, void *object);
|
||||
/**
|
||||
* @brief Add a new image item to the menu.
|
||||
*
|
||||
* @param menu
|
||||
* Reference to the menu object.
|
||||
* @param sprite
|
||||
* Sprite that will be used by this item.
|
||||
* @param sprite_index
|
||||
* Index inside the sprite.
|
||||
* @param enabled
|
||||
* If the item will start enabled.
|
||||
* @param object
|
||||
* Reference to a custom object that can be used later by the user.
|
||||
*/
|
||||
void menu_add_item_image(Menu *menu, sprite_t *sprite, int sprite_index, bool enabled,
|
||||
void *object);
|
||||
/**
|
||||
* @brief Add a new text item to the menu with custom colors.
|
||||
*
|
||||
* @param menu
|
||||
* Reference to the menu object.
|
||||
* @param text
|
||||
* Text that will be rendered.
|
||||
* @param enabled
|
||||
* If the item will start enabled.
|
||||
* @param color_selected
|
||||
* Color for this object when selected.
|
||||
* @param color_enabled
|
||||
* Color for this object when enabled and not selected.
|
||||
* @param color_disabled
|
||||
* Color for this object when disabled and not selected.
|
||||
* @param object
|
||||
* Reference to a custom object that can be used later by the user.
|
||||
*/
|
||||
void menu_add_item_colored(Menu *menu, char *text, bool enabled, uint32_t color_selected,
|
||||
uint32_t color_enabled, uint32_t color_disabled, void *object);
|
||||
|
||||
/**
|
||||
* @brief Render the menu.
|
||||
*
|
||||
* @param menu
|
||||
* Reference to the menu object.
|
||||
* @param disp
|
||||
* The display context used in the game.
|
||||
*/
|
||||
void menu_render(Menu *menu, display_context_t disp);
|
||||
|
||||
/**
|
||||
* @brief Allocates and initializes submenus for the menu.
|
||||
*
|
||||
* @param menu
|
||||
* Reference to the menu object.
|
||||
* @param memory_pool
|
||||
* The memory pool to be used. If NULL will use malloc.
|
||||
* @param total_submenus
|
||||
* Total count of submenus that will be created.
|
||||
* @param display_when_on_submenu
|
||||
* If the menu will be displayed even when a submenu is active.
|
||||
*/
|
||||
void menu_init_submenus(Menu *menu, MemZone *memory_pool, uint8_t total_submenus,
|
||||
bool display_when_on_submenu);
|
||||
|
||||
/**
|
||||
* @brief Method that will render the borders of the menu.
|
||||
*/
|
||||
void menu_draw_background_borders(display_context_t disp, int top, int left, int bottom, int right);
|
||||
/**
|
||||
* @brief Method that will render the center of the menu.
|
||||
*/
|
||||
void menu_draw_background_center(display_context_t disp, int top, int left, int bottom, int right);
|
||||
|
||||
/**
|
||||
* @brief Initializes the menu default colors
|
||||
*/
|
||||
void menu_global_init();
|
||||
|
||||
/**
|
||||
* @brief Set the sprite used by the background menu
|
||||
*
|
||||
* @param menu_sprite
|
||||
* Sprite that will be used. See `sample_assets/menu.png` for example.
|
||||
* Order should be according to enum `MenuBackgroundSprite`.
|
||||
* @param hand_sprite
|
||||
* Sprite that will be used as the 'hand'.
|
||||
* @param hand_sprite_offset
|
||||
* Offset of the sprite (used by `graphics_draw_sprite_trans_stride`).
|
||||
*/
|
||||
void menu_global_set_sprites(sprite_t *menu_sprite, sprite_t *hand_sprite,
|
||||
uint8_t hand_sprite_offset);
|
||||
|
||||
/**
|
||||
* @brief Set the default colors for all menus when the item doesn't have any set.
|
||||
*
|
||||
* @param selected
|
||||
* Color used when the item is the current selected one. Default is {0,0,255,255}
|
||||
* @param enabled
|
||||
* Color used when the item is enabled but not selected. Default is {255,255,255,255}
|
||||
* @param disabled
|
||||
* Color used when the item is disabled. Default is {100,100,255,255}
|
||||
* @param background
|
||||
* Color used for the background of the text. Default is {0,0,0,0}
|
||||
* @param out_of_bounds
|
||||
* Color used when the item is out of the bounds of the menu (when scrolling). Default is
|
||||
* {0,0,0,255}
|
||||
* @param menu_background
|
||||
* Color used for the background. Due to 32 bits issue, we do not use the background from the
|
||||
* sprite.
|
||||
*/
|
||||
void menu_global_set_default_colors(uint32_t selected, uint32_t enabled, uint32_t disabled,
|
||||
uint32_t background, uint32_t out_of_bounds,
|
||||
uint32_t menu_background);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
BIN
src/libs/menu_utils/menu.png
Normal file
BIN
src/libs/menu_utils/menu.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 184 B |
Loading…
Reference in New Issue
Block a user