text_input.c 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807
  1. #include "text_input.h"
  2. #ifndef FW_ORIGIN_Momentum
  3. extern const Icon I_KeySaveSelected_22x11;
  4. extern const Icon I_KeySave_22x11;
  5. extern const Icon I_KeyKeyboardSelected_10x11;
  6. extern const Icon I_KeyKeyboard_10x11;
  7. extern const Icon I_KeyBackspaceSelected_17x11;
  8. extern const Icon I_KeyBackspace_17x11;
  9. extern const Icon I_WarningDolphin_45x42;
  10. #include <gui/elements.h>
  11. #include <furi.h>
  12. struct TextInput {
  13. View* view;
  14. FuriTimer* timer;
  15. };
  16. typedef struct {
  17. const char text;
  18. const uint8_t x;
  19. const uint8_t y;
  20. } TextInputKey;
  21. typedef struct {
  22. const TextInputKey* rows[3];
  23. const uint8_t keyboard_index;
  24. } Keyboard;
  25. typedef struct {
  26. const char* header;
  27. char* text_buffer;
  28. size_t text_buffer_size;
  29. size_t minimum_length;
  30. bool clear_default_text;
  31. TextInputCallback callback;
  32. void* callback_context;
  33. uint8_t selected_row;
  34. uint8_t selected_column;
  35. TextInputValidatorCallback validator_callback;
  36. void* validator_callback_context;
  37. FuriString* validator_text;
  38. bool validator_message_visible;
  39. bool illegal_symbols;
  40. bool cursor_select;
  41. uint8_t selected_keyboard;
  42. size_t cursor_pos;
  43. } TextInputModel;
  44. static const uint8_t keyboard_origin_x = 1;
  45. static const uint8_t keyboard_origin_y = 29;
  46. static const uint8_t keyboard_row_count = 3;
  47. static const uint8_t keyboard_count = 2;
  48. #define ENTER_KEY '\r'
  49. #define BACKSPACE_KEY '\b'
  50. #define SWITCH_KEYBOARD_KEY '\t'
  51. static const TextInputKey keyboard_keys_row_1[] = {
  52. {'q', 1, 8},
  53. {'w', 10, 8},
  54. {'e', 19, 8},
  55. {'r', 28, 8},
  56. {'t', 37, 8},
  57. {'y', 46, 8},
  58. {'u', 55, 8},
  59. {'i', 64, 8},
  60. {'o', 73, 8},
  61. {'p', 82, 8},
  62. {'0', 92, 8},
  63. {'1', 102, 8},
  64. {'2', 111, 8},
  65. {'3', 120, 8},
  66. };
  67. static const TextInputKey keyboard_keys_row_2[] = {
  68. {'a', 1, 20},
  69. {'s', 10, 20},
  70. {'d', 19, 20},
  71. {'f', 28, 20},
  72. {'g', 37, 20},
  73. {'h', 46, 20},
  74. {'j', 55, 20},
  75. {'k', 64, 20},
  76. {'l', 73, 20},
  77. {BACKSPACE_KEY, 82, 11},
  78. {'4', 102, 20},
  79. {'5', 111, 20},
  80. {'6', 120, 20},
  81. };
  82. static const TextInputKey keyboard_keys_row_3[] = {
  83. {SWITCH_KEYBOARD_KEY, 0, 23},
  84. {'z', 13, 32},
  85. {'x', 21, 32},
  86. {'c', 29, 32},
  87. {'v', 37, 32},
  88. {'b', 45, 32},
  89. {'n', 53, 32},
  90. {'m', 61, 32},
  91. {'_', 69, 32},
  92. {ENTER_KEY, 77, 23},
  93. {'7', 102, 32},
  94. {'8', 111, 32},
  95. {'9', 120, 32},
  96. };
  97. static const TextInputKey symbol_keyboard_keys_row_1[] = {
  98. {'!', 2, 8},
  99. {'@', 12, 8},
  100. {'#', 22, 8},
  101. {'$', 32, 8},
  102. {'%', 42, 8},
  103. {'^', 52, 8},
  104. {'&', 62, 8},
  105. {'(', 71, 8},
  106. {')', 81, 8},
  107. {'0', 92, 8},
  108. {'1', 102, 8},
  109. {'2', 111, 8},
  110. {'3', 120, 8},
  111. };
  112. static const TextInputKey symbol_keyboard_keys_row_2[] = {
  113. {'~', 2, 20},
  114. {'+', 12, 20},
  115. {'-', 22, 20},
  116. {'=', 32, 20},
  117. {'[', 42, 20},
  118. {']', 52, 20},
  119. {'{', 62, 20},
  120. {'}', 72, 20},
  121. {BACKSPACE_KEY, 82, 11},
  122. {'4', 102, 20},
  123. {'5', 111, 20},
  124. {'6', 120, 20},
  125. };
  126. static const TextInputKey symbol_keyboard_keys_row_3[] = {
  127. {SWITCH_KEYBOARD_KEY, 0, 23},
  128. {'.', 15, 32},
  129. {',', 29, 32},
  130. {';', 41, 32},
  131. {'`', 53, 32},
  132. {'\'', 65, 32},
  133. {ENTER_KEY, 77, 23},
  134. {'7', 102, 32},
  135. {'8', 111, 32},
  136. {'9', 120, 32},
  137. };
  138. static const Keyboard keyboard = {
  139. .rows =
  140. {
  141. keyboard_keys_row_1,
  142. keyboard_keys_row_2,
  143. keyboard_keys_row_3,
  144. },
  145. .keyboard_index = 0,
  146. };
  147. static const Keyboard symbol_keyboard = {
  148. .rows =
  149. {
  150. symbol_keyboard_keys_row_1,
  151. symbol_keyboard_keys_row_2,
  152. symbol_keyboard_keys_row_3,
  153. },
  154. .keyboard_index = 1,
  155. };
  156. static const Keyboard* keyboards[] = {
  157. &keyboard,
  158. &symbol_keyboard,
  159. };
  160. static void switch_keyboard(TextInputModel* model) {
  161. model->selected_keyboard = (model->selected_keyboard + 1) % keyboard_count;
  162. }
  163. static uint8_t get_row_size(const Keyboard* keyboard, uint8_t row_index) {
  164. uint8_t row_size = 0;
  165. if(keyboard == &symbol_keyboard) {
  166. switch(row_index + 1) {
  167. case 1:
  168. row_size = COUNT_OF(symbol_keyboard_keys_row_1);
  169. break;
  170. case 2:
  171. row_size = COUNT_OF(symbol_keyboard_keys_row_2);
  172. break;
  173. case 3:
  174. row_size = COUNT_OF(symbol_keyboard_keys_row_3);
  175. break;
  176. default:
  177. furi_crash();
  178. }
  179. } else {
  180. switch(row_index + 1) {
  181. case 1:
  182. row_size = COUNT_OF(keyboard_keys_row_1);
  183. break;
  184. case 2:
  185. row_size = COUNT_OF(keyboard_keys_row_2);
  186. break;
  187. case 3:
  188. row_size = COUNT_OF(keyboard_keys_row_3);
  189. break;
  190. default:
  191. furi_crash();
  192. }
  193. }
  194. return row_size;
  195. }
  196. static const TextInputKey* get_row(const Keyboard* keyboard, uint8_t row_index) {
  197. const TextInputKey* row = NULL;
  198. if(row_index < 3) {
  199. row = keyboard->rows[row_index];
  200. } else {
  201. furi_crash();
  202. }
  203. return row;
  204. }
  205. static char get_selected_char(TextInputModel* model) {
  206. return get_row(
  207. keyboards[model->selected_keyboard], model->selected_row)[model->selected_column]
  208. .text;
  209. }
  210. static bool char_is_lowercase(char letter) {
  211. return letter >= 0x61 && letter <= 0x7A;
  212. }
  213. static char char_to_uppercase(const char letter) {
  214. if(letter == '_') {
  215. return 0x20;
  216. } else if(char_is_lowercase(letter)) {
  217. return letter - 0x20;
  218. } else {
  219. return letter;
  220. }
  221. }
  222. static char char_to_illegal_symbol(char original) {
  223. switch(original) {
  224. default:
  225. return original;
  226. case '0':
  227. return '_';
  228. case '1':
  229. return '<';
  230. case '2':
  231. return '>';
  232. case '3':
  233. return ':';
  234. case '4':
  235. return '"';
  236. case '5':
  237. return '/';
  238. case '6':
  239. return '\\';
  240. case '7':
  241. return '|';
  242. case '8':
  243. return '?';
  244. case '9':
  245. return '*';
  246. }
  247. }
  248. static void text_input_backspace_cb(TextInputModel* model) {
  249. if(model->clear_default_text) {
  250. model->text_buffer[0] = 0;
  251. model->cursor_pos = 0;
  252. } else if(model->cursor_pos > 0) {
  253. char* move = model->text_buffer + model->cursor_pos;
  254. memmove(move - 1, move, strlen(move) + 1);
  255. model->cursor_pos--;
  256. }
  257. }
  258. static void text_input_view_draw_callback(Canvas* canvas, void* _model) {
  259. TextInputModel* model = _model;
  260. uint8_t text_length = model->text_buffer ? strlen(model->text_buffer) : 0;
  261. uint8_t needed_string_width = canvas_width(canvas) - 8;
  262. uint8_t start_pos = 4;
  263. model->cursor_pos = model->cursor_pos > text_length ? text_length : model->cursor_pos;
  264. size_t cursor_pos = model->cursor_pos;
  265. canvas_clear(canvas);
  266. canvas_set_color(canvas, ColorBlack);
  267. canvas_draw_str(canvas, 2, 8, model->header);
  268. elements_slightly_rounded_frame(canvas, 1, 12, 126, 15);
  269. char buf[text_length + 1];
  270. if(model->text_buffer) {
  271. strlcpy(buf, model->text_buffer, sizeof(buf));
  272. }
  273. char* str = buf;
  274. if(model->clear_default_text) {
  275. elements_slightly_rounded_box(
  276. canvas, start_pos - 1, 14, canvas_string_width(canvas, str) + 2, 10);
  277. canvas_set_color(canvas, ColorWhite);
  278. } else {
  279. char* move = str + cursor_pos;
  280. memmove(move + 1, move, strlen(move) + 1);
  281. str[cursor_pos] = '|';
  282. }
  283. if(cursor_pos > 0 && canvas_string_width(canvas, str) > needed_string_width) {
  284. canvas_draw_str(canvas, start_pos, 22, "...");
  285. start_pos += 6;
  286. needed_string_width -= 8;
  287. for(uint32_t off = 0;
  288. strlen(str) && canvas_string_width(canvas, str) > needed_string_width &&
  289. off < cursor_pos;
  290. off++) {
  291. str++;
  292. }
  293. }
  294. if(canvas_string_width(canvas, str) > needed_string_width) {
  295. needed_string_width -= 4;
  296. size_t len = strlen(str);
  297. while(len && canvas_string_width(canvas, str) > needed_string_width) {
  298. str[len--] = '\0';
  299. }
  300. strlcat(str, "...", sizeof(buf) - (str - buf));
  301. }
  302. canvas_draw_str(canvas, start_pos, 22, str);
  303. canvas_set_font(canvas, FontKeyboard);
  304. bool uppercase = model->clear_default_text || text_length == 0;
  305. bool symbols = model->selected_keyboard == symbol_keyboard.keyboard_index;
  306. for(uint8_t row = 0; row < keyboard_row_count; row++) {
  307. const uint8_t column_count = get_row_size(keyboards[model->selected_keyboard], row);
  308. const TextInputKey* keys = get_row(keyboards[model->selected_keyboard], row);
  309. for(size_t column = 0; column < column_count; column++) {
  310. bool selected = !model->cursor_select && model->selected_row == row &&
  311. model->selected_column == column;
  312. const Icon* icon = NULL;
  313. if(keys[column].text == ENTER_KEY) {
  314. icon = selected ? &I_KeySaveSelected_22x11 : &I_KeySave_22x11;
  315. } else if(keys[column].text == SWITCH_KEYBOARD_KEY) {
  316. icon = selected ? &I_KeyKeyboardSelected_10x11 : &I_KeyKeyboard_10x11;
  317. } else if(keys[column].text == BACKSPACE_KEY) {
  318. icon = selected ? &I_KeyBackspaceSelected_17x11 : &I_KeyBackspace_17x11;
  319. }
  320. canvas_set_color(canvas, ColorBlack);
  321. if(icon != NULL) {
  322. canvas_draw_icon(
  323. canvas,
  324. keyboard_origin_x + keys[column].x,
  325. keyboard_origin_y + keys[column].y,
  326. icon);
  327. } else {
  328. if(selected) {
  329. elements_slightly_rounded_box(
  330. canvas,
  331. keyboard_origin_x + keys[column].x - 2,
  332. keyboard_origin_y + keys[column].y - 9,
  333. 9,
  334. 11);
  335. canvas_set_color(canvas, ColorWhite);
  336. }
  337. char glyph = keys[column].text;
  338. if(uppercase && !symbols) {
  339. canvas_draw_glyph(
  340. canvas,
  341. keyboard_origin_x + keys[column].x,
  342. keyboard_origin_y + keys[column].y,
  343. char_to_uppercase(glyph));
  344. } else {
  345. canvas_draw_glyph(
  346. canvas,
  347. keyboard_origin_x + keys[column].x,
  348. keyboard_origin_y + keys[column].y -
  349. (glyph == '_' || char_is_lowercase(glyph)),
  350. (symbols && model->illegal_symbols) ? char_to_illegal_symbol(glyph) :
  351. glyph);
  352. }
  353. }
  354. }
  355. }
  356. if(model->validator_message_visible) {
  357. canvas_set_font(canvas, FontSecondary);
  358. canvas_set_color(canvas, ColorWhite);
  359. canvas_draw_box(canvas, 8, 10, 110, 48);
  360. canvas_set_color(canvas, ColorBlack);
  361. canvas_draw_icon(canvas, 10, 14, &I_WarningDolphin_45x42);
  362. canvas_draw_rframe(canvas, 8, 8, 112, 50, 3);
  363. canvas_draw_rframe(canvas, 9, 9, 110, 48, 2);
  364. elements_multiline_text(canvas, 62, 20, furi_string_get_cstr(model->validator_text));
  365. canvas_set_font(canvas, FontKeyboard);
  366. }
  367. }
  368. static void text_input_handle_up(TextInput* text_input, TextInputModel* model) {
  369. UNUSED(text_input);
  370. if(model->selected_row > 0) {
  371. model->selected_row--;
  372. if(model->selected_row == 0 &&
  373. model->selected_column >
  374. get_row_size(keyboards[model->selected_keyboard], model->selected_row) - 6) {
  375. model->selected_column = model->selected_column + 1;
  376. }
  377. if(model->selected_row == 1 &&
  378. model->selected_keyboard == symbol_keyboard.keyboard_index) {
  379. if(model->selected_column > 5)
  380. model->selected_column += 2;
  381. else if(model->selected_column > 1)
  382. model->selected_column += 1;
  383. }
  384. } else {
  385. model->cursor_select = true;
  386. model->clear_default_text = false;
  387. }
  388. }
  389. static void text_input_handle_down(TextInput* text_input, TextInputModel* model) {
  390. UNUSED(text_input);
  391. if(model->cursor_select) {
  392. model->cursor_select = false;
  393. } else if(model->selected_row < keyboard_row_count - 1) {
  394. model->selected_row++;
  395. if(model->selected_row == 1 &&
  396. model->selected_column >
  397. get_row_size(keyboards[model->selected_keyboard], model->selected_row) - 4) {
  398. model->selected_column = model->selected_column - 1;
  399. }
  400. if(model->selected_row == 2 &&
  401. model->selected_keyboard == symbol_keyboard.keyboard_index) {
  402. if(model->selected_column > 6)
  403. model->selected_column -= 2;
  404. else if(model->selected_column > 1)
  405. model->selected_column -= 1;
  406. }
  407. }
  408. }
  409. static void text_input_handle_left(TextInput* text_input, TextInputModel* model) {
  410. UNUSED(text_input);
  411. if(model->cursor_select) {
  412. model->clear_default_text = false;
  413. if(model->cursor_pos > 0) {
  414. model->cursor_pos = CLAMP(model->cursor_pos - 1, strlen(model->text_buffer), 0u);
  415. }
  416. } else if(model->selected_column > 0) {
  417. model->selected_column--;
  418. } else {
  419. model->selected_column =
  420. get_row_size(keyboards[model->selected_keyboard], model->selected_row) - 1;
  421. }
  422. }
  423. static void text_input_handle_right(TextInput* text_input, TextInputModel* model) {
  424. UNUSED(text_input);
  425. if(model->cursor_select) {
  426. model->clear_default_text = false;
  427. model->cursor_pos = CLAMP(model->cursor_pos + 1, strlen(model->text_buffer), 0u);
  428. } else if(
  429. model->selected_column <
  430. get_row_size(keyboards[model->selected_keyboard], model->selected_row) - 1) {
  431. model->selected_column++;
  432. } else {
  433. model->selected_column = 0;
  434. }
  435. }
  436. static void text_input_handle_ok(TextInput* text_input, TextInputModel* model, InputType type) {
  437. if(model->cursor_select) {
  438. model->clear_default_text = !model->clear_default_text;
  439. return;
  440. }
  441. bool shift = type == InputTypeLong;
  442. bool repeat = type == InputTypeRepeat;
  443. char selected = get_selected_char(model);
  444. size_t text_length = strlen(model->text_buffer);
  445. if(selected == ENTER_KEY) {
  446. if(model->validator_callback &&
  447. (!model->validator_callback(
  448. model->text_buffer, model->validator_text, model->validator_callback_context))) {
  449. model->validator_message_visible = true;
  450. furi_timer_start(text_input->timer, furi_kernel_get_tick_frequency() * 4);
  451. } else if(model->callback != 0 && text_length >= model->minimum_length) {
  452. model->callback(model->callback_context);
  453. }
  454. } else if(selected == SWITCH_KEYBOARD_KEY) {
  455. switch_keyboard(model);
  456. } else {
  457. if(selected == BACKSPACE_KEY) {
  458. text_input_backspace_cb(model);
  459. } else if(!repeat) {
  460. if(model->clear_default_text) {
  461. text_length = 0;
  462. }
  463. if(text_length < (model->text_buffer_size - 1)) {
  464. if(shift != (text_length == 0) &&
  465. model->selected_keyboard != symbol_keyboard.keyboard_index) {
  466. selected = char_to_uppercase(selected);
  467. }
  468. if(model->selected_keyboard == symbol_keyboard.keyboard_index &&
  469. model->illegal_symbols) {
  470. selected = char_to_illegal_symbol(selected);
  471. }
  472. if(model->clear_default_text) {
  473. model->text_buffer[0] = selected;
  474. model->text_buffer[1] = '\0';
  475. model->cursor_pos = 1;
  476. } else {
  477. char* move = model->text_buffer + model->cursor_pos;
  478. memmove(move + 1, move, strlen(move) + 1);
  479. model->text_buffer[model->cursor_pos] = selected;
  480. model->cursor_pos++;
  481. }
  482. }
  483. }
  484. model->clear_default_text = false;
  485. }
  486. }
  487. static bool text_input_view_input_callback(InputEvent* event, void* context) {
  488. TextInput* text_input = context;
  489. furi_assert(text_input);
  490. bool consumed = false;
  491. // Acquire model
  492. TextInputModel* model = view_get_model(text_input->view);
  493. if((!(event->type == InputTypePress) && !(event->type == InputTypeRelease)) &&
  494. model->validator_message_visible) {
  495. model->validator_message_visible = false;
  496. consumed = true;
  497. } else if(event->type == InputTypeShort) {
  498. consumed = true;
  499. switch(event->key) {
  500. case InputKeyUp:
  501. text_input_handle_up(text_input, model);
  502. break;
  503. case InputKeyDown:
  504. text_input_handle_down(text_input, model);
  505. break;
  506. case InputKeyLeft:
  507. text_input_handle_left(text_input, model);
  508. break;
  509. case InputKeyRight:
  510. text_input_handle_right(text_input, model);
  511. break;
  512. case InputKeyOk:
  513. text_input_handle_ok(text_input, model, event->type);
  514. break;
  515. default:
  516. consumed = false;
  517. break;
  518. }
  519. } else if(event->type == InputTypeLong) {
  520. consumed = true;
  521. switch(event->key) {
  522. case InputKeyUp:
  523. text_input_handle_up(text_input, model);
  524. break;
  525. case InputKeyDown:
  526. text_input_handle_down(text_input, model);
  527. break;
  528. case InputKeyLeft:
  529. text_input_handle_left(text_input, model);
  530. break;
  531. case InputKeyRight:
  532. text_input_handle_right(text_input, model);
  533. break;
  534. case InputKeyOk:
  535. text_input_handle_ok(text_input, model, event->type);
  536. break;
  537. case InputKeyBack:
  538. text_input_backspace_cb(model);
  539. break;
  540. default:
  541. consumed = false;
  542. break;
  543. }
  544. } else if(event->type == InputTypeRepeat) {
  545. consumed = true;
  546. switch(event->key) {
  547. case InputKeyUp:
  548. text_input_handle_up(text_input, model);
  549. break;
  550. case InputKeyDown:
  551. text_input_handle_down(text_input, model);
  552. break;
  553. case InputKeyLeft:
  554. text_input_handle_left(text_input, model);
  555. break;
  556. case InputKeyRight:
  557. text_input_handle_right(text_input, model);
  558. break;
  559. case InputKeyOk:
  560. text_input_handle_ok(text_input, model, event->type);
  561. break;
  562. case InputKeyBack:
  563. text_input_backspace_cb(model);
  564. break;
  565. default:
  566. consumed = false;
  567. break;
  568. }
  569. }
  570. // Commit model
  571. view_commit_model(text_input->view, consumed);
  572. return consumed;
  573. }
  574. void text_input_timer_callback(void* context) {
  575. furi_assert(context);
  576. TextInput* text_input = context;
  577. with_view_model(
  578. text_input->view,
  579. TextInputModel * model,
  580. { model->validator_message_visible = false; },
  581. true);
  582. }
  583. TextInput* text_input_alloc(void) {
  584. TextInput* text_input = malloc(sizeof(TextInput));
  585. text_input->view = view_alloc();
  586. view_set_context(text_input->view, text_input);
  587. view_allocate_model(text_input->view, ViewModelTypeLocking, sizeof(TextInputModel));
  588. view_set_draw_callback(text_input->view, text_input_view_draw_callback);
  589. view_set_input_callback(text_input->view, text_input_view_input_callback);
  590. text_input->timer = furi_timer_alloc(text_input_timer_callback, FuriTimerTypeOnce, text_input);
  591. with_view_model(
  592. text_input->view,
  593. TextInputModel * model,
  594. {
  595. model->validator_text = furi_string_alloc();
  596. model->minimum_length = 1;
  597. model->illegal_symbols = false;
  598. model->cursor_pos = 0;
  599. model->cursor_select = false;
  600. },
  601. false);
  602. text_input_reset(text_input);
  603. return text_input;
  604. }
  605. void text_input_free(TextInput* text_input) {
  606. furi_check(text_input);
  607. with_view_model(
  608. text_input->view,
  609. TextInputModel * model,
  610. { furi_string_free(model->validator_text); },
  611. false);
  612. // Send stop command
  613. furi_timer_stop(text_input->timer);
  614. // Release allocated memory
  615. furi_timer_free(text_input->timer);
  616. view_free(text_input->view);
  617. free(text_input);
  618. }
  619. void text_input_reset(TextInput* text_input) {
  620. furi_check(text_input);
  621. with_view_model(
  622. text_input->view,
  623. TextInputModel * model,
  624. {
  625. model->header = "";
  626. model->selected_row = 0;
  627. model->selected_column = 0;
  628. model->selected_keyboard = 0;
  629. model->minimum_length = 1;
  630. model->illegal_symbols = false;
  631. model->clear_default_text = false;
  632. model->cursor_pos = 0;
  633. model->cursor_select = false;
  634. model->text_buffer = NULL;
  635. model->text_buffer_size = 0;
  636. model->callback = NULL;
  637. model->callback_context = NULL;
  638. model->validator_callback = NULL;
  639. model->validator_callback_context = NULL;
  640. furi_string_reset(model->validator_text);
  641. model->validator_message_visible = false;
  642. },
  643. true);
  644. }
  645. View* text_input_get_view(TextInput* text_input) {
  646. furi_check(text_input);
  647. return text_input->view;
  648. }
  649. void text_input_set_result_callback(
  650. TextInput* text_input,
  651. TextInputCallback callback,
  652. void* callback_context,
  653. char* text_buffer,
  654. size_t text_buffer_size,
  655. bool clear_default_text) {
  656. furi_check(text_input);
  657. with_view_model(
  658. text_input->view,
  659. TextInputModel * model,
  660. {
  661. model->callback = callback;
  662. model->callback_context = callback_context;
  663. model->text_buffer = text_buffer;
  664. model->text_buffer_size = text_buffer_size;
  665. model->clear_default_text = clear_default_text;
  666. model->cursor_select = false;
  667. if(text_buffer && text_buffer[0] != '\0') {
  668. model->cursor_pos = strlen(text_buffer);
  669. // Set focus on Save
  670. model->selected_row = 2;
  671. model->selected_column = 9;
  672. model->selected_keyboard = 0;
  673. } else {
  674. model->cursor_pos = 0;
  675. }
  676. },
  677. true);
  678. }
  679. void text_input_set_minimum_length(TextInput* text_input, size_t minimum_length) {
  680. furi_check(text_input);
  681. with_view_model(
  682. text_input->view,
  683. TextInputModel * model,
  684. { model->minimum_length = minimum_length; },
  685. true);
  686. }
  687. void text_input_show_illegal_symbols(TextInput* text_input, bool show) {
  688. furi_check(text_input);
  689. with_view_model(
  690. text_input->view, TextInputModel * model, { model->illegal_symbols = show; }, true);
  691. }
  692. void text_input_set_validator(
  693. TextInput* text_input,
  694. TextInputValidatorCallback callback,
  695. void* callback_context) {
  696. furi_check(text_input);
  697. with_view_model(
  698. text_input->view,
  699. TextInputModel * model,
  700. {
  701. model->validator_callback = callback;
  702. model->validator_callback_context = callback_context;
  703. },
  704. true);
  705. }
  706. TextInputValidatorCallback text_input_get_validator_callback(TextInput* text_input) {
  707. furi_check(text_input);
  708. TextInputValidatorCallback validator_callback = NULL;
  709. with_view_model(
  710. text_input->view,
  711. TextInputModel * model,
  712. { validator_callback = model->validator_callback; },
  713. false);
  714. return validator_callback;
  715. }
  716. void* text_input_get_validator_callback_context(TextInput* text_input) {
  717. furi_check(text_input);
  718. void* validator_callback_context = NULL;
  719. with_view_model(
  720. text_input->view,
  721. TextInputModel * model,
  722. { validator_callback_context = model->validator_callback_context; },
  723. false);
  724. return validator_callback_context;
  725. }
  726. void text_input_set_header_text(TextInput* text_input, const char* text) {
  727. furi_check(text_input);
  728. with_view_model(text_input->view, TextInputModel * model, { model->header = text; }, true);
  729. }
  730. #endif