Menu work

This commit is contained in:
Dylan Smith
2026-01-15 15:53:02 -05:00
parent 66cb2436c7
commit b48cb11197
9 changed files with 248 additions and 63 deletions

View File

@@ -16,8 +16,8 @@ extern lv_font_t roboto_bold_large_font;
extern lv_font_t runescape_font;
#endif
uint16_t draw_character(pixel_t *framebuffer, const lv_font_t *font, const uint16_t x_loc, const uint16_t y_loc, const uint8_t character, pixel_t color);
uint16_t draw_character(pixel_t *framebuffer, const lv_font_t *font, const uint16_t x_loc, const uint16_t y_loc, const char character, pixel_t color);
void draw_string(pixel_t *framebuffer, const lv_font_t *font, const uint16_t x_loc, const uint16_t y_loc, const uint8_t *string, pixel_t color);
void draw_string(pixel_t *framebuffer, const lv_font_t *font, const uint16_t x_loc, const uint16_t y_loc, const char *string, pixel_t color);
#endif

View File

@@ -5,28 +5,46 @@
#include "stdbool.h"
typedef void (*menu_callback_t)(void *args);
typedef struct _graphical_menu_layout_t graphical_menu_layout_t;
typedef struct _graphical_menu_t graphical_menu_t;
// TODO
// typedef struct {
// uint16_t x;
// uint16_t y;
// uint16_t width;
// uint16_t height;
// } graphical_menu_position_t;
typedef struct
{
uint8_t *title;
bool enabled; // not enabled = grayed out, unhighlightable
menu_callback_t highlighted_callback_function;
void *highlighted_callback_function_args;
menu_callback_t selected_callback_function;
void *selected_callback_function_args;
const char *const title;
const bool enabled; // not enabled = grayed out, unhighlightable
const menu_callback_t highlighted_callback_function;
void *const highlighted_callback_function_args;
const menu_callback_t selected_callback_function;
void *const selected_callback_function_args;
} graphical_menu_entry_t;
typedef struct _graphical_menu_t graphical_menu_t;
struct _graphical_menu_t {
graphical_menu_t *parent_menu;
uint8_t num_entries;
graphical_menu_entry_t *entries;
struct _graphical_menu_layout_t {
graphical_menu_t *const parent_menu;
const uint8_t num_entries;
const graphical_menu_entry_t *const entries;
};
struct _graphical_menu_t {
const graphical_menu_layout_t *const layout;
uint8_t selected_entry_idx;
};
void draw_menu(const graphical_menu_t *const menu);
void select_menu_entry(const graphical_menu_t *const menu);
void select_menu_entry(graphical_menu_t *const menu);
uint8_t get_selected_menu_entry_idx(void);
void set_selected_menu_entry_idx(const graphical_menu_t *const menu, uint8_t idx);
void set_selected_menu_entry_idx(graphical_menu_t *const menu, int8_t idx);
void decrement_selected_menu_entry_idx(graphical_menu_t *const menu);
void increment_selected_menu_entry_idx(graphical_menu_t *const menu);
#endif

View File

@@ -374,6 +374,16 @@ void Error_Handler(void);
/* Size of read registers */
#define LCD_READ_ID4_SIZE 3 /* Size of Read ID4 */
/******************** */
/* Button definitions */
#define DOWN_BUTTON_PRESSED() (HAL_GPIO_ReadPin(BUTTON2_GPIO_Port, BUTTON2_Pin) == GPIO_PIN_RESET)
#define RIGHT_BUTTON_PRESSED() (HAL_GPIO_ReadPin(BUTTON3_GPIO_Port, BUTTON3_Pin) == GPIO_PIN_RESET)
#define LEFT_BUTTON_PRESSED() (HAL_GPIO_ReadPin(BUTTON1_GPIO_Port, BUTTON1_Pin) == GPIO_PIN_RESET)
#define OK_BUTTON_PRESSED() (HAL_GPIO_ReadPin(BUTTON4_GPIO_Port, BUTTON4_Pin) == GPIO_PIN_RESET)
#define UP_BUTTON_PRESSED() (HAL_GPIO_ReadPin(BUTTON5_GPIO_Port, BUTTON5_Pin) == GPIO_PIN_RESET)
/* USER CODE END Private defines */
#ifdef __cplusplus

16
Core/Inc/ui.h Normal file
View File

@@ -0,0 +1,16 @@
#ifndef UI_H
#define UI_H
#include "stdint.h"
#include "stdbool.h"
void ui_left_button_pressed(void);
void ui_right_button_pressed(void);
void ui_up_button_pressed(void);
void ui_down_button_pressed(void);
void ui_ok_button_pressed(void);
void ui_request_redraw(void);
void ui_task(void);
#endif

View File

@@ -71,7 +71,7 @@ const void * lv_font_get_bitmap_fmt_txt(lv_font_fmt_txt_glyph_dsc_t * g_dsc, lv_
* @note If the character is not found in the font's character map, the function returns
* 0 without drawing anything.
*/
uint16_t draw_character(pixel_t *framebuffer, const lv_font_t *font, const uint16_t x_loc, const uint16_t y_loc, const uint8_t character, pixel_t color)
uint16_t draw_character(pixel_t *framebuffer, const lv_font_t *font, const uint16_t x_loc, const uint16_t y_loc, const char character, pixel_t color)
{
const lv_font_fmt_txt_dsc_t *font_dsc = (lv_font_fmt_txt_dsc_t *)font->dsc;
lv_font_fmt_txt_glyph_dsc_t glyph_dsc;
@@ -140,11 +140,11 @@ uint16_t draw_character(pixel_t *framebuffer, const lv_font_t *font, const uint1
* @note All characters in the string are drawn on the same horizontal baseline (y_loc
* remains constant for all characters).
*/
void draw_string(pixel_t *framebuffer, const lv_font_t *font, const uint16_t x_loc, const uint16_t y_loc, const uint8_t *string, pixel_t color)
void draw_string(pixel_t *framebuffer, const lv_font_t *font, const uint16_t x_loc, const uint16_t y_loc, const char *string, pixel_t color)
{
uint32_t current_x = x_loc << 4;
for (const uint8_t *char_ptr = string; *char_ptr != '\0'; char_ptr++)
for (const char *char_ptr = string; *char_ptr != '\0'; char_ptr++)
{
uint16_t advance_width = draw_character(framebuffer, font, current_x >> 4, y_loc, *char_ptr, color);
current_x += advance_width;

View File

@@ -2,8 +2,7 @@
#include "graphics.h"
#include "fonts/font.h"
#include "display.h"
static uint8_t selected_entry_idx = 0;
#include "ui.h"
void draw_menu(const graphical_menu_t *const menu)
{
@@ -15,18 +14,14 @@ void draw_menu(const graphical_menu_t *const menu)
const pixel_t entry_bg_color = MAKE_PIXEL(255, 255, 255);
const pixel_t highlighted_bg_color = MAKE_PIXEL(0, 0, 0);
const pixel_t highlighted_text_color = MAKE_PIXEL(255, 255, 0);
DrawBox(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT, entry_bg_color);
// Ensure selected_entry_idx is within bounds
if (selected_entry_idx >= menu->num_entries)
{
selected_entry_idx = 0;
}
for (uint8_t i = 0; i < menu->num_entries; i++)
for (uint8_t i = 0; i < menu->layout->num_entries; i++)
{
const uint16_t y_pos = i * entry_height;
const graphical_menu_entry_t *entry = &menu->entries[i];
const bool is_highlighted = (i == selected_entry_idx) && entry->enabled;
const graphical_menu_entry_t *entry = &menu->layout->entries[i];
const bool is_highlighted = (i == menu->selected_entry_idx) && entry->enabled;
// Draw entry background - use highlighted color if this is the selected entry
pixel_t bg_color = is_highlighted ? highlighted_bg_color : entry_bg_color;
@@ -51,15 +46,15 @@ void draw_menu(const graphical_menu_t *const menu)
}
}
void select_menu_entry(const graphical_menu_t *const menu)
void select_menu_entry(graphical_menu_t *const menu)
{
// Ensure selected_entry_idx is within bounds
if (selected_entry_idx >= menu->num_entries)
if (menu->selected_entry_idx >= menu->layout->num_entries)
{
return;
}
const graphical_menu_entry_t *entry = &menu->entries[selected_entry_idx];
const graphical_menu_entry_t *entry = &menu->layout->entries[menu->selected_entry_idx];
// Only select if the entry is enabled
if (entry->enabled && entry->selected_callback_function != NULL)
@@ -68,28 +63,37 @@ void select_menu_entry(const graphical_menu_t *const menu)
}
}
uint8_t get_selected_menu_entry_idx(void)
void set_selected_menu_entry_idx(graphical_menu_t *const menu, int8_t idx)
{
return selected_entry_idx;
}
void set_selected_menu_entry_idx(const graphical_menu_t *const menu, uint8_t idx)
{
// Clamp the index to valid range
if (idx >= menu->num_entries)
{
idx = menu->num_entries > 0 ? menu->num_entries - 1 : 0;
}
// Only allow selecting enabled entries
if (menu->entries[idx].enabled)
while (!menu->layout->entries[idx].enabled)
{
selected_entry_idx = idx;
// Call highlighted callback if it exists
if (menu->entries[idx].highlighted_callback_function != NULL)
if (idx >menu->selected_entry_idx)
{
menu->entries[idx].highlighted_callback_function(menu->entries[idx].highlighted_callback_function_args);
idx--;
}
else
{
idx++;
}
}
// Clamp the index to valid range
if (idx < 0 || idx >= menu->layout->num_entries)
{
idx = menu->layout->num_entries > 0 ? menu->layout->num_entries - 1 : 0;
}
menu->selected_entry_idx = idx;
ui_request_redraw();
}
void decrement_selected_menu_entry_idx(graphical_menu_t *const menu)
{
set_selected_menu_entry_idx(menu, menu->selected_entry_idx - 1);
}
void increment_selected_menu_entry_idx(graphical_menu_t *const menu)
{
set_selected_menu_entry_idx(menu, menu->selected_entry_idx + 1);
}

View File

@@ -19,6 +19,7 @@
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "cmsis_os.h"
#include "ui.h"
#include "usb_host.h"
/* Private includes ----------------------------------------------------------*/
@@ -915,15 +916,6 @@ static void MX_GPIO_Init(void)
/* USER CODE BEGIN 4 */
const graphical_menu_t menu_main = {
.num_entries = 3,
.entries = (graphical_menu_entry_t[]){
{.title = "where varock", .enabled = true, .highlighted_callback_function = NULL, .highlighted_callback_function_args = NULL, .selected_callback_function = NULL, .selected_callback_function_args = NULL},
{.title = "Settings", .enabled = true, .highlighted_callback_function = NULL, .highlighted_callback_function_args = NULL, .selected_callback_function = NULL, .selected_callback_function_args = NULL},
{.title = "About", .enabled = true, .highlighted_callback_function = NULL, .highlighted_callback_function_args = NULL, .selected_callback_function = NULL, .selected_callback_function_args = NULL},
}
};
/* USER CODE END 4 */
/* USER CODE BEGIN Header_StartDefaultTask */
@@ -939,14 +931,32 @@ void StartDefaultTask(void const * argument)
/* USER CODE BEGIN 5 */
ILI9341_Init();
uint8_t time_string[20] = {0};
DisplayTest(0xFFFF);
draw_menu(&menu_main);
/* Infinite loop */
for(;;)
{
if (UP_BUTTON_PRESSED())
{
ui_up_button_pressed();
}
if (DOWN_BUTTON_PRESSED())
{
ui_down_button_pressed();
}
if (LEFT_BUTTON_PRESSED())
{
ui_left_button_pressed();
}
if (RIGHT_BUTTON_PRESSED())
{
ui_right_button_pressed();
}
if (OK_BUTTON_PRESSED())
{
ui_ok_button_pressed();
}
ui_task();
osDelay(100);
}
/* USER CODE END 5 */
}

126
Core/Src/ui.c Normal file
View File

@@ -0,0 +1,126 @@
#include "ui.h"
#include "graphics.h"
#include "menu.h"
void ui_enter_submenu(void *const args);
/******************* */
/* Menu declarations */
/******************* */
static graphical_menu_t main_menu;
static graphical_menu_t runescape_memes_menu;
/******************* */
/* Menu definitions */
/******************* */
static const graphical_menu_layout_t main_menu_layout = {
.parent_menu = 0,
.num_entries = 3,
.entries = (graphical_menu_entry_t[]){
{.title = "runescape memes", .enabled = true, .highlighted_callback_function = 0, .highlighted_callback_function_args = 0, .selected_callback_function = ui_enter_submenu, .selected_callback_function_args = &runescape_memes_menu},
{.title = "Settings", .enabled = true, .highlighted_callback_function = 0, .highlighted_callback_function_args = 0, .selected_callback_function = 0, .selected_callback_function_args = 0},
{.title = "About", .enabled = true, .highlighted_callback_function = 0, .highlighted_callback_function_args = 0, .selected_callback_function = 0, .selected_callback_function_args = 0},
}
};
static const graphical_menu_layout_t runescape_memes_menu_layout = {
.parent_menu = &main_menu,
.num_entries = 8,
.entries = (graphical_menu_entry_t[]){
{.title = "where varock", .enabled = true, .highlighted_callback_function = 0, .highlighted_callback_function_args = 0, .selected_callback_function = 0, .selected_callback_function_args = 0},
{.title = "buying gf", .enabled = true, .highlighted_callback_function = 0, .highlighted_callback_function_args = 0, .selected_callback_function = 0, .selected_callback_function_args = 0},
{.title = "le goblin", .enabled = true, .highlighted_callback_function = 0, .highlighted_callback_function_args = 0, .selected_callback_function = 0, .selected_callback_function_args = 0},
{.title = "you chop the tree", .enabled = true, .highlighted_callback_function = 0, .highlighted_callback_function_args = 0, .selected_callback_function = 0, .selected_callback_function_args = 0},
{.title = "you chop the tree", .enabled = true, .highlighted_callback_function = 0, .highlighted_callback_function_args = 0, .selected_callback_function = 0, .selected_callback_function_args = 0},
{.title = "you chop the tree", .enabled = true, .highlighted_callback_function = 0, .highlighted_callback_function_args = 0, .selected_callback_function = 0, .selected_callback_function_args = 0},
{.title = "you chop the tree", .enabled = true, .highlighted_callback_function = 0, .highlighted_callback_function_args = 0, .selected_callback_function = 0, .selected_callback_function_args = 0},
{.title = "you chop the tree", .enabled = true, .highlighted_callback_function = 0, .highlighted_callback_function_args = 0, .selected_callback_function = 0, .selected_callback_function_args = 0},
}
};
static graphical_menu_t main_menu = {
.layout = &main_menu_layout,
.selected_entry_idx = 0,
};
static graphical_menu_t runescape_memes_menu = {
.layout = &runescape_memes_menu_layout,
.selected_entry_idx = 0,
};
/********************** */
/* UI state definitions */
/********************** */
enum ui_state_t {
UI_STATE_MENU,
};
static bool redraw_requested = true;
static enum ui_state_t ui_state = UI_STATE_MENU;
static graphical_menu_t *current_menu = &main_menu;
/************************* */
/* UI function definitions */
/************************* */
void ui_enter_submenu(void *const args)
{
current_menu = (graphical_menu_t *)args;
redraw_requested = true;
}
void ui_exit_submenu(void)
{
if (current_menu->layout->parent_menu == 0)
{
return;
}
current_menu = current_menu->layout->parent_menu;
redraw_requested = true;
}
void ui_left_button_pressed(void)
{
// decrement_selected_menu_entry_idx(&main_menu);
ui_exit_submenu();
}
void ui_right_button_pressed(void)
{
// increment_selected_menu_entry_idx(&main_menu);
}
void ui_up_button_pressed(void)
{
decrement_selected_menu_entry_idx(current_menu);
}
void ui_down_button_pressed(void)
{
increment_selected_menu_entry_idx(current_menu);
}
void ui_ok_button_pressed(void)
{
select_menu_entry(current_menu);
}
void ui_request_redraw(void)
{
redraw_requested = true;
}
void ui_task(void)
{
if (redraw_requested)
{
redraw_requested = false;
draw_menu(current_menu);
}
}

View File

@@ -90,6 +90,7 @@ Core/Src/stm32f4xx_it.c \
Core/Src/syscalls.c \
Core/Src/sysmem.c \
Core/Src/system_stm32f4xx.c \
Core/Src/ui.c \
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal.c \
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_cortex.c \
Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_crc.c \