ui_textosk.h 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  1. /*
  2. * ui_textosk.h
  3. * https://gitlab.com/bztsrc/smgui
  4. *
  5. * Copyright (C) 2024 bzt (bztsrc@gitlab), MIT license
  6. *
  7. * Permission is hereby granted, free of charge, to any person obtaining a copy
  8. * of this software and associated documentation files (the "Software"), to
  9. * deal in the Software without restriction, including without limitation the
  10. * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  11. * sell copies of the Software, and to permit persons to whom the Software is
  12. * furnished to do so, subject to the following conditions:
  13. *
  14. * The above copyright notice and this permission notice shall be included in
  15. * all copies or substantial portions of the Software.
  16. *
  17. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ANY
  20. * DEVELOPER OR DISTRIBUTOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  21. * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
  22. * IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  23. *
  24. * @brief Text input with on screen keyboard for SMGUI
  25. */
  26. #ifndef UI_TEXTOSK_H
  27. #define UI_TEXTOSK_H 1
  28. #ifndef UI_H
  29. #include <ui.h>
  30. #undef UI_H
  31. #endif
  32. #ifdef __cplusplus
  33. extern "C" {
  34. #endif
  35. #define UI_FLAGS_NOOSK 256
  36. #define UI_TEXT UI_CUSTOM,.bbox=ui_textosk_bbox,.view=ui_textosk_view,.ctrl=ui_textosk_ctrl
  37. #define UI_SIZ _ui_textosk_popup[0].x
  38. #define UI_KBD _ui_textosk_popup[0].y
  39. #define UI_SCR _ui_textosk_popup[0].w
  40. #define UI_SEL _ui_textosk_popup[0].h
  41. #define UI_NUMKBD 13
  42. static ui_form_t _ui_textosk_popup[] = { { .type = UI_END } };
  43. typedef struct {
  44. char *id;
  45. int pair, num;
  46. const char **keys;
  47. } _ui_textosk_kbd_t;
  48. const char *_ui_textosk_numbers[] ={ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "_", "-", "+", ".", "。", "$", "¢", "£", "¤",
  49. "¥", "§", "©", "®", "ª", "°", "¹", "²", "³", "½", "¼", "¾", "'", "/", NULL };
  50. const char *_ui_textosk_capital[] ={ "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T",
  51. "U", "V", "W", "X", "Y", "Z", "ß", "À", "Á", "Â", "Ã", "Ä", "Å", "Æ", "Ā", "Ă", "Ą", "Ç", "Ć", "Ĉ", "Ċ", "Č", "Ð", "Ď", "Đ",
  52. "È", "É", "Ê", "Ë", "Ē", "Ĕ", "Ė", "Ę", "Ě", "Ĝ", "Ğ", "Ġ", "Ģ", "Ĥ", "Ħ", "Ì", "Í", "Î", "Ï", "Ĩ", "Ī", "Ĭ", "Į", "İ", "Ĵ",
  53. "Ķ", "Ĺ", "Ļ", "Ľ", "Ŀ", "Ł", "Ñ", "Ń", "Ņ", "Ň", "Ŋ", "Ò", "Ó", "Ô", "Õ", "Ö", "Ø", "Ō", "Ŏ", "Ő", "Œ", "Ŕ", "Ŗ", "Ř", "Ś",
  54. "Ŝ", "Ş", "Š", "Ţ", "Ť", "Ŧ", "Ù", "Ú", "Û", "Ü", "Ũ", "Ū", "Ŭ", "Ů", "Ű", "Ų", "Ŵ", "Ý", "Ŷ", "Ÿ", "Þ", "Ź", "Ż", "Ž", NULL };
  55. const char *_ui_textosk_lower[] = { "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t",
  56. "u", "v", "w", "x", "y", "z", "ß", "à", "á", "â", "ã", "ä", "å", "æ", "ā", "ă", "ą", "ç", "ć", "ĉ", "ċ", "č", "ð", "ď", "đ",
  57. "è", "é", "ê", "ë", "ē", "ĕ", "ė", "ę", "ě", "ĝ", "ğ", "ġ", "ģ", "ĥ", "ħ", "ì", "í", "î", "ï", "ĩ", "ī", "ĭ", "į", "ı", "ĵ",
  58. "ķ", "ĺ", "ļ", "ľ", "ŀ", "ł", "ñ", "ń", "ņ", "ň", "ŋ", "ò", "ó", "ô", "õ", "ö", "ø", "ō", "ŏ", "ő", "œ", "ŕ", "ŗ", "ř", "ś",
  59. "ŝ", "ş", "š", "ţ", "ť", "ŧ", "ù", "ú", "û", "ü", "ũ", "ū", "ŭ", "ů", "ű", "ų", "ŵ", "ý", "ŷ", "ÿ", "þ", "ź", "ż", "ž", NULL };
  60. const char *_ui_textosk_greekc[] = { "Α", "Β", "Γ", "Δ", "Ε", "Ζ", "Η", "Θ", "Ι", "Κ", "Λ", "Μ", "Ν", "Ξ", "Ο", "Π", "Ρ", "Σ", "Τ",
  61. "Υ", "Φ", "Χ", "Ψ", "Ω", "Ͱ", "Ͳ", "Ͷ", "Ϙ", "Ϛ", "Ϝ", "Ϟ", "Ϡ", "Ϣ", "Ϥ", "Ϧ", "Ϩ", "Ϫ", "Ϭ", "Ϯ", "Ϳ", "Ϻ", "Ͻ", "Ͼ", "Ͽ",
  62. "Ϸ", "Ά", "Έ", "Ή", "Ί", "Ϊ", "Ϋ", "Ό", "Ύ", "Ώ", "ϒ", "ϓ", "ϔ", NULL };
  63. const char *_ui_textosk_greekl[] = { "α", "β", "γ", "δ", "ε", "ζ", "η", "θ", "ι", "κ", "λ", "μ", "ν", "ξ", "ο", "π", "ρ", "σ", "τ",
  64. "υ", "φ", "χ", "ψ", "ω", "ͱ", "ͳ", "ͷ", "ϙ", "ϛ", "ϝ", "ϟ", "ϡ", "ϣ", "ϥ", "ϧ", "ϩ", "ϫ", "ϭ", "ϯ", "ϳ", "ϻ", "ͻ", "ͼ", "ͽ",
  65. "ϸ", "ά", "έ", "ή", "ί", "ϊ", "ϋ", "ό", "ύ", "ώ", "ΐ", "ΰ", "ς", NULL };
  66. const char *_ui_textosk_cyrillc[] ={ "А", "Б", "В", "Г", "Д", "Е", "Ж", "З", "И", "Й", "К", "Л", "М", "Н", "О", "П", "Р", "С", "Т",
  67. "У", "Ф", "Х", "Ц", "Ч", "Ш", "Щ", "Ъ", "Ы", "Ь", "Э", "Ю", "Я", "Ѐ", "Ё", "Ђ", "Ѓ", "Є", "Ѕ", "І", "Ї", "Ј", "Љ", "Њ", "Ћ",
  68. "Ќ", "Ѝ", "Ў", "Џ", "Ѡ", "Ѣ", "Ѥ", "Ѧ", "Ѩ", "Ѫ", "Ѭ", "Ѯ", "Ѱ", "Ѳ", "Ѵ", "Ѷ", "Ѹ", "Ѻ", "Ѽ", "Ѿ", "Ҁ", NULL };
  69. const char *_ui_textosk_cyrilll[] ={ "а", "б", "в", "г", "д", "е", "ж", "з", "и", "й", "к", "л", "м", "н", "о", "п", "р", "с", "т",
  70. "у", "ф", "х", "ц", "ч", "ш", "щ", "ъ", "ы", "ь", "э", "ю", "я", "ѐ", "ё", "ђ", "ѓ", "є", "ѕ", "і", "ї", "ј", "љ", "њ", "ћ",
  71. "ќ", "ѝ", "ў", "џ", "ѡ", "ѣ", "ѥ", "ѧ", "ѩ", "ѫ", "ѭ", "ѯ", "ѱ", "ѳ", "ѵ", "ѷ", "ѹ", "ѻ", "ѽ", "ѿ", "ҁ", NULL };
  72. const char *_ui_textosk_hirac[] ={ "あ", "い", "う", "え", "お", "が", "ぎ", "ぐ", "げ", "ご", "ざ", "じ", "ず", "ぜ", "ぞ", "だ", "ぢ", "つ",
  73. "づ", "で", "ど", "に", "ね", "の", "ば", "ぱ", "び", "ぴ", "ぶ", "ぷ", "べ", "ぺ", "ぼ", "ぽ", "む", "も", "や", "ゆ", "よ", "り", "れ", "ろ",
  74. "わ", "ゑ", "ん", "ゕ", "ゖ", NULL };
  75. const char *_ui_textosk_hiral[] ={ "ぁ", "ぃ", "ぅ", "ぇ", "ぉ", "か", "き", "く", "け", "こ", "さ", "し", "す", "せ", "そ", "た", "ち", "っ",
  76. "づ", "て", "と", "な", "ぬ", "の", "は", "ぱ", "ひ", "ぴ", "ふ", "ぷ", "へ", "ぺ", "ほ", "ま", "み", "め", "ゃ", "ゅ", "ょ", "ら", "る", "ろ",
  77. "ゎ", "ゐ", "を", "ゔ", "ゖ", NULL };
  78. const char *_ui_textosk_katac[] ={ "ア", "イ", "ウ", "エ", "オ", "ガ", "ギ", "グ", "ゲ", "ゴ", "ザ", "ジ", "ズ", "ゼ", "ゾ", "ダ", "ヂ", "ツ",
  79. "ヅ", "デ", "ド", "ニ", "ネ", "ハ", "パ", "ビ", "ピ", "ブ", "プ", "ベ", "ペ", "ボ", "ポ", "マ", "ム", "モ", "ヤ", "ユ", "ヨ", "ラ", "ル", "ロ",
  80. "ワ", "ヴ", "ヸ", "ヹ", "ヺ", "ヵ", "ヶ", "・", NULL };
  81. const char *_ui_textosk_katal[] ={ "ァ", "ィ", "ゥ", "ェ", "ォ", "カ", "キ", "ク", "ケ", "コ", "サ", "シ", "ス", "セ", "ソ", "タ", "チ", "ッ",
  82. "ヅ", "テ", "ト", "ナ", "ヌ", "ノ", "バ", "ヒ", "ピ", "フ", "プ", "ヘ", "ペ", "ホ", "ポ", "マ", "ミ", "メ", "ャ", "ュ", "ョ", "ラ", "リ", "レ",
  83. "ヮ", "ヷ", "ヰ", "ヱ", "ヲ", "ン", "ヶ", "・", NULL };
  84. const char *_ui_textosk_hebrew[] = { "א", "ב", "ג", "ד", "ה", "ו", "ז", "ח", "ט", "י", "ך", "כ", "ל", "ם", "מ", "ן", "נ", "ס", "ע", "ף",
  85. "פ", "ץ", "צ", "ק", "ר", "ש", "ת", NULL };
  86. const char *_ui_textosk_devana[] = { "ऄ", "अ", "आ", "इ", "ई", "उ", "ऊ", "ऋ", "ऌ", "ऍ", "ऎ", "ए", "ऐ", "ऑ", "ऒ", "ओ",
  87. "औ", "क", "ख", "ग", "घ", "ङ", "च", "छ", "ज", "झ", "ञ", "ट", "ठ", "ड", "ढ", "ण", "त", "थ", "द", "ध", "न",
  88. "ऩ", "प", "फ", "ब", "भ", "म", "य", "र", "ऱ", "ल", "ळ", "ऴ", "व", "श", "ष", "स", "ह", "ॐ", "क़", "ख़", "ग़",
  89. "ज़", "ड़", "ढ़", "फ़", "य़", "ॠ", "ॡ", "ॲ", "ॳ", "ॴ", "ॵ", "ॶ", "ॷ", "ॸ", "ॹ", "ॺ", "ॻ", "ॼ", "ॽ", "ॾ", "ॿ", NULL };
  90. _ui_textosk_kbd_t _ui_textosk_keyboards[UI_NUMKBD] = {
  91. { "0", 0, 0, _ui_textosk_numbers },
  92. { "A", 2, 0, _ui_textosk_capital },
  93. { "a", 1, 0, _ui_textosk_lower },
  94. { "Γ", 4, 0, _ui_textosk_greekc },
  95. { "γ", 3, 0, _ui_textosk_greekl },
  96. { "Б", 6, 0, _ui_textosk_cyrillc },
  97. { "б", 5, 0, _ui_textosk_cyrilll },
  98. { "あ", 8, 0, _ui_textosk_hirac },
  99. { "ぁ", 7, 0, _ui_textosk_hiral },
  100. { "ア", 10, 0, _ui_textosk_katac },
  101. { "ァ", 9, 0, _ui_textosk_katal },
  102. { "א", 0, 0, _ui_textosk_hebrew },
  103. { "ऄ", 0, 0, _ui_textosk_devana }
  104. };
  105. /* we must not rely on all fonts always have these glyphs */
  106. uint16_t _ui_textosk_glyphs[6*16] = {
  107. 0,0,0,1024,1536,1792,1920,1984,2016,1984,1920,1792,1536,1024,0,0,0,0,0,32,96,224,480,992,2016,992,480,224,96,32,0,0,0,0,65504,
  108. 32784,32776,34948,34050,33281,34050,34948,32776,32784,65504,0,0,0,0,0,0,0,0,32,48,8184,8184,48,32,0,0,0,0,0,0,0,0,0,0,1024,3072,
  109. 8184,8184,3072,1024,0,0,0,0,0,0,0,0,12288,12288,12320,12336,12344,16380,16380,56,48,32,0,0,0
  110. };
  111. /**
  112. * Display an osk key
  113. */
  114. void _ui_textosk_key(ui_t *ctx, int x, int y, int w, int h, int pressed, char *str)
  115. {
  116. uint32_t fg = 0xff7f7f7f, bg = 0xff020202;
  117. int dw = 0, dh = 0, l = 0, t = 0;
  118. if(pressed == 2) { fg = 0xffffffff; bg = 0xff003f00; }
  119. if(pressed == 1) { fg = 0xffffffff; bg = 0xff3f0000; }
  120. if(pressed == -1) { fg = 0xff020202; bg = 0xff080808; }
  121. _ui_rect(ctx, x, y, w, h, bg, 0, bg);
  122. _ui_frect(ctx, x + 1, y + 1, w - 2, h - 2, bg);
  123. if(str) {
  124. if(str[0] > 0 && str[0] < 7) {
  125. _ui_bmp16(ctx, x + (w - 16)/2, y + (h - 16)/2, &_ui_textosk_glyphs[((int)str[0] - 1) << 4], fg);
  126. } else
  127. if(ctx->fnt && ctx->bbox && ctx->draw) {
  128. (*ctx->bbox)(ctx->fnt, str, NULL, &dw, &dh, &l, &t);
  129. (*ctx->draw)(ctx->fnt, str, NULL, ctx->screen.buf, fg, x - l + (w - dw)/2, y + (h - dh)/2, l, t, ctx->screen.p,
  130. ctx->cx0 + 1, ctx->cy0 + 1, ctx->cx1 - 1, ctx->cy1 - 1);
  131. }
  132. }
  133. }
  134. /**
  135. * Recalculate form element bounding box
  136. */
  137. int ui_textosk_bbox(ui_t *ctx, int x, int y, int w, int h, ui_form_t *form, int *dw, int *dh)
  138. {
  139. if(!ctx || !form || !dw || !dh) return UI_ERR_BADINP;
  140. (void)x; (void)y; (void)w; (void)h;
  141. if(ctx->fnt && ctx->bbox && form->ptr) {
  142. /* if string is empty, use a default one to get height and baseline correctly */
  143. (*ctx->bbox)(ctx->fnt, form->ptr, NULL, dw, dh, &form->l, &form->t);
  144. if(!form->ptr || !*((char*)form->ptr)) { *dw = 0; *dh = ctx->ds; form->t = ctx->dt; }
  145. (*dh) += 4;
  146. }
  147. if(form->ew > 0) *dw = form->ew;
  148. return UI_OK;
  149. }
  150. /**
  151. * Display a text input field and an OSK
  152. */
  153. int ui_textosk_view(ui_t *ctx, int x, int y, int w, int h, ui_form_t *form)
  154. {
  155. int i, j, c;
  156. if(!ctx || !form) return UI_ERR_BADINP;
  157. if(form == _ui_textosk_popup) {
  158. /* draw the osk */
  159. _ui_frect(ctx, ctx->px, ctx->py, ctx->pw, ctx->ph, 0xcf000000);
  160. y = ctx->py + 8; w = ctx->pw / 12; h = (ctx->ph - 24) / 5; c = (w < h ? w : h) - 4;
  161. j = (ctx->pw - w) / UI_NUMKBD;
  162. for(i = 0, x = w / 2; i < UI_NUMKBD; i++, x += j)
  163. _ui_textosk_key(ctx, x + (j - c) / 2, y + (h - c) / 2, c, c,
  164. UI_SEL == i ? 1 : (UI_KBD == i ? 2 : 0), (char*)_ui_textosk_keyboards[i].id);
  165. y += h + 8;
  166. for(i = j = 0; j < 3; j++, y += h)
  167. for(x = 0; x < 12 && i + UI_SCR < _ui_textosk_keyboards[UI_KBD].num; x++, i++)
  168. _ui_textosk_key(ctx, x * w + (w - c) / 2, y + (h - c) / 2, c, c,
  169. UI_SEL == UI_NUMKBD + i, (char*)_ui_textosk_keyboards[UI_KBD].keys[i + UI_SCR]);
  170. _ui_textosk_key(ctx, (w - c) / 2, y + (h - c) / 2, c, c, UI_SCR ? UI_SEL == UI_NUMKBD + 36 : -1, "\x1");
  171. _ui_textosk_key(ctx, w + (w - c) / 2, y + (h - c) / 2, c, c, UI_SCR + 36 < _ui_textosk_keyboards[UI_KBD].num ?
  172. UI_SEL == UI_NUMKBD + 37 : -1, "\x2");
  173. _ui_textosk_key(ctx, 2 * w + (w - c) / 2, y + (h - c) / 2, c, c, UI_SEL == UI_NUMKBD + 38, "\x3");
  174. _ui_textosk_key(ctx, 3 * w + (w - c) / 2, y + (h - c) / 2, c + 4 * w, c, UI_SEL == UI_NUMKBD + 39, " ");
  175. _ui_textosk_key(ctx, 8 * w + (w - c) / 2, y + (h - c) / 2, c, c, UI_SEL == UI_NUMKBD + 40, "\x4");
  176. _ui_textosk_key(ctx, 9 * w + (w - c) / 2, y + (h - c) / 2, c, c, UI_SEL == UI_NUMKBD + 41, "\x5");
  177. _ui_textosk_key(ctx, 10 * w + (w - c) / 2, y + (h - c) / 2, c + w, c, UI_SEL == UI_NUMKBD + 42, "\x6");
  178. } else
  179. /* draw the input field */
  180. _ui_text(ctx, x, y, w, h, form->l, form->t, form->flags, (ctx->text == form) | (form->inc == UI_FILTER_PASS ? 2 : 0),
  181. form->ptr, NULL);
  182. return UI_OK;
  183. }
  184. /**
  185. * Process events
  186. */
  187. int ui_textosk_ctrl(ui_t *ctx, int x, int y, int w, int h, ui_form_t *form, ui_event_t *evt)
  188. {
  189. static int joydir = 0;
  190. int i, j, dw, dh;
  191. char keystr[8] = { 0 };
  192. if(!ctx || !form || !evt) return UI_ERR_BADINP;
  193. if(!ctx->popup && evt->type == UI_EVT_MOUSE && (evt->btn & UI_BTN_L)) {
  194. /* user clicked on the text input field */
  195. if(form->ptr && form->max > 0) {
  196. evt->type = UI_EVT_NONE;
  197. ((char*)form->ptr)[form->max - 1] = 0;
  198. _ui_text_start(ctx, x, y, w, h, form, form->ptr, form->max);
  199. if(!(ctx->flags & UI_FLAGS_NOOSK)) {
  200. ctx->popup = _ui_textosk_popup;
  201. ctx->dr = ui_textosk_view;
  202. ctx->pe = ui_textosk_ctrl;
  203. ctx->px = 0;
  204. ctx->pw = ctx->screen.w;
  205. for(j = 0; j < UI_NUMKBD; j++) {
  206. if(!_ui_textosk_keyboards[j].num) {
  207. for(i = 0; _ui_textosk_keyboards[j].keys[i]; i++)
  208. if(ctx->fnt && ctx->bbox) {
  209. (*ctx->bbox)(ctx->fnt, (char*)_ui_textosk_keyboards[j].keys[i], NULL, &dw, &dh, NULL, NULL);
  210. if(UI_SIZ < dw) UI_SIZ = dw;
  211. if(UI_SIZ < dh) UI_SIZ = dh;
  212. }
  213. _ui_textosk_keyboards[j].num = i;
  214. }
  215. }
  216. ctx->ph = (UI_SIZ + 16) * 5 + 24;
  217. ctx->py = ctx->screen.h - ctx->ph;
  218. UI_SEL = -1;
  219. }
  220. }
  221. } else
  222. if(ctx->popup) {
  223. /* popup event handler */
  224. if(evt->type == UI_EVT_MOUSE) {
  225. /* touchpad */
  226. if(!(evt->btn & UI_BTN_RELEASE)) {
  227. UI_SEL = -1;
  228. y = ctx->py + 8; w = ctx->pw / 12; h = (ctx->ph - 24) / 5;
  229. if(ctx->mousey < ctx->py + 8 + h && ctx->mousex >= w / 2) {
  230. j = (ctx->pw - w) / UI_NUMKBD;
  231. i = (ctx->mousex - w / 2) / j;
  232. if(i < UI_NUMKBD) UI_SEL = i;
  233. } else
  234. if(ctx->mousey >= ctx->py + 16 + h && ctx->mousey < ctx->py + 16 + 4 * h) {
  235. i = (ctx->mousey - ctx->py - 16 - h) / h * 12 + ctx->mousex / w;
  236. if(i + UI_SCR < _ui_textosk_keyboards[UI_KBD].num) {
  237. UI_SEL = UI_NUMKBD + i;
  238. }
  239. } else
  240. if(ctx->mousey >= ctx->py + 16 + 4 * h) {
  241. if(ctx->mousex < w) UI_SEL = UI_NUMKBD + 36; else
  242. if(ctx->mousex < 2 * w) UI_SEL = UI_NUMKBD + 37; else
  243. if(ctx->mousex < 3 * w) UI_SEL = UI_NUMKBD + 38; else
  244. if(ctx->mousex < 8 * w) UI_SEL = UI_NUMKBD + 39; else
  245. if(ctx->mousex < 9 * w) UI_SEL = UI_NUMKBD + 40; else
  246. if(ctx->mousex < 10 * w) UI_SEL = UI_NUMKBD + 41; else UI_SEL = UI_NUMKBD + 42;
  247. }
  248. setsel: memset(keystr, 0, sizeof(keystr));
  249. if(UI_SEL >= 0 && UI_SEL < UI_NUMKBD) {
  250. if(_ui_textosk_keyboards[UI_SEL].pair != UI_KBD) UI_SCR = 0;
  251. UI_KBD = UI_SEL;
  252. } else
  253. if(UI_SEL >= UI_NUMKBD && UI_SEL < UI_NUMKBD + 36) {
  254. if(UI_SEL - UI_NUMKBD + UI_SCR < _ui_textosk_keyboards[UI_KBD].num)
  255. memcpy(keystr, _ui_textosk_keyboards[UI_KBD].keys[UI_SEL - UI_NUMKBD + UI_SCR], 4);
  256. } else
  257. if(UI_SEL == UI_NUMKBD + 36) { if(UI_SCR >= 36) UI_SCR -= 36; else UI_SCR = 0; } else
  258. if(UI_SEL == UI_NUMKBD + 37) { if(UI_SCR + 36 < _ui_textosk_keyboards[UI_KBD].num) UI_SCR += 36; } else
  259. if(UI_SEL == UI_NUMKBD + 38) keystr[0] = '\b'; else
  260. if(UI_SEL == UI_NUMKBD + 39) keystr[0] = ' '; else
  261. if(UI_SEL == UI_NUMKBD + 40) memcpy(keystr, "Left", 5); else
  262. if(UI_SEL == UI_NUMKBD + 41) memcpy(keystr, "Right", 6); else
  263. if(UI_SEL == UI_NUMKBD + 42) keystr[0] = '\n';
  264. if(UI_SEL == UI_NUMKBD + 36 && !UI_SCR) UI_SEL++;
  265. if(UI_SEL == UI_NUMKBD + 37 && UI_SCR + 36 >= _ui_textosk_keyboards[UI_KBD].num) { if(UI_SCR) UI_SEL--; else UI_SEL++; }
  266. } else UI_SEL = -1;
  267. if(keystr[0]) {
  268. memset(evt, 0, sizeof(ui_event_t));
  269. memcpy(evt->key, keystr, 8);
  270. evt->type = UI_EVT_KEY;
  271. _ui_text_ctrl(ctx, evt);
  272. memset(evt, 0, sizeof(ui_event_t));
  273. }
  274. } else
  275. if(evt->type == UI_EVT_KEY && (evt->btn & UI_BTN_GUI)) {
  276. /* fake "gamepad" */
  277. if(UI_SEL < 0) UI_SEL = 0;
  278. if(!memcmp(evt->key, "Up", 3)) goto up;
  279. if(!memcmp(evt->key, "Down", 5)) goto down;
  280. if(!memcmp(evt->key, "Left", 5)) goto left;
  281. if(!memcmp(evt->key, "Right", 6)) goto right;
  282. if(evt->key[0] == '\n' || evt->key[0] == ' ') goto setsel;
  283. } else
  284. if(evt->type == UI_EVT_GAMEPAD) {
  285. /* gamepad */
  286. if(UI_SEL < 0) UI_SEL = 0;
  287. if(evt->x < -8000 || evt->rx < -8000) joydir = UI_BTN_L;
  288. if(evt->x > 8000 || evt->rx > 8000) joydir = UI_BTN_R;
  289. if(evt->y < -8000 || evt->ry < -8000) joydir = UI_BTN_U;
  290. if(evt->y > 8000 || evt->ry > 8000) joydir = UI_BTN_D;
  291. if(evt->x > -8000 && evt->x < 8000 && evt->y > -8000 && evt->y < 8000 &&
  292. evt->rx > -8000 && evt->rx < 8000 && evt->ry > -8000 && evt->ry < 8000 && joydir) {
  293. evt->btn |= joydir; joydir = 0;
  294. }
  295. if(evt->btn & UI_BTN_U) {
  296. up: if(UI_SEL - 12 < UI_NUMKBD) {
  297. w = ctx->pw / 12; j = (ctx->pw - w) / UI_NUMKBD;
  298. UI_SEL = (UI_SEL - UI_NUMKBD) * w / j;
  299. } else
  300. if(UI_SEL >= UI_NUMKBD + 36) {
  301. i = UI_SEL - UI_NUMKBD - 36; if(i > 3) i += 4;
  302. i += UI_SCR + 36 < _ui_textosk_keyboards[UI_KBD].num ? 24 : ((_ui_textosk_keyboards[UI_KBD].num % 36) / 12) * 12;
  303. if(i + UI_SCR >= _ui_textosk_keyboards[UI_KBD].num) i -= 12;
  304. UI_SEL = UI_NUMKBD + i;
  305. } else UI_SEL -= 12;
  306. if(UI_SEL < 0 || UI_SEL > UI_NUMKBD + 42) UI_SEL = UI_NUMKBD + 42;
  307. } else
  308. if(evt->btn & UI_BTN_L) {
  309. left: if(UI_SEL > 0) UI_SEL--; else UI_SEL = UI_NUMKBD + 42;
  310. if(UI_SEL == UI_NUMKBD + 37 && UI_SCR + 36 >= _ui_textosk_keyboards[UI_KBD].num) UI_SEL--;
  311. if(UI_SEL == UI_NUMKBD + 36 && !UI_SCR) UI_SEL--;
  312. if(UI_SEL > UI_NUMKBD && UI_SEL < UI_NUMKBD + 36 && UI_SEL - UI_NUMKBD + UI_SCR >= _ui_textosk_keyboards[UI_KBD].num)
  313. UI_SEL = UI_NUMKBD + (_ui_textosk_keyboards[UI_KBD].num % 36) - 1;
  314. } else
  315. if(evt->btn & UI_BTN_R) {
  316. right: UI_SEL++;
  317. if(UI_SEL > UI_NUMKBD + 42) UI_SEL = 0;
  318. if(UI_SEL - UI_NUMKBD + UI_SCR >= _ui_textosk_keyboards[UI_KBD].num) UI_SEL = UI_NUMKBD + 36;
  319. if(UI_SEL == UI_NUMKBD + 36 && !UI_SCR) UI_SEL++;
  320. if(UI_SEL == UI_NUMKBD + 37 && UI_SCR + 36 >= _ui_textosk_keyboards[UI_KBD].num) UI_SEL++;
  321. } else
  322. if(evt->btn & UI_BTN_D) {
  323. down: if(UI_SEL < UI_NUMKBD) {
  324. w = ctx->pw / 12; j = (ctx->pw - w) / UI_NUMKBD;
  325. UI_SEL = UI_NUMKBD + ((w + UI_SEL * j) / w);
  326. } else {
  327. UI_SEL += 12;
  328. i = UI_SEL - UI_NUMKBD + UI_SCR;
  329. if(i >= _ui_textosk_keyboards[UI_KBD].num || i - UI_SCR >= 36) {
  330. i = (UI_SEL - UI_NUMKBD) % 12;
  331. UI_SEL = UI_NUMKBD + 36 + (i < 4 ? i : (i < 8 ? 3 : i - 4));
  332. }
  333. if(UI_SEL == UI_NUMKBD + 36 && !UI_SCR) UI_SEL++;
  334. if(UI_SEL == UI_NUMKBD + 37 && UI_SCR + 36 >= _ui_textosk_keyboards[UI_KBD].num) UI_SEL++;
  335. }
  336. if(UI_SEL < 0 || UI_SEL > UI_NUMKBD + 42) UI_SEL = UI_NUMKBD + 42;
  337. } else
  338. if(evt->btn & UI_BTN_BA) {
  339. ctx->flags |= UI_CLOSE;
  340. _ui_text_ctrl(ctx, evt);
  341. } else
  342. if(evt->btn & (UI_BTN_A | UI_BTN_B | UI_BTN_X | UI_BTN_Y)) goto setsel;
  343. }
  344. }
  345. return UI_OK;
  346. }
  347. #undef UI_SIZ
  348. #undef UI_KBD
  349. #undef UI_SCR
  350. #undef UI_SEL
  351. #undef UI_NUMKBD
  352. #ifdef __cplusplus
  353. }
  354. #endif
  355. #endif /* UI_TEXTOSK_H */