diff --git a/Core/Inc/graphics/fonts/font.h b/Core/Inc/graphics/fonts/font.h index 376f8c4..0ab32fc 100644 --- a/Core/Inc/graphics/fonts/font.h +++ b/Core/Inc/graphics/fonts/font.h @@ -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 \ No newline at end of file diff --git a/Core/Inc/graphics/menu.h b/Core/Inc/graphics/menu.h index 777a00e..efecafe 100644 --- a/Core/Inc/graphics/menu.h +++ b/Core/Inc/graphics/menu.h @@ -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 \ No newline at end of file diff --git a/Core/Inc/main.h b/Core/Inc/main.h index 793b1bf..a1fc20c 100644 --- a/Core/Inc/main.h +++ b/Core/Inc/main.h @@ -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 diff --git a/Core/Inc/ui.h b/Core/Inc/ui.h new file mode 100644 index 0000000..8773d41 --- /dev/null +++ b/Core/Inc/ui.h @@ -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 \ No newline at end of file diff --git a/Core/Src/graphics/fonts/font.c b/Core/Src/graphics/fonts/font.c index b952005..132360b 100644 --- a/Core/Src/graphics/fonts/font.c +++ b/Core/Src/graphics/fonts/font.c @@ -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; diff --git a/Core/Src/graphics/menu.c b/Core/Src/graphics/menu.c index c911c24..f054152 100644 --- a/Core/Src/graphics/menu.c +++ b/Core/Src/graphics/menu.c @@ -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); } \ No newline at end of file diff --git a/Core/Src/main.c b/Core/Src/main.c index 04901a1..23ae4cd 100644 --- a/Core/Src/main.c +++ b/Core/Src/main.c @@ -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 */ } diff --git a/Core/Src/ui.c b/Core/Src/ui.c new file mode 100644 index 0000000..bd35592 --- /dev/null +++ b/Core/Src/ui.c @@ -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); + } +} \ No newline at end of file diff --git a/STM32Make.make b/STM32Make.make index da9a5ba..e2414ca 100644 --- a/STM32Make.make +++ b/STM32Make.make @@ -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 \