#include #include #include #include #include "liquid.h" #include "liquid/input_event.h" #include "graphics/drawing.h" #include "graphics/display_spec.h" #include "scene_string.h" enum Focus { FOC_CHAR, FOC_CHAR_EDIT, FOC_SUBMIT, FOC_CANCEL, }; struct StringScene { struct Scene base; struct StringSceneOpts *opts; int editedchar; int length; enum Focus foc; }; // !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ const char SYMBOLS[] = " ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; static void onWheel(struct StringScene *self, struct InputEvent event); static struct SceneEvent onButton(struct StringScene *self, struct InputEvent event); static void ensureCharNotNull(struct StringScene *self); static void trimTailSpaces(struct StringScene *self); static struct SceneEvent onInput(struct StringScene *self, struct InputEvent event) { switch (event.kind) { case InputEventKind_Wheel: onWheel(self, event); return SceneEvent_Repaint(); case InputEventKind_Button: return onButton(self, event); default: return SceneEvent_None(); } } static struct SceneEvent onButton(struct StringScene *self, struct InputEvent event) { if (event.button.state) { switch (self->foc) { case FOC_CHAR: printf("start char edit\n"); self->foc = FOC_CHAR_EDIT; ensureCharNotNull(self); return SceneEvent_Repaint(); case FOC_CHAR_EDIT: if (self->editedchar == self->length) { printf("submit last char\n"); if (self->opts->value[self->editedchar] == ' ') { printf("empty, go to -> submit\n"); self->foc = FOC_SUBMIT; } else { printf("not empty, advance and keep editing\n"); self->length += 1; self->editedchar += 1; } } else { printf("exit char edit\n"); self->foc = FOC_CHAR; } return SceneEvent_Repaint(); case FOC_SUBMIT: return SceneEvent_Close(1, NULL); // TODO copy str case FOC_CANCEL: return SceneEvent_Close(0, NULL); } } return SceneEvent_None(); } static void onWheel(struct StringScene *self, struct InputEvent event) { switch (self->foc) { case FOC_CHAR: self->editedchar += event.wheel.direction; printf("Move cursor %d\n", event.wheel.direction); if (self->editedchar < 0) { self->editedchar = 0; self->foc = FOC_CANCEL; printf("chars -> cancel\n"); } else if (self->editedchar > self->length) { self->editedchar = self->length; self->foc = FOC_SUBMIT; printf("chars -> submit\n"); } else { printf("move within chars\n"); ensureCharNotNull(self); } trimTailSpaces(self); break; case FOC_CHAR_EDIT:; // FIXME this looks buggy const char *pos = strchr(SYMBOLS, self->opts->value[self->editedchar]) + event.wheel.direction; if (pos < SYMBOLS) { pos = SYMBOLS + strlen(SYMBOLS) - 1; } else if (*pos == 0) { pos = SYMBOLS; } self->opts->value[self->editedchar] = *pos; printf("char %d change: %c\n", self->editedchar, *pos); break; case FOC_SUBMIT: if (event.wheel.direction > 0) { printf("submit -> cancel\n"); self->foc = FOC_CANCEL; } else { printf("submit -> last char\n"); self->foc = FOC_CHAR; self->editedchar = self->length; } break; case FOC_CANCEL: if (event.wheel.direction > 0) { printf("cancel -> first char\n"); self->foc = FOC_CHAR; self->editedchar = 0; } else { printf("cancel -> submit\n"); self->foc = FOC_SUBMIT; } break; } } static void paint(struct StringScene *self) { struct StringSceneOpts *opts = self->opts; LCD_clearDisplay(0); ensureCharNotNull(self); LCD_setStrEx(opts->value, 1, 1, BLACK, (struct TextStyle) { .limit = MAX(self->editedchar+1, self->length), }); if (self->foc == FOC_CHAR) { LCD_setRect(self->editedchar*6, 8, self->editedchar*6+5, 8, false, BLACK); } else if (self->foc == FOC_CHAR_EDIT) { LCD_setRect(self->editedchar*6, 8, self->editedchar*6+5, 9, false, BLACK); } LCD_setStrEx("Ok", 50, 15, self->foc == FOC_SUBMIT ? WHITE : BLACK, (struct TextStyle) {.size = FONT_BOLD, .bg=1}); LCD_setStrEx("Cancel", 1, 15, self->foc == FOC_CANCEL ? WHITE : BLACK, (struct TextStyle) {.size = FONT_BOLD, .bg=1}); } static void ensureCharNotNull(struct StringScene *self) { struct StringSceneOpts *opts = self->opts; // ensure there is something to draw if (!opts->value[self->editedchar]) { opts->value[self->editedchar] = ' '; } } static void trimTailSpaces(struct StringScene *self) { int pos = self->length; while (pos > 0 && self->opts->value[pos - 1] == ' ') { pos--; } self->length = pos; } static void deinit(struct StringScene *self) { free(self->opts); self->opts = NULL; } struct Scene *NewScene_String(struct StringSceneOpts *opts) { struct StringScene *scene = calloc(1, sizeof(struct StringScene)); if (!scene) return NULL; scene->opts = opts; scene->base.onInput = (Scene_onInput_t) onInput; scene->base.paint = (Scene_paint_t) paint; scene->base.deinit = (Scene_deinit_t) deinit; scene->length = strlen(opts->value); scene->foc = scene->length==0? FOC_CHAR_EDIT : FOC_CHAR; scene->editedchar = 0; ensureCharNotNull(scene); return (struct Scene *) scene; }