text_input.c 24 KB

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