You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
116 lines
3.6 KiB
116 lines
3.6 KiB
//
|
|
// Created by MightyPork on 2023/04/09.
|
|
//
|
|
|
|
#include <stdbool.h>
|
|
#include <string.h>
|
|
#include "screen_menu.h"
|
|
#include "app_gui.h"
|
|
#include "FreeRTOS.h"
|
|
#include "task.h"
|
|
#include "ufb/utf8.h"
|
|
#include "ufb/framebuffer_config.h"
|
|
#include "ufb/framebuffer.h"
|
|
#include "ufb/fb_text.h"
|
|
|
|
struct menu_state {
|
|
int pos;
|
|
int len;
|
|
uint32_t change_time;
|
|
uint32_t slide_end_time;
|
|
uint16_t text_slide;
|
|
void * last_opts;
|
|
} s_menu;
|
|
|
|
|
|
void screen_menu(GuiEvent event, const char **options, menu_callback_t cb) {
|
|
screen_menu_offset(event, options, cb, 15);
|
|
}
|
|
|
|
void screen_menu_offset(GuiEvent event, const char **options, menu_callback_t cb, fbpos_t offset) {
|
|
if (event != GUI_EVENT_SCREEN_INIT && s_menu.last_opts != (void*) options) {
|
|
// ensure the menu is properly inited when the options list changes.
|
|
screen_menu(GUI_EVENT_SCREEN_INIT, options, cb);
|
|
}
|
|
|
|
bool menu_changed = false;
|
|
const uint32_t tickNow = xTaskGetTickCount();
|
|
|
|
switch (event) {
|
|
case GUI_EVENT_SCREEN_INIT:
|
|
memset(&s_menu, 0, sizeof(s_menu));
|
|
s_menu.last_opts = (void*) options;
|
|
|
|
s_menu.change_time = tickNow;
|
|
|
|
// count options
|
|
const char **opt = options;
|
|
while (*opt) {
|
|
s_menu.len++;
|
|
opt++;
|
|
}
|
|
break;
|
|
|
|
case GUI_EVENT_SCREEN_TICK:
|
|
// long text sliding animation
|
|
if (tickNow - s_menu.change_time >= pdMS_TO_TICKS(500)) {
|
|
const uint32_t textlen = utf8_strlen(options[s_menu.pos]) * 6;
|
|
if (textlen >= FBW - 2) {
|
|
if (textlen - s_menu.text_slide > FBW - 1) {
|
|
s_menu.text_slide += 1;
|
|
if (textlen - s_menu.text_slide >= FBW - 1) {
|
|
s_menu.slide_end_time = tickNow;
|
|
}
|
|
} else if (tickNow - s_menu.slide_end_time >= pdMS_TO_TICKS(500)) {
|
|
s_menu.change_time = tickNow;
|
|
s_menu.slide_end_time = 0;
|
|
s_menu.text_slide = 0;
|
|
}
|
|
request_paint();
|
|
}
|
|
}
|
|
break;
|
|
|
|
case GUI_EVENT_PAINT:
|
|
for (int i = 0; i < s_menu.len; i++) {
|
|
// is the row currently rendered the selected row?
|
|
const bool is_selected = s_menu.pos == i;
|
|
const fbcolor_t color = !is_selected; // text color - black if selected, because it's inverted
|
|
const fbpos_t y = 12 + offset + i * 10 ;
|
|
fb_rect(0, y, FBW, 10, !color);
|
|
fb_text(1 - (is_selected ? s_menu.text_slide : 0), y + 1, options[i], FONT_5X7, color);
|
|
// ensure the text doesn't touch the edge (looks ugly)
|
|
fb_vline(FBW - 1, y, 10, !color);
|
|
fb_vline(0, y, 10, !color);
|
|
}
|
|
break;
|
|
|
|
// the button is held! release is what activates the button
|
|
case GUI_EVENT_KNOB_RELEASE:
|
|
input_sound_effect();
|
|
cb(s_menu.pos);
|
|
break;
|
|
|
|
case GUI_EVENT_KNOB_PLUS:
|
|
if (s_menu.pos < s_menu.len - 1) {
|
|
s_menu.pos++;
|
|
menu_changed = true;
|
|
}
|
|
break;
|
|
|
|
case GUI_EVENT_KNOB_MINUS:
|
|
if (s_menu.pos > 0) {
|
|
s_menu.pos--;
|
|
menu_changed = true;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (menu_changed) {
|
|
s_menu.change_time = tickNow;
|
|
s_menu.text_slide = 0;
|
|
s_menu.slide_end_time = 0;
|
|
input_sound_effect();
|
|
request_paint();
|
|
}
|
|
}
|
|
|