uart_text_input.h 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703
  1. // from https://github.com/xMasterX/all-the-plugins/blob/dev/base_pack/uart_terminal/uart_text_input.c
  2. // all credits to xMasterX for the code
  3. #ifndef UART_TEXT_INPUT_H
  4. #define UART_TEXT_INPUT_H
  5. #include <gui/elements.h>
  6. #include "flip_library_icons.h"
  7. #include <furi.h>
  8. #include <furi.h>
  9. #include <furi_hal.h>
  10. #include <gui/gui.h>
  11. #include <gui/view.h>
  12. #include <core/common_defines.h>
  13. /** Text input anonymous structure */
  14. typedef struct UART_TextInput UART_TextInput;
  15. typedef void (*UART_TextInputCallback)(void* context);
  16. typedef bool (*UART_TextInputValidatorCallback)(const char* text, FuriString* error, void* context);
  17. UART_TextInputValidatorCallback
  18. uart_text_input_get_validator_callback(UART_TextInput* uart_text_input);
  19. void uart_text_input_reset(UART_TextInput* uart_text_input);
  20. struct UART_TextInput {
  21. View* view;
  22. FuriTimer* timer;
  23. };
  24. typedef struct {
  25. const char text;
  26. const uint8_t x;
  27. const uint8_t y;
  28. } UART_TextInputKey;
  29. typedef struct {
  30. const char* header;
  31. char* text_buffer;
  32. size_t text_buffer_size;
  33. bool clear_default_text;
  34. UART_TextInputCallback callback;
  35. void* callback_context;
  36. uint8_t selected_row;
  37. uint8_t selected_column;
  38. UART_TextInputValidatorCallback validator_callback;
  39. void* validator_callback_context;
  40. FuriString* validator_text;
  41. bool valadator_message_visible;
  42. } UART_TextInputModel;
  43. static const uint8_t keyboard_origin_x = 1;
  44. static const uint8_t keyboard_origin_y = 29;
  45. static const uint8_t keyboard_row_count = 4;
  46. #define mode_AT "Send AT command to UART"
  47. #define ENTER_KEY '\r'
  48. #define BACKSPACE_KEY '\b'
  49. static const UART_TextInputKey keyboard_keys_row_1[] = {
  50. {'{', 1, 0},
  51. {'(', 9, 0},
  52. {'[', 17, 0},
  53. {'|', 25, 0},
  54. {'@', 33, 0},
  55. {'&', 41, 0},
  56. {'#', 49, 0},
  57. {';', 57, 0},
  58. {'^', 65, 0},
  59. {'*', 73, 0},
  60. {'`', 81, 0},
  61. {'"', 89, 0},
  62. {'~', 97, 0},
  63. {'\'', 105, 0},
  64. {'.', 113, 0},
  65. {'/', 120, 0},
  66. };
  67. static const UART_TextInputKey keyboard_keys_row_2[] = {
  68. {'q', 1, 10},
  69. {'w', 9, 10},
  70. {'e', 17, 10},
  71. {'r', 25, 10},
  72. {'t', 33, 10},
  73. {'y', 41, 10},
  74. {'u', 49, 10},
  75. {'i', 57, 10},
  76. {'o', 65, 10},
  77. {'p', 73, 10},
  78. {'0', 81, 10},
  79. {'1', 89, 10},
  80. {'2', 97, 10},
  81. {'3', 105, 10},
  82. {'=', 113, 10},
  83. {'-', 120, 10},
  84. };
  85. static const UART_TextInputKey keyboard_keys_row_3[] = {
  86. {'a', 1, 21},
  87. {'s', 9, 21},
  88. {'d', 18, 21},
  89. {'f', 25, 21},
  90. {'g', 33, 21},
  91. {'h', 41, 21},
  92. {'j', 49, 21},
  93. {'k', 57, 21},
  94. {'l', 65, 21},
  95. {BACKSPACE_KEY, 72, 13},
  96. {'4', 89, 21},
  97. {'5', 97, 21},
  98. {'6', 105, 21},
  99. {'$', 113, 21},
  100. {'%', 120, 21},
  101. };
  102. static const UART_TextInputKey keyboard_keys_row_4[] = {
  103. {'z', 1, 33},
  104. {'x', 9, 33},
  105. {'c', 18, 33},
  106. {'v', 25, 33},
  107. {'b', 33, 33},
  108. {'n', 41, 33},
  109. {'m', 49, 33},
  110. {'_', 57, 33},
  111. {ENTER_KEY, 64, 24},
  112. {'7', 89, 33},
  113. {'8', 97, 33},
  114. {'9', 105, 33},
  115. {'!', 113, 33},
  116. {'+', 120, 33},
  117. };
  118. static uint8_t get_row_size(uint8_t row_index) {
  119. uint8_t row_size = 0;
  120. switch(row_index + 1) {
  121. case 1:
  122. row_size = sizeof(keyboard_keys_row_1) / sizeof(UART_TextInputKey);
  123. break;
  124. case 2:
  125. row_size = sizeof(keyboard_keys_row_2) / sizeof(UART_TextInputKey);
  126. break;
  127. case 3:
  128. row_size = sizeof(keyboard_keys_row_3) / sizeof(UART_TextInputKey);
  129. break;
  130. case 4:
  131. row_size = sizeof(keyboard_keys_row_4) / sizeof(UART_TextInputKey);
  132. break;
  133. }
  134. return row_size;
  135. }
  136. static const UART_TextInputKey* get_row(uint8_t row_index) {
  137. const UART_TextInputKey* row = NULL;
  138. switch(row_index + 1) {
  139. case 1:
  140. row = keyboard_keys_row_1;
  141. break;
  142. case 2:
  143. row = keyboard_keys_row_2;
  144. break;
  145. case 3:
  146. row = keyboard_keys_row_3;
  147. break;
  148. case 4:
  149. row = keyboard_keys_row_4;
  150. break;
  151. }
  152. return row;
  153. }
  154. static char get_selected_char(UART_TextInputModel* model) {
  155. return get_row(model->selected_row)[model->selected_column].text;
  156. }
  157. static bool char_is_lowercase(char letter) {
  158. return (letter >= 0x61 && letter <= 0x7A);
  159. }
  160. static bool char_is_uppercase(char letter) {
  161. return (letter >= 0x41 && letter <= 0x5A);
  162. }
  163. static char char_to_lowercase(const char letter) {
  164. switch(letter) {
  165. case ' ':
  166. return 0x5f;
  167. break;
  168. case ')':
  169. return 0x28;
  170. break;
  171. case '}':
  172. return 0x7b;
  173. break;
  174. case ']':
  175. return 0x5b;
  176. break;
  177. case '\\':
  178. return 0x2f;
  179. break;
  180. case ':':
  181. return 0x3b;
  182. break;
  183. case ',':
  184. return 0x2e;
  185. break;
  186. case '?':
  187. return 0x21;
  188. break;
  189. case '>':
  190. return 0x3c;
  191. break;
  192. }
  193. if(char_is_uppercase(letter)) {
  194. return (letter + 0x20);
  195. } else {
  196. return letter;
  197. }
  198. }
  199. static char char_to_uppercase(const char letter) {
  200. switch(letter) {
  201. case '_':
  202. return 0x20;
  203. break;
  204. case '(':
  205. return 0x29;
  206. break;
  207. case '{':
  208. return 0x7d;
  209. break;
  210. case '[':
  211. return 0x5d;
  212. break;
  213. case '/':
  214. return 0x5c;
  215. break;
  216. case ';':
  217. return 0x3a;
  218. break;
  219. case '.':
  220. return 0x2c;
  221. break;
  222. case '!':
  223. return 0x3f;
  224. break;
  225. case '<':
  226. return 0x3e;
  227. break;
  228. }
  229. if(char_is_lowercase(letter)) {
  230. return (letter - 0x20);
  231. } else {
  232. return letter;
  233. }
  234. }
  235. static void uart_text_input_backspace_cb(UART_TextInputModel* model) {
  236. uint8_t text_length = model->clear_default_text ? 1 : strlen(model->text_buffer);
  237. if(text_length > 0) {
  238. model->text_buffer[text_length - 1] = 0;
  239. }
  240. }
  241. static void uart_text_input_view_draw_callback(Canvas* canvas, void* _model) {
  242. UART_TextInputModel* model = _model;
  243. // uint8_t text_length = model->text_buffer ? strlen(model->text_buffer) : 0;
  244. uint8_t needed_string_width = canvas_width(canvas) - 8;
  245. uint8_t start_pos = 4;
  246. const char* text = model->text_buffer;
  247. canvas_clear(canvas);
  248. canvas_set_color(canvas, ColorBlack);
  249. canvas_draw_str(canvas, 2, 7, model->header);
  250. elements_slightly_rounded_frame(canvas, 1, 8, 126, 12);
  251. if(canvas_string_width(canvas, text) > needed_string_width) {
  252. canvas_draw_str(canvas, start_pos, 17, "...");
  253. start_pos += 6;
  254. needed_string_width -= 8;
  255. }
  256. while(text != 0 && canvas_string_width(canvas, text) > needed_string_width) {
  257. text++;
  258. }
  259. if(model->clear_default_text) {
  260. elements_slightly_rounded_box(
  261. canvas, start_pos - 1, 14, canvas_string_width(canvas, text) + 2, 10);
  262. canvas_set_color(canvas, ColorWhite);
  263. } else {
  264. canvas_draw_str(canvas, start_pos + canvas_string_width(canvas, text) + 1, 18, "|");
  265. canvas_draw_str(canvas, start_pos + canvas_string_width(canvas, text) + 2, 18, "|");
  266. }
  267. canvas_draw_str(canvas, start_pos, 17, text);
  268. canvas_set_font(canvas, FontKeyboard);
  269. for(uint8_t row = 0; row <= keyboard_row_count; row++) {
  270. const uint8_t column_count = get_row_size(row);
  271. const UART_TextInputKey* keys = get_row(row);
  272. for(size_t column = 0; column < column_count; column++) {
  273. if(keys[column].text == ENTER_KEY) {
  274. canvas_set_color(canvas, ColorBlack);
  275. if(model->selected_row == row && model->selected_column == column) {
  276. canvas_draw_icon(
  277. canvas,
  278. keyboard_origin_x + keys[column].x,
  279. keyboard_origin_y + keys[column].y,
  280. &I_KeySaveSelected_24x11);
  281. } else {
  282. canvas_draw_icon(
  283. canvas,
  284. keyboard_origin_x + keys[column].x,
  285. keyboard_origin_y + keys[column].y,
  286. &I_KeySave_24x11);
  287. }
  288. } else if(keys[column].text == BACKSPACE_KEY) {
  289. canvas_set_color(canvas, ColorBlack);
  290. if(model->selected_row == row && model->selected_column == column) {
  291. canvas_draw_icon(
  292. canvas,
  293. keyboard_origin_x + keys[column].x,
  294. keyboard_origin_y + keys[column].y,
  295. &I_KeyBackspaceSelected_16x9);
  296. } else {
  297. canvas_draw_icon(
  298. canvas,
  299. keyboard_origin_x + keys[column].x,
  300. keyboard_origin_y + keys[column].y,
  301. &I_KeyBackspace_16x9);
  302. }
  303. } else {
  304. if(model->selected_row == row && model->selected_column == column) {
  305. canvas_set_color(canvas, ColorBlack);
  306. canvas_draw_box(
  307. canvas,
  308. keyboard_origin_x + keys[column].x - 1,
  309. keyboard_origin_y + keys[column].y - 8,
  310. 7,
  311. 10);
  312. canvas_set_color(canvas, ColorWhite);
  313. } else {
  314. canvas_set_color(canvas, ColorBlack);
  315. }
  316. if(0 == strcmp(model->header, mode_AT)) {
  317. canvas_draw_glyph(
  318. canvas,
  319. keyboard_origin_x + keys[column].x,
  320. keyboard_origin_y + keys[column].y,
  321. char_to_uppercase(keys[column].text));
  322. } else {
  323. canvas_draw_glyph(
  324. canvas,
  325. keyboard_origin_x + keys[column].x,
  326. keyboard_origin_y + keys[column].y,
  327. keys[column].text);
  328. }
  329. }
  330. }
  331. }
  332. if(model->valadator_message_visible) {
  333. canvas_set_font(canvas, FontSecondary);
  334. canvas_set_color(canvas, ColorWhite);
  335. canvas_draw_box(canvas, 8, 10, 110, 48);
  336. canvas_set_color(canvas, ColorBlack);
  337. canvas_draw_icon(canvas, 10, 14, &I_WarningDolphin_45x42);
  338. canvas_draw_rframe(canvas, 8, 8, 112, 50, 3);
  339. canvas_draw_rframe(canvas, 9, 9, 110, 48, 2);
  340. elements_multiline_text(canvas, 62, 20, furi_string_get_cstr(model->validator_text));
  341. canvas_set_font(canvas, FontKeyboard);
  342. }
  343. }
  344. static void
  345. uart_text_input_handle_up(UART_TextInput* uart_text_input, UART_TextInputModel* model) {
  346. UNUSED(uart_text_input);
  347. if(model->selected_row > 0) {
  348. model->selected_row--;
  349. if(model->selected_column > get_row_size(model->selected_row) - 6) {
  350. model->selected_column = model->selected_column + 1;
  351. }
  352. }
  353. }
  354. static void
  355. uart_text_input_handle_down(UART_TextInput* uart_text_input, UART_TextInputModel* model) {
  356. UNUSED(uart_text_input);
  357. if(model->selected_row < keyboard_row_count - 1) {
  358. model->selected_row++;
  359. if(model->selected_column > get_row_size(model->selected_row) - 4) {
  360. model->selected_column = model->selected_column - 1;
  361. }
  362. }
  363. }
  364. static void
  365. uart_text_input_handle_left(UART_TextInput* uart_text_input, UART_TextInputModel* model) {
  366. UNUSED(uart_text_input);
  367. if(model->selected_column > 0) {
  368. model->selected_column--;
  369. } else {
  370. model->selected_column = get_row_size(model->selected_row) - 1;
  371. }
  372. }
  373. static void
  374. uart_text_input_handle_right(UART_TextInput* uart_text_input, UART_TextInputModel* model) {
  375. UNUSED(uart_text_input);
  376. if(model->selected_column < get_row_size(model->selected_row) - 1) {
  377. model->selected_column++;
  378. } else {
  379. model->selected_column = 0;
  380. }
  381. }
  382. static void uart_text_input_handle_ok(
  383. UART_TextInput* uart_text_input,
  384. UART_TextInputModel* model,
  385. bool shift) {
  386. char selected = get_selected_char(model);
  387. uint8_t text_length = strlen(model->text_buffer);
  388. if(0 == strcmp(model->header, mode_AT)) {
  389. selected = char_to_uppercase(selected);
  390. }
  391. if(shift) {
  392. if(0 == strcmp(model->header, mode_AT)) {
  393. selected = char_to_lowercase(selected);
  394. } else {
  395. selected = char_to_uppercase(selected);
  396. }
  397. }
  398. if(selected == ENTER_KEY) {
  399. if(model->validator_callback &&
  400. (!model->validator_callback(
  401. model->text_buffer, model->validator_text, model->validator_callback_context))) {
  402. model->valadator_message_visible = true;
  403. furi_timer_start(uart_text_input->timer, furi_kernel_get_tick_frequency() * 4);
  404. } else if(model->callback != 0 && text_length > 0) {
  405. model->callback(model->callback_context);
  406. }
  407. } else if(selected == BACKSPACE_KEY) {
  408. uart_text_input_backspace_cb(model);
  409. } else {
  410. if(model->clear_default_text) {
  411. text_length = 0;
  412. }
  413. if(text_length < (model->text_buffer_size - 1)) {
  414. model->text_buffer[text_length] = selected;
  415. model->text_buffer[text_length + 1] = 0;
  416. }
  417. }
  418. model->clear_default_text = false;
  419. }
  420. static bool uart_text_input_view_input_callback(InputEvent* event, void* context) {
  421. UART_TextInput* uart_text_input = context;
  422. furi_assert(uart_text_input);
  423. bool consumed = false;
  424. // Acquire model
  425. UART_TextInputModel* model = view_get_model(uart_text_input->view);
  426. if((!(event->type == InputTypePress) && !(event->type == InputTypeRelease)) &&
  427. model->valadator_message_visible) {
  428. model->valadator_message_visible = false;
  429. consumed = true;
  430. } else if(event->type == InputTypeShort) {
  431. consumed = true;
  432. switch(event->key) {
  433. case InputKeyUp:
  434. uart_text_input_handle_up(uart_text_input, model);
  435. break;
  436. case InputKeyDown:
  437. uart_text_input_handle_down(uart_text_input, model);
  438. break;
  439. case InputKeyLeft:
  440. uart_text_input_handle_left(uart_text_input, model);
  441. break;
  442. case InputKeyRight:
  443. uart_text_input_handle_right(uart_text_input, model);
  444. break;
  445. case InputKeyOk:
  446. uart_text_input_handle_ok(uart_text_input, model, false);
  447. break;
  448. default:
  449. consumed = false;
  450. break;
  451. }
  452. } else if(event->type == InputTypeLong) {
  453. consumed = true;
  454. switch(event->key) {
  455. case InputKeyUp:
  456. uart_text_input_handle_up(uart_text_input, model);
  457. break;
  458. case InputKeyDown:
  459. uart_text_input_handle_down(uart_text_input, model);
  460. break;
  461. case InputKeyLeft:
  462. uart_text_input_handle_left(uart_text_input, model);
  463. break;
  464. case InputKeyRight:
  465. uart_text_input_handle_right(uart_text_input, model);
  466. break;
  467. case InputKeyOk:
  468. uart_text_input_handle_ok(uart_text_input, model, true);
  469. break;
  470. case InputKeyBack:
  471. uart_text_input_backspace_cb(model);
  472. break;
  473. default:
  474. consumed = false;
  475. break;
  476. }
  477. } else if(event->type == InputTypeRepeat) {
  478. consumed = true;
  479. switch(event->key) {
  480. case InputKeyUp:
  481. uart_text_input_handle_up(uart_text_input, model);
  482. break;
  483. case InputKeyDown:
  484. uart_text_input_handle_down(uart_text_input, model);
  485. break;
  486. case InputKeyLeft:
  487. uart_text_input_handle_left(uart_text_input, model);
  488. break;
  489. case InputKeyRight:
  490. uart_text_input_handle_right(uart_text_input, model);
  491. break;
  492. case InputKeyBack:
  493. uart_text_input_backspace_cb(model);
  494. break;
  495. default:
  496. consumed = false;
  497. break;
  498. }
  499. }
  500. // Commit model
  501. view_commit_model(uart_text_input->view, consumed);
  502. return consumed;
  503. }
  504. void uart_text_input_timer_callback(void* context) {
  505. furi_assert(context);
  506. UART_TextInput* uart_text_input = context;
  507. with_view_model(
  508. uart_text_input->view,
  509. UART_TextInputModel * model,
  510. { model->valadator_message_visible = false; },
  511. true);
  512. }
  513. UART_TextInput* uart_text_input_alloc() {
  514. UART_TextInput* uart_text_input = malloc(sizeof(UART_TextInput));
  515. uart_text_input->view = view_alloc();
  516. view_set_context(uart_text_input->view, uart_text_input);
  517. view_allocate_model(uart_text_input->view, ViewModelTypeLocking, sizeof(UART_TextInputModel));
  518. view_set_draw_callback(uart_text_input->view, uart_text_input_view_draw_callback);
  519. view_set_input_callback(uart_text_input->view, uart_text_input_view_input_callback);
  520. uart_text_input->timer =
  521. furi_timer_alloc(uart_text_input_timer_callback, FuriTimerTypeOnce, uart_text_input);
  522. with_view_model(
  523. uart_text_input->view,
  524. UART_TextInputModel * model,
  525. { model->validator_text = furi_string_alloc(); },
  526. false);
  527. uart_text_input_reset(uart_text_input);
  528. return uart_text_input;
  529. }
  530. void uart_text_input_free(UART_TextInput* uart_text_input) {
  531. furi_assert(uart_text_input);
  532. with_view_model(
  533. uart_text_input->view,
  534. UART_TextInputModel * model,
  535. { furi_string_free(model->validator_text); },
  536. false);
  537. // Send stop command
  538. furi_timer_stop(uart_text_input->timer);
  539. // Release allocated memory
  540. furi_timer_free(uart_text_input->timer);
  541. view_free(uart_text_input->view);
  542. free(uart_text_input);
  543. }
  544. void uart_text_input_reset(UART_TextInput* uart_text_input) {
  545. furi_assert(uart_text_input);
  546. with_view_model(
  547. uart_text_input->view,
  548. UART_TextInputModel * model,
  549. {
  550. model->text_buffer_size = 0;
  551. model->header = "";
  552. model->selected_row = 0;
  553. model->selected_column = 0;
  554. model->clear_default_text = false;
  555. model->text_buffer = NULL;
  556. model->text_buffer_size = 0;
  557. model->callback = NULL;
  558. model->callback_context = NULL;
  559. model->validator_callback = NULL;
  560. model->validator_callback_context = NULL;
  561. furi_string_reset(model->validator_text);
  562. model->valadator_message_visible = false;
  563. },
  564. true);
  565. }
  566. View* uart_text_input_get_view(UART_TextInput* uart_text_input) {
  567. furi_assert(uart_text_input);
  568. return uart_text_input->view;
  569. }
  570. void uart_text_input_set_result_callback(
  571. UART_TextInput* uart_text_input,
  572. UART_TextInputCallback callback,
  573. void* callback_context,
  574. char* text_buffer,
  575. size_t text_buffer_size,
  576. bool clear_default_text) {
  577. with_view_model(
  578. uart_text_input->view,
  579. UART_TextInputModel * model,
  580. {
  581. model->callback = callback;
  582. model->callback_context = callback_context;
  583. model->text_buffer = text_buffer;
  584. model->text_buffer_size = text_buffer_size;
  585. model->clear_default_text = clear_default_text;
  586. if(text_buffer && text_buffer[0] != '\0') {
  587. // Set focus on Save
  588. model->selected_row = 2;
  589. model->selected_column = 8;
  590. }
  591. },
  592. true);
  593. }
  594. void uart_text_input_set_validator(
  595. UART_TextInput* uart_text_input,
  596. UART_TextInputValidatorCallback callback,
  597. void* callback_context) {
  598. with_view_model(
  599. uart_text_input->view,
  600. UART_TextInputModel * model,
  601. {
  602. model->validator_callback = callback;
  603. model->validator_callback_context = callback_context;
  604. },
  605. true);
  606. }
  607. UART_TextInputValidatorCallback
  608. uart_text_input_get_validator_callback(UART_TextInput* uart_text_input) {
  609. UART_TextInputValidatorCallback validator_callback = NULL;
  610. with_view_model(
  611. uart_text_input->view,
  612. UART_TextInputModel * model,
  613. { validator_callback = model->validator_callback; },
  614. false);
  615. return validator_callback;
  616. }
  617. void* uart_text_input_get_validator_callback_context(UART_TextInput* uart_text_input) {
  618. void* validator_callback_context = NULL;
  619. with_view_model(
  620. uart_text_input->view,
  621. UART_TextInputModel * model,
  622. { validator_callback_context = model->validator_callback_context; },
  623. false);
  624. return validator_callback_context;
  625. }
  626. void uart_text_input_set_header_text(UART_TextInput* uart_text_input, const char* text) {
  627. with_view_model(
  628. uart_text_input->view, UART_TextInputModel * model, { model->header = text; }, true);
  629. }
  630. #endif // UART_TEXT_INPUT_H