Files
ui-library-playground/Core/Src/graphics/menu.c
2026-01-16 16:28:00 -05:00

161 lines
5.3 KiB
C

#include "menu.h"
#include "graphics.h"
#include "fonts/font.h"
#include "display.h"
static const uint16_t entry_height = 40;
static const uint16_t padding_x = 10;
static const uint16_t padding_y = 0;
static const pixel_t enabled_text_color = MAKE_PIXEL(255, 255, 255);
static const pixel_t disabled_text_color = MAKE_PIXEL(80, 80, 80);
static const pixel_t entry_bg_color = MAKE_PIXEL(0, 0, 0);
static const pixel_t highlighted_bg_color = MAKE_PIXEL(50, 50, 50);
static const pixel_t highlighted_text_color = MAKE_PIXEL(255, 255, 0);
void draw_menu_entry(volatile pixel_t *const framebuffer, const graphical_menu_t *const menu, uint8_t entry_idx)
{
const uint16_t y_pos = entry_idx * entry_height;
const graphical_menu_entry_t *entry = &menu->children[entry_idx];
const bool is_highlighted = (entry_idx == menu->highlighted_child_idx) && !entry->disabled;
// Draw entry background - use highlighted color if this is the selected entry
pixel_t bg_color = is_highlighted ? highlighted_bg_color : entry_bg_color;
DrawBox(0, y_pos + 1, DISPLAY_WIDTH, entry_height - 1, bg_color);
// Draw entry text - use highlighted color if selected, otherwise use enabled/disabled color
pixel_t text_color;
if (is_highlighted)
{
text_color = highlighted_text_color;
}
else
{
text_color = !entry->disabled ? enabled_text_color : disabled_text_color;
}
// Calculate baseline from top position: baseline = top + (line_height - base_line)
const uint16_t text_baseline_y = y_pos + padding_y + (roboto_bold_font.line_height - roboto_bold_font.base_line);
draw_string(framebuffer, &roboto_bold_font,
padding_x, text_baseline_y,
entry->title, text_color);
// draw a line between the menu entries
if (entry_idx != 0)
{
DrawBox(5, y_pos, DISPLAY_WIDTH - 10, 1, MAKE_PIXEL(80, 80, 80));
}
if (entry->extra_draw_function != NULL)
{
const menu_entry_size_t menu_entry_size = {
.x = 0,
.y = y_pos,
.width = DISPLAY_WIDTH,
.height = entry_height,
};
entry->extra_draw_function(&menu_entry_size, entry->extra_draw_function_args);
}
}
void draw_menu(volatile pixel_t *const framebuffer, const graphical_menu_t *const menu)
{
DrawBox(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT, entry_bg_color);
for (uint8_t i = 0; i < menu->num_children; i++)
{
draw_menu_entry(framebuffer, menu, i);
}
}
void partial_redraw_menu(volatile pixel_t *const framebuffer, graphical_menu_t *const menu, uint8_t old_highlighted_entry_idx, uint8_t new_highlighted_entry_idx)
{
draw_menu_entry(framebuffer, menu, old_highlighted_entry_idx);
draw_menu_entry(framebuffer, menu, new_highlighted_entry_idx);
}
void select_menu_entry(graphical_menu_t *const menu)
{
// Ensure selected_entry_idx is within bounds
if (menu->highlighted_child_idx >= menu->num_children)
{
return;
}
const graphical_menu_entry_t *entry = &menu->children[menu->highlighted_child_idx];
// Only select if the entry is enabled
if (!entry->disabled && entry->selected_callback_function != NULL)
{
entry->selected_callback_function(entry->selected_callback_function_args);
}
}
void set_selected_menu_entry_idx(volatile pixel_t *const framebuffer, graphical_menu_t *const menu, uint8_t idx)
{
const uint16_t old_highlighted_entry_idx = menu->highlighted_child_idx;
// Clamp the index to valid range
if (idx >= menu->num_children)
{
if (menu->num_children == 0) return;
idx = menu->num_children - 1;
}
menu->highlighted_child_idx = idx;
partial_redraw_menu(framebuffer, menu, old_highlighted_entry_idx, idx);
}
void decrement_selected_menu_entry_idx(volatile pixel_t *const framebuffer, graphical_menu_t *const menu)
{
uint8_t idx = menu->highlighted_child_idx;
// Only allow selecting enabled entries
do
{
if (idx == 0)
{
idx = menu->num_children - 1;
}
else
{
idx--;
}
}
while (menu->children[idx].disabled);
set_selected_menu_entry_idx(framebuffer, menu, idx);
}
void increment_selected_menu_entry_idx(volatile pixel_t *const framebuffer, graphical_menu_t *const menu)
{
uint8_t idx = (menu->highlighted_child_idx + 1) % menu->num_children;
// Only allow selecting enabled entries
while (menu->children[idx].disabled)
{
idx = (idx + 1) % menu->num_children;
}
set_selected_menu_entry_idx(framebuffer, menu, idx);
}
void add_toggle_switch_to_menu_entry(const menu_entry_size_t *const menu_entry_size, void *const toggle_switch_value)
{
const uint16_t width_padding = 10;
const uint16_t toggle_switch_width = 40;
const uint16_t toggle_switch_height = 20;
const uint16_t x = menu_entry_size->x + ( (menu_entry_size->width - toggle_switch_width)) - width_padding;
const uint16_t y = menu_entry_size->y + ( (menu_entry_size->height / 2) - (toggle_switch_height / 2));
const toggle_switch_t toggle_switch = {
.x = x,
.y = y,
.width = toggle_switch_width,
.height = toggle_switch_height,
.inner_padding_pixels = 3,
.value = *(bool *)toggle_switch_value,
};
draw_toggle_switch(next_framebuffer, &toggle_switch);
}