|
|
|
@ -0,0 +1,209 @@ |
|
|
|
|
#include <malloc.h> |
|
|
|
|
#include <stdio.h> |
|
|
|
|
#include <string.h> |
|
|
|
|
#include <common_utils/utils.h> |
|
|
|
|
|
|
|
|
|
#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; |
|
|
|
|
} |