|
|
|
#include <malloc.h>
|
|
|
|
#include <graphics/bitmaps.h>
|
|
|
|
#include <sys/param.h>
|
|
|
|
#include <stdbool.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <liquid/input_event.h>
|
|
|
|
|
|
|
|
#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
|
|
|
|
|
|
|
|
static void 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,
|
|
|
|
.nowrap = 1,
|
|
|
|
.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) 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;
|
|
|
|
}
|