#include #include #include #include #include #include #include #include "liquid.h" #include "graphics/drawing.h" #include "graphics/display_spec.h" #include "scene_menu.h" #if 0 static void paint(struct MenuScene *self) { LCD_clearDisplay(0); int row = 0; for (int i = self->scroll_up; i < MIN(self->items_len, self->scroll_up + self->visible_lines); i++, row++) { bool selected = self->selected==i; LCD_setStrEx(self->items[i].label, 0, row * 8, selected?WHITE:BLACK,FONT_NORMAL|(selected?FONT_BG:0)); } } #endif #define HSCROLL_START_MS 750 #define HSCROLL_MS 250 void MenuScene_Paint(struct MenuScene *self) { LCD_setRect(self->x, self->y, self->x + self->ncols*6, self->y + self->nlines*9, 1, WHITE); int row = 0; for (int i = self->scroll_up; i < MIN(self->items_len, self->scroll_up + self->nlines); i++, row++) { bool selected = self->selected==i; if (selected) LCD_setRect(self->x, self->y + row*9, self->x + self->ncols*6, self->y + row*9 + 8, 1, 1); size_t shift = 0; if (selected) { size_t len = utf8_strlen(self->items[i].label); shift = self->hscroll; if (shift > len - self->ncols) { shift = len - self->ncols; } } LCD_setStrEx(self->items[i].label, self->x + 1, self->y + row * 9 + 1, !selected, (struct TextStyle) { .bg = selected, .limit = self->ncols, .skip = shift, }); } } static struct SceneEvent onInput(struct MenuScene *self, struct InputEvent event) { switch (event.kind) { case InputEventKind_Wheel:; self->selected += event.wheel.direction; if (self->selected < 0) { if (self->wraparound) { self->selected = self->items_len - 1; } else { self->selected = 0; } } else if (self->selected >= self->items_len) { if (self->wraparound) { self->selected = 0; } else { self->selected = self->items_len - 1; } } // hacky scroll adjust while (self->selected - self->scroll_up < 1 && self->scroll_up > 0) { self->scroll_up--; } while (self->selected - self->scroll_up >= (self->nlines - 1) && self->scroll_up < self->items_len - self->nlines) { self->scroll_up++; } // reset hscroll self->hscroll_cnt = 0; self->hscroll = 0; return SceneEvent_Repaint(); case InputEventKind_Button: if (event.button.state) { if (self->onSelect) { return self->onSelect(self); } return SceneEvent_Close(0, NULL); } // fall through default: return SceneEvent_None(); } } static struct SceneEvent onTick(struct MenuScene *self, uint32_t millis) { size_t len = utf8_strlen(self->items[self->selected].label); self->hscroll_cnt += millis; if (len > self->ncols) { size_t limit = (self->hscroll == 0 ? HSCROLL_START_MS : HSCROLL_MS); if (self->hscroll_cnt >= limit) { self->hscroll_cnt -= limit; self->hscroll++; if (self->hscroll > len - self->ncols + 2/* trailing delay */) { self->hscroll = 0; } return SceneEvent_Repaint(); } } else { self->hscroll = 0; // ensure periodic repaint (for parent) if (self->hscroll_cnt > 20) { return SceneEvent_Repaint(); } } return SceneEvent_None(); } static void deinit(struct MenuScene *self) { free(self->items); self->items = NULL; if (self->extra_deinit) { self->extra_deinit((struct Scene *) self); self->extra = NULL; } } struct MenuScene *NewScene_Menu(struct MenuItem *items, size_t items_len) { struct MenuScene *scene = calloc(1, sizeof(struct MenuScene)); scene->base.paint = (Scene_paint_t) MenuScene_Paint; scene->base.onInput = (Scene_onInput_t) onInput; scene->base.deinit = (Scene_deinit_t) deinit; scene->base.onTick = (Scene_onTick_t) onTick; // onChildReturn is free to set by subclass // TODO long selected label scrolling on tick scene->items = items; scene->items_len = items_len; scene->nlines = 5; scene->ncols = 14; scene->x = 0; scene->y = 0; return scene; }