u8g2-copy/sys/sdl/ui/ui.c

474 lines
10 KiB
C

/*
"Ux" the interface
"Si" the style
"Fiixy" Generic field: Places field with id ii at x/y
"Biixy|text|" Buffon field (field if ii with a fixed text)
"Lxy|labeltext|" Places a text at the specified position, field with id FL
"Mxyv|labeltext|"
"Jxyu|menutext|" Jump button to user interface form u, current u is placed on a stack
"Xxy|menutext|" Go to the u which is placed on the top of the stack
"Gxyu|menutext|" Go to the specified menu without placing the user interface form on the stack
*/
#include "ui.h"
//#define ui_get_fds_char(s) ((uint8_t)(*s))
uint8_t ui_get_fds_char(fds_t s)
{
return (uint8_t)(*s);
}
/*
s must point to a valid command within FDS
*/
size_t ui_fds_get_cmd_size_without_text(ui_t *ui, fds_t s)
{
uint8_t c = ui_get_fds_char(s);
switch(c)
{
case 'U': return 2;
case 'S': return 2;
case 'F': return 5;
case 'B': return 5;
case 'L': return 3;
case 'M': return 4;
case 'X': return 3;
case 'J': return 4;
case 'G': return 4;
case 0: return 0;
}
return 1;
}
/*
s must point to the string delimiter start: first '/' for "B00ab/ok/"
- '/' actually is 0xff
- return the total size of the string, including the delimiter
- copies the content of the string ("ok") to the ui text buffer
*/
size_t ui_fds_parse_text(ui_t *ui, fds_t s)
{
uint8_t i = 0;
ui->delimiter = ui_get_fds_char(s);
uint8_t c;
fds_t t = s;
//printf("ui_fds_parse_text del=%d\n", delimiter);
#ifdef UI_CHECK_EOFDS
if ( ui->delimiter == 0 )
return 0;
#endif
t++;
for( ;; )
{
c = ui_get_fds_char(t);
//printf("ui_fds_parse_text i=%d, c=%c\n", i, c);
#ifdef UI_CHECK_EOFDS
if ( c == 0 )
break;
#endif
if ( c == ui->delimiter )
{
t++;
break;
}
if ( i < UI_MAX_TEXT_LEN )
{
ui->text[i++] = c;
}
t++;
}
ui->text[i] = '\0' ;
return t-s;
}
/*
get the first token within a text argument.
The text argument may look like this:
"B00ab/banana|apple|peach|cherry/"
The outer delimiter "/" is not fixed and can be any char except "|" and "\0"
The inner delimiter "|" is fixed. It must be the pipe symbol.
This function will place "banana" into ui->text if the result is not 0
if ( ui_fds_first_token(ui) )
{
do
{
// handle token in ui->text
} while ( ui_fds_next_token(ui) )
}
*/
uint8_t ui_fds_first_token(ui_t *ui)
{
ui->token = ui->fds;
ui->token += ui_fds_get_cmd_size_without_text(ui, ui->fds);
ui->delimiter = ui_get_fds_char(ui->token);
ui->token++;
return ui_fds_next_token(ui);
}
uint8_t ui_fds_next_token(ui_t *ui)
{
uint8_t c;
uint8_t i = 0;
for( ;; )
{
c = ui_get_fds_char(ui->token);
#ifdef UI_CHECK_EOFDS
if ( c == 0 )
break;
#endif
if ( c == '|' || c == ui->delimiter )
break;
if ( i < UI_MAX_TEXT_LEN )
{
ui->text[i++] = c;
}
ui->token++;
}
ui->text[i] = '\0' ;
if ( i == 0 )
return 0; // no further token found
return 1; // token placed in ui->text
}
/*
find nth token, return 0 if n exceeds the number of tokens, 1 otherwise
the result is stored in ui->text
*/
uint8_t ui_fds_get_nth_token(ui_t *ui, uint8_t n)
{
if ( ui_fds_first_token(ui) )
{
do
{
if ( n == 0 )
return 1;
n--;
} while ( ui_fds_next_token(ui) );
}
return 0;
}
uint8_t ui_fds_get_token_cnt(ui_t *ui)
{
uint8_t n = 0;
if ( ui_fds_first_token(ui) )
{
do
{
n++;
} while ( ui_fds_next_token(ui) );
}
return n;
}
#define ui_fds_is_text(c) ( (c) == 'U' || (c) == 'S' || (c) == 'F' ? 0 : 1 )
/*
s must point to a valid command within FDS
return
The complete length of the command (including any text part)
sideeffect:
Any existing text part will be copied into ui->text
*/
size_t ui_fds_get_cmd_size(ui_t *ui, fds_t s)
{
size_t l = ui_fds_get_cmd_size_without_text(ui, s);
uint8_t c = ui_get_fds_char(s);
if ( ui_fds_is_text(c) )
{
l += ui_fds_parse_text(ui, s+l);
}
return l;
}
/*
fds_t ui_get_field_text_start(ui_t *ui, fds_t s)
{
uint8_t c;
c = ui_get_fds_char(s);
switch(c)
{
case 'U': return NULL;
case 'S': return NULL;
case 'F': return NULL;
case 'B': return s+5;
case 'L': return s+3;
case 'X': return s+3;
case 'J': return s+4;
case 'G': return s+4;
}
return NULL;
}
*/
void ui_Init(ui_t *ui, fds_t fds, uif_t *uif_list, size_t uif_cnt)
{
memset(ui, 0, sizeof(ui_t));
ui->root_fds = fds;
ui->current_form_fds = fds;
ui->uif_list = uif_list;
ui->uif_cnt = uif_cnt;
}
ssize_t ui_find_uif(ui_t *ui, uint8_t id0, uint8_t id1)
{
ssize_t i;
for( i = 0; i < ui->uif_cnt; i++ )
{
if ( ui->uif_list[i].id0 == id0 )
if ( ui->uif_list[i].id1 == id1 )
return i;
}
return -1;
}
/*
assumes a valid position in ui->fds and calculates all the other variables
*/
void ui_prepare_current_field(ui_t *ui)
{
ssize_t uif_idx;
ui->uif = NULL;
ui->dflags = 0;
ui->id0 = 0;
ui->id1 = 0;
/* calculate the length of the command and copy the text argument */
ui->len = ui_fds_get_cmd_size(ui, ui->fds);
//printf("ui_prepare_current_field text=%s\n", ui->text);
/* get the command and check whether end of form is reached */
ui->cmd = ui_get_fds_char(ui->fds);
if ( ui->cmd == 'U' || ui->cmd == 0 )
return;
/* calculate the dynamic flags */
if ( ui->fds == ui->cursor_focus_fds )
ui->dflags |= UIF_DFLAG_IS_CURSOR_FOCUS;
if ( ui->fds == ui->touch_focus_fds )
ui->dflags |= UIF_DFLAG_IS_TOUCH_FOCUS;
/* get the id0 and id1 values */
if ( ui->cmd == 'F' || ui->cmd == 'B' )
{
ui->id0 = ui_get_fds_char(ui->fds+1);
ui->id1 = ui_get_fds_char(ui->fds+2);
ui->x = ui_get_fds_char(ui->fds+3);
ui->y = ui_get_fds_char(ui->fds+4);
}
else if ( ui->cmd == 'S' )
{
ui->id0 = 'S';
ui->id1 = ui_get_fds_char(ui->fds+1);
}
else
{
ui->id0 = 'F';
ui->id1 = ui->cmd;
ui->x = ui_get_fds_char(ui->fds+1);
ui->y = ui_get_fds_char(ui->fds+2);
}
/* find the field and execute the task */
uif_idx = ui_find_uif(ui, ui->id0, ui->id1);
//printf("ui_prepare_current_field: uif_idx=%d\n", uif_idx);
if ( uif_idx >= 0 )
{
ui->uif = ui->uif_list + uif_idx;
}
}
void ui_loop_over_form(ui_t *ui, void (*task)(ui_t *ui))
{
uint8_t cmd;
ui->fds = ui->current_form_fds;
ui->target_fds = NULL;
ui->tmp_fds = NULL;
ui->fds += ui_fds_get_cmd_size(ui, ui->fds); // skip the first entry, it is U always
for(;;)
{
//printf("fds=%p *fds=%d\n", ui->fds, ui->fds[0]);
/* get the command and check whether end of form is reached */
cmd = ui_get_fds_char(ui->fds);
if ( cmd == 'U' || cmd == 0 )
break;
ui_prepare_current_field(ui);
if ( ui->uif )
{
task(ui);
}
ui->fds += ui->len;
}
//printf("ui_loop_over_form ended\n");
}
/* === task procedures (arguments for ui_loop_over_form === */
void ui_task_draw(ui_t *ui)
{
//printf("ui_task_draw fds=%p uif=%p text=%s\n", ui->fds, ui->uif, ui->text);
uif_get_cb(ui->uif)(ui, UIF_MSG_DRAW);
}
void ui_task_form_start(ui_t *ui)
{
uif_get_cb(ui->uif)(ui, UIF_MSG_FORM_START);
}
void ui_task_form_end(ui_t *ui)
{
uif_get_cb(ui->uif)(ui, UIF_MSG_FORM_START);
}
void ui_task_find_prev_cursor_uif(ui_t *ui)
{
if ( uif_get_cflags(ui->uif) & UIF_CFLAG_IS_CURSOR_SELECTABLE )
{
if ( ui->fds == ui->cursor_focus_fds )
{
ui->target_fds = ui->tmp_fds;
}
ui->tmp_fds = ui->fds;
}
}
void ui_task_find_first_cursor_uif(ui_t *ui)
{
if ( uif_get_cflags(ui->uif) & UIF_CFLAG_IS_CURSOR_SELECTABLE )
{
if ( ui->target_fds == NULL )
{
ui->target_fds = ui->fds;
}
}
}
void ui_task_find_last_cursor_uif(ui_t *ui)
{
if ( uif_get_cflags(ui->uif) & UIF_CFLAG_IS_CURSOR_SELECTABLE )
{
ui->target_fds = ui->fds;
}
}
void ui_task_find_next_cursor_uif(ui_t *ui)
{
if ( uif_get_cflags(ui->uif) & UIF_CFLAG_IS_CURSOR_SELECTABLE )
{
if ( ui->tmp_fds != NULL )
{
ui->target_fds = ui->fds;
ui->tmp_fds = NULL;
}
if ( ui->fds == ui->cursor_focus_fds )
{
ui->tmp_fds = ui->fds;
}
}
}
/* === utility functions for the user API === */
void ui_send_cursor_msg(ui_t *ui, uint8_t msg)
{
if ( ui->cursor_focus_fds )
{
ui->fds = ui->cursor_focus_fds;
ui_prepare_current_field(ui);
uif_get_cb(ui->uif)(ui, msg);
}
}
/* === user API === */
void ui_Draw(ui_t *ui)
{
ui_loop_over_form(ui, ui_task_draw);
}
/* input: current_form_fds */
void ui_EnterForm(ui_t *ui)
{
/* clean focus fields */
ui->touch_focus_fds = NULL;
ui->cursor_focus_fds = NULL;
/* inform all fields that we start a new form */
ui_loop_over_form(ui, ui_task_form_start);
/* assign initional cursor focus */
ui_loop_over_form(ui, ui_task_find_first_cursor_uif);
ui->cursor_focus_fds = ui->target_fds; // NULL is ok
ui_send_cursor_msg(ui, UIF_MSG_CURSOR_ENTER);
}
/* input: current_form_fds */
void ui_LeaveForm(ui_t *ui)
{
ui_send_cursor_msg(ui, UIF_MSG_CURSOR_LEAVE);
ui->cursor_focus_fds = NULL;
/* inform all fields that we leave the form */
ui_loop_over_form(ui, ui_task_form_end);
}
void ui_NextField(ui_t *ui)
{
ui_send_cursor_msg(ui, UIF_MSG_CURSOR_LEAVE);
ui_loop_over_form(ui, ui_task_find_next_cursor_uif);
ui->cursor_focus_fds = ui->target_fds; // NULL is ok
if ( ui->target_fds == NULL )
{
ui_loop_over_form(ui, ui_task_find_first_cursor_uif);
ui->cursor_focus_fds = ui->target_fds; // NULL is ok
}
ui_send_cursor_msg(ui, UIF_MSG_CURSOR_ENTER);
}
void ui_PrevField(ui_t *ui)
{
ui_send_cursor_msg(ui, UIF_MSG_CURSOR_LEAVE);
ui_loop_over_form(ui, ui_task_find_prev_cursor_uif);
ui->cursor_focus_fds = ui->target_fds; // NULL is ok
if ( ui->target_fds == NULL )
{
ui_loop_over_form(ui, ui_task_find_last_cursor_uif);
ui->cursor_focus_fds = ui->target_fds; // NULL is ok
}
ui_send_cursor_msg(ui, UIF_MSG_CURSOR_ENTER);
}
void ui_SendSelect(ui_t *ui)
{
ui_send_cursor_msg(ui, UIF_MSG_CURSOR_SELECT);
}