#include #include #include "clipb.h" #include "eve.h" #include "eve_kbd.h" #include "eve_font.h" #include "screen/window.h" #include "screen/page.h" #include "widget.h" #define STRW_TOUCH_OPT EVE_TOUCH_OPT_TRACK | EVE_TOUCH_OPT_TRACK_X | EVE_TOUCH_OPT_TRACK_EXT_X | EVE_TOUCH_OPT_LPRESS #define STRW_TMODE_NONE 0 #define STRW_TMODE_CRSR 1 #define STRW_TMODE_TXT 2 #define CH_BS 0x08 #define CH_DEL 0x7f #define CH_CTRLX 0x18 #define CH_CTRLC 0x03 #define CH_CTRLV 0x16 #define CHAR_VALID_INPUT(c) ((c >= 0x20) && (c < 0x7f)) void eve_strw_init(EVEStrWidget *widget, EVERect *g, EVEPage *page, EVEFont *font, utf8_t *str, uint16_t str_size) { EVEWidget *_widget = &widget->w; int rv, str_len; memset(widget, 0, sizeof(EVEStrWidget)); eve_widget_init(_widget, EVE_WIDGET_TYPE_STR, g, page, eve_strw_draw, eve_strw_touch, eve_strw_putc); widget->font = font; rv = utf8_verify(str, str_size, &str_len); if (rv != UTF_OK) { if (str_len >= str_size) str_len = 0; str[str_len] = '\0'; } widget->str = str; widget->str_size = str_size; widget->str_len = str_len; widget->str_g.w = eve_font_str_w(widget->font, str); if (_widget->g.h == 0) _widget->g.h = eve_font_h(widget->font); } int eve_strw_update(EVEStrWidget *widget) { int rv, str_len; rv = utf8_verify(widget->str, widget->str_size, &str_len); if (rv != UTF_OK) { if (str_len >= widget->str_size) str_len = 0; widget->str[str_len] = '\0'; } widget->str_len = str_len; widget->str_g.w = eve_font_str_w(widget->font, widget->str); return (rv == UTF_OK) ? EVE_OK : EVE_ERR; } int eve_strw_create(EVEWidget *_widget, EVEWidgetSpec *spec, EVEPage *page) { EVEStrWidget *widget = (EVEStrWidget *)_widget; EVEStrSpec *tspec = &spec->tspec.str; EVEFont *font = tspec->font ? tspec->font : eve_window_font(page->v.window); utf8_t *str; uint16_t *line; str = eve_malloc(tspec->str_size); if (str == NULL) return EVE_ERR_NOMEM; str[0] = '\0'; eve_strw_init(widget, &spec->g, page, font, str, tspec->str_size); return EVE_OK; } void eve_strw_destroy(EVEWidget *_widget) { EVEStrWidget *widget = (EVEStrWidget *)_widget; eve_free(widget->str); } static void set_focus(EVEStrWidget *widget) { EVEWidget *_widget = &widget->w; EVERect focus; focus.x = _widget->g.x; focus.y = _widget->g.y; focus.w = _widget->g.w; focus.h = 2 * widget->font->h; eve_widget_set_focus(_widget, &focus); } static EVEStrCursor *cursor_prox(EVEStrWidget *widget, EVEStrCursor *cursor, EVETouch *touch, short *dx) { EVEWidget *_widget = &widget->w; EVEPage *page = _widget->page; int x = eve_page_x(page, touch->x0) - _widget->g.x + widget->str_g.x; int _dx; *dx = cursor->x - x; _dx = *dx < 0 ? -(*dx) : *dx; if (_dx <= widget->font->w) return cursor; return NULL; } static void draw_string(EVEStrWidget *widget, uint16_t ch, uint16_t len, uint16_t x1, uint16_t x2, char s) { int16_t x; EVEWidget *_widget = &widget->w; EVEPage *page = _widget->page; x = _widget->g.x - widget->str_g.x; if (x1 != x2) { eve_cmd_dl(BEGIN(EVE_RECTS)); if (!s) eve_cmd_dl(COLOR_MASK(0 ,0 ,0 ,0)); eve_cmd_dl(VERTEX2F(x + x1, _widget->g.y)); eve_cmd_dl(VERTEX2F(x + x2, _widget->g.y + widget->font->h)); if (!s) { eve_cmd_dl(COLOR_MASK(1 ,1 ,1 ,1)); eve_cmd_dl(BEGIN(EVE_LINES)); eve_cmd_dl(VERTEX2F(x + x1, _widget->g.y + widget->font->h)); eve_cmd_dl(VERTEX2F(x + x2, _widget->g.y + widget->font->h)); } eve_cmd_dl(END()); if (len) { if (s) eve_cmd_dl(COLOR_RGBC(page->v.color_bg)); eve_cmd(CMD_TEXT, "hhhhpb", x + x1, _widget->g.y, widget->font->id, 0, widget->str + ch, len, 0); if (s) eve_cmd_dl(COLOR_RGBC(page->v.color_fg)); } } } static void draw_cursor(EVEStrWidget *widget, EVEStrCursor *cursor) { uint16_t x, y; EVEWidget *_widget = &widget->w; x = _widget->g.x - widget->str_g.x + cursor->x; y = _widget->g.y; eve_cmd_dl(BEGIN(EVE_LINES)); eve_cmd_dl(VERTEX2F(x, y)); eve_cmd_dl(VERTEX2F(x, y + widget->font->h)); eve_cmd_dl(END()); } uint8_t eve_strw_draw(EVEWidget *_widget, uint8_t tag0) { EVEStrWidget *widget = (EVEStrWidget *)_widget; char cut = widget->str_g.x || (widget->str_g.w > _widget->g.w); _widget->tag0 = tag0; if (tag0 != EVE_NOTAG) { eve_cmd_dl(TAG(tag0)); eve_touch_set_opt(tag0, STRW_TOUCH_OPT); tag0++; } _widget->tagN = tag0; if (cut) { EVEPage *page = _widget->page; EVEWindow *window = page->v.window; int16_t x = eve_page_scr_x(page, _widget->g.x); int16_t y = eve_page_scr_y(page, _widget->g.y); uint16_t w = _widget->g.w; uint16_t h = _widget->g.h; int16_t win_x1 = window->g.x; int16_t win_y1 = window->g.y; int16_t win_x2 = win_x1 + window->g.w; int16_t win_y2 = win_y1 + window->g.h; if (win_x1 < 0) win_x1 = 0; if (win_y1 < 0) win_y1 = 0; if (win_x2 > window->root->w.g.w) win_x2 = window->root->w.g.w; if (win_y2 > window->root->w.g.h) win_y2 = window->root->w.g.h; if (x < win_x1) { w += x - win_x1; x = win_x1; } if (y < win_y1) { h += y - win_y1; y = win_y1; } if (x + w > win_x2) w = win_x2 - x; if (y + h > win_y2) h = win_y2 - y; eve_cmd_dl(SAVE_CONTEXT()); eve_cmd_dl(SCISSOR_XY(x, y)); eve_cmd_dl(SCISSOR_SIZE(w, h)); } if (widget->cursor2.on) { EVEStrCursor *c1, *c2; int l1, l2, l3; if (widget->cursor1.ch <= widget->cursor2.ch) { c1 = &widget->cursor1; c2 = &widget->cursor2; } else { c1 = &widget->cursor2; c2 = &widget->cursor1; } l1 = c1->ch; l2 = c2->ch - c1->ch; l3 = widget->str_len - c2->ch; draw_string(widget, 0, l1, 0, c1->x, 0); draw_string(widget, c1->ch, l2, c1->x, c2->x, 1); draw_string(widget, c2->ch, l3, c2->x, widget->str_g.x + _widget->g.w, 0); } else { if (widget->cursor1.on) draw_cursor(widget, &widget->cursor1); draw_string(widget, 0, widget->str_len, 0, widget->str_g.x + _widget->g.w, 0); } if (cut) { eve_cmd_dl(RESTORE_CONTEXT()); } return _widget->tagN; } int eve_strw_touch(EVEWidget *_widget, EVETouch *touch, uint16_t evt) { EVEStrWidget *widget = (EVEStrWidget *)_widget; EVEPage *page = _widget->page; EVEStrCursor *t_cursor = NULL; short dx; int ret = 0; if (evt & (EVE_TOUCH_ETYPE_LPRESS | EVE_TOUCH_ETYPE_TRACK_START)) { if (widget->cursor2.on) { t_cursor = cursor_prox(widget, &widget->cursor2, touch, &dx); } if ((t_cursor == NULL) && widget->cursor1.on) { t_cursor = cursor_prox(widget, &widget->cursor1, touch, &dx); } if (evt & EVE_TOUCH_ETYPE_TRACK_START) { if (t_cursor) { widget->track.mode = STRW_TMODE_CRSR; widget->track.cursor = t_cursor; widget->track.dx = dx; } else if (touch->eevt & EVE_TOUCH_EETYPE_TRACK_X) { widget->track.mode = STRW_TMODE_TXT; } } } if (widget->track.mode) { int x, w1; if (evt & EVE_TOUCH_ETYPE_TRACK) { switch (widget->track.mode) { case STRW_TMODE_TXT: if (evt & EVE_TOUCH_ETYPE_TRACK_START) { widget->str_g.x0 = widget->str_g.x; } x = widget->str_g.x0 + touch->x0 - touch->x; w1 = _widget->g.w - widget->font->w; if (x > widget->str_g.w - w1) x = widget->str_g.w - w1; if (x < 0) x = 0; widget->str_g.x = x; break; case STRW_TMODE_CRSR: eve_strw_cursor_set(widget, widget->track.cursor, eve_page_x(page, touch->x) + widget->track.dx); break; } } ret = 1; } else { if (evt & EVE_TOUCH_ETYPE_LPRESS) { if (widget->cursor2.on) { // copy } else if (widget->cursor1.on) { if (t_cursor) { // paste } else { eve_strw_cursor_set(widget, &widget->cursor2, eve_page_x(page, touch->x)); } } else { // select } ret = 1; } if ((evt & EVE_TOUCH_ETYPE_POINT_UP) && !(touch->eevt & (EVE_TOUCH_EETYPE_TRACK_XY | EVE_TOUCH_EETYPE_ABORT | EVE_TOUCH_EETYPE_LPRESS))) { eve_strw_cursor_set(widget, &widget->cursor1, eve_page_x(page, touch->x0)); if (widget->cursor2.on) eve_strw_cursor_clear(widget, &widget->cursor2); set_focus(widget); ret = 1; } } if (evt & EVE_TOUCH_ETYPE_TRACK_STOP) { widget->track.mode = STRW_TMODE_NONE; widget->track.cursor = NULL; widget->track.dx = 0; } if (ret) eve_widget_set_focus(_widget, NULL); return ret; } void eve_strw_putc(void *w, int c) { EVEStrWidget *widget = (EVEStrWidget *)w; EVEWidget *_widget = &widget->w; EVEStrCursor *cursor1 = &widget->cursor1; EVEStrCursor *cursor2 = &widget->cursor2; utf8_t *str; utf8_t *clipb = NULL; int w0 = widget->font->w; int w1 = _widget->g.w - widget->font->w; int ins_c = 0, del_c = 0; int ins_w = 0, del_w = 0; if (c == EVE_PAGE_KBDCH_CLOSE) { if (cursor1->on) eve_strw_cursor_clear(widget, cursor1); if (cursor2->on) eve_strw_cursor_clear(widget, cursor2); return; } if (!cursor1->on) return; if (!cursor2->on && ((c == CH_BS) || (c == CH_DEL))) { utf32_t uc; str = widget->str + cursor1->ch; switch (c) { case CH_BS: if (cursor1->ch > 0) { del_c = -utf8_seek(str, -1, &uc); del_w = eve_font_ch_w(widget->font, uc); memmove(str - del_c, str, widget->str_len - cursor1->ch + 1); widget->str_len -= del_c; widget->str_g.w -= del_w; cursor1->ch -= del_c; cursor1->x -= del_w; } break; case CH_DEL: if (cursor1->ch < widget->str_len) { del_c = utf8_dec(str, &uc); del_w = eve_font_ch_w(widget->font, uc); memmove(str, str + del_c, widget->str_len - cursor1->ch - del_c + 1); widget->str_len -= del_c; widget->str_g.w -= del_w; } break; } if (widget->str_g.w - widget->str_g.x < w1) { widget->str_g.x -= del_w; if (widget->str_g.x < 0) widget->str_g.x = 0; } } else { EVEStrCursor *c1 = cursor1; EVEStrCursor *c2 = cursor1; utf8_t utf8_buf[4]; if (cursor2->on) { if (cursor1->ch <= cursor2->ch) { c2 = cursor2; } else { c1 = cursor2; } del_c = c2->ch - c1->ch; del_w = eve_font_buf_w(widget->font, str, del_c); if ((c == CH_CTRLX) || (c == CH_CTRLC)) { eve_clipb_push(str, del_c); if (c == CH_CTRLC) return; } } str = widget->str + c1->ch; if (CHAR_VALID_INPUT(c)) { ins_c = utf8_enc(c, utf8_buf); ins_w = eve_font_ch_w(widget->font, c); } else if (c == CH_CTRLV) { int rv, clipb_len = 0; clipb = eve_clipb_get(); if (clipb) { rv = utf8_verify(clipb, EVE_CLIPB_SIZE_BUF, &clipb_len); if (rv != UTF_OK) { clipb = NULL; clipb_len = 0; } } ins_c = clipb_len; ins_w = eve_font_str_w(widget->font, clipb); } if (widget->str_len + ins_c >= widget->str_size + del_c) { ins_c = 0; ins_w = 0; } if (ins_c != del_c) memmove(str + ins_c, str + del_c, widget->str_len - c2->ch + 1); if (ins_c) { if (c == CH_CTRLV) { memcpy(str, clipb, ins_c); } else if (ins_c > 1) { memcpy(str, utf8_buf, ins_c); } else { *str = utf8_buf[0]; } c1->ch += ins_c; c1->x += ins_w; } widget->str_len += ins_c - del_c; widget->str_g.w += ins_w - del_w; if (c1 == cursor2) widget->cursor1 = widget->cursor2; if (cursor2->on) eve_strw_cursor_clear(widget, cursor2); } if (cursor1->x - widget->str_g.x < w0) widget->str_g.x = cursor1->x > w0 ? cursor1->x - w0 : 0; if (cursor1->x - widget->str_g.x > w1) widget->str_g.x = cursor1->x - w1; } void eve_strw_cursor_set(EVEStrWidget *widget, EVEStrCursor *cursor, int16_t x) { int i; int16_t _x, _d; utf32_t ch; uint8_t ch_w; uint8_t ch_l; EVEWidget *_widget = &widget->w; x = x - _widget->g.x + widget->str_g.x; _x = 0; _d = x; i = 0; while (i < widget->str_len) { ch_l = utf8_dec(widget->str + i, &ch); ch_w = eve_font_ch_w(widget->font, ch); _x += ch_w; i += ch_l; if (_x >= x) { if (_x - x > _d) { _x -= ch_w; i -= ch_l; } break; } else { _d = x - _x; } } cursor->x = _x; cursor->ch = i; cursor->on = 1; } void eve_strw_cursor_clear(EVEStrWidget *widget, EVEStrCursor *cursor) { cursor->on = 0; }