parent
e22d4fb9e2
commit
dd689841bb
@ -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; |
||||
} |
@ -0,0 +1,35 @@ |
||||
/**
|
||||
* TODO file description |
||||
*
|
||||
* Created on 2020/01/05. |
||||
*/ |
||||
|
||||
#ifndef REFLOWER_SCENE_STR_H |
||||
#define REFLOWER_SCENE_STR_H |
||||
|
||||
#include <stdint.h> |
||||
|
||||
#define STR_EDIT_VALUE_MAXLEN 17 |
||||
|
||||
/**
|
||||
* Options passed to the StringScene constructor |
||||
*/ |
||||
struct StringSceneOpts { |
||||
// Screen title
|
||||
char label[15]; |
||||
// Value
|
||||
char value[STR_EDIT_VALUE_MAXLEN]; |
||||
}; |
||||
|
||||
/**
|
||||
* Create string picker. |
||||
* |
||||
* "opts" must be on heap and will be internally mutated. |
||||
* The scene frees them on exit, returning the selected value as data (heap string), or NULL on cancel. |
||||
* |
||||
* @param opts |
||||
* @return |
||||
*/ |
||||
struct Scene *NewScene_String(struct StringSceneOpts *opts); |
||||
|
||||
#endif //REFLOWER_SCENE_STR_H
|
Loading…
Reference in new issue