uart_hex_input.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519
  1. #include "uart_hex_input.h"
  2. #include "uart_text_input.h"
  3. #include <gui/elements.h>
  4. #include "uart_terminal_icons.h"
  5. #include <assets_icons.h>
  6. #include "uart_terminal_app_i.h"
  7. #include <furi.h>
  8. struct UART_TextInput {
  9. View* view;
  10. FuriTimer* timer;
  11. };
  12. typedef struct {
  13. const char text;
  14. const uint8_t x;
  15. const uint8_t y;
  16. } UART_TextInputKey;
  17. typedef struct {
  18. const char* header;
  19. char* text_buffer;
  20. size_t text_buffer_size;
  21. bool clear_default_text;
  22. UART_TextInputCallback callback;
  23. void* callback_context;
  24. uint8_t selected_row;
  25. uint8_t selected_column;
  26. UART_TextInputValidatorCallback validator_callback;
  27. void* validator_callback_context;
  28. FuriString* validator_text;
  29. bool valadator_message_visible;
  30. } UART_TextInputModel;
  31. static const uint8_t keyboard_origin_x = 5;
  32. static const uint8_t keyboard_origin_y = 28;
  33. static const uint8_t keyboard_row_count = 2;
  34. #define ENTER_KEY '\r'
  35. #define BACKSPACE_KEY '\b'
  36. static const UART_TextInputKey keyboard_keys_row_1[] = {
  37. {'0', 0, 12},
  38. {'1', 11, 12},
  39. {'2', 22, 12},
  40. {'3', 33, 12},
  41. {'4', 44, 12},
  42. {'5', 55, 12},
  43. {'6', 66, 12},
  44. {'7', 77, 12},
  45. {BACKSPACE_KEY, 103, 4},
  46. };
  47. static const UART_TextInputKey keyboard_keys_row_2[] = {
  48. {'8', 0, 26},
  49. {'9', 11, 26},
  50. {'A', 22, 26},
  51. {'B', 33, 26},
  52. {'C', 44, 26},
  53. {'D', 55, 26},
  54. {'E', 66, 26},
  55. {'F', 77, 26},
  56. {ENTER_KEY, 95, 17},
  57. };
  58. static uint8_t get_row_size(uint8_t row_index) {
  59. uint8_t row_size = 0;
  60. switch(row_index + 1) {
  61. case 1:
  62. row_size = sizeof(keyboard_keys_row_1) / sizeof(UART_TextInputKey);
  63. break;
  64. case 2:
  65. row_size = sizeof(keyboard_keys_row_2) / sizeof(UART_TextInputKey);
  66. break;
  67. }
  68. return row_size;
  69. }
  70. static const UART_TextInputKey* get_row(uint8_t row_index) {
  71. const UART_TextInputKey* row = NULL;
  72. switch(row_index + 1) {
  73. case 1:
  74. row = keyboard_keys_row_1;
  75. break;
  76. case 2:
  77. row = keyboard_keys_row_2;
  78. break;
  79. }
  80. return row;
  81. }
  82. static char get_selected_char(UART_TextInputModel* model) {
  83. return get_row(model->selected_row)[model->selected_column].text;
  84. }
  85. static void uart_hex_input_backspace_cb(UART_TextInputModel* model) {
  86. uint8_t text_length = model->clear_default_text ? 1 : strlen(model->text_buffer);
  87. if(text_length > 0) {
  88. model->text_buffer[text_length - 1] = 0;
  89. }
  90. }
  91. static void uart_hex_input_view_draw_callback(Canvas* canvas, void* _model) {
  92. UART_TextInputModel* model = _model;
  93. uint8_t needed_string_width = canvas_width(canvas) - 8;
  94. uint8_t start_pos = 4;
  95. const char* text = model->text_buffer;
  96. canvas_clear(canvas);
  97. canvas_set_color(canvas, ColorBlack);
  98. canvas_draw_str(canvas, 2, 7, model->header);
  99. elements_slightly_rounded_frame(canvas, 1, 8, 126, 12);
  100. if(canvas_string_width(canvas, text) > needed_string_width) {
  101. canvas_draw_str(canvas, start_pos, 17, "...");
  102. start_pos += 6;
  103. needed_string_width -= 8;
  104. }
  105. while(text != 0 && canvas_string_width(canvas, text) > needed_string_width) {
  106. text++;
  107. }
  108. if(model->clear_default_text) {
  109. elements_slightly_rounded_box(
  110. canvas, start_pos - 1, 14, canvas_string_width(canvas, text) + 2, 10);
  111. canvas_set_color(canvas, ColorWhite);
  112. } else {
  113. canvas_draw_str(canvas, start_pos + canvas_string_width(canvas, text) + 1, 18, "|");
  114. canvas_draw_str(canvas, start_pos + canvas_string_width(canvas, text) + 2, 18, "|");
  115. }
  116. canvas_draw_str(canvas, start_pos, 17, text);
  117. canvas_set_font(canvas, FontKeyboard);
  118. for(uint8_t row = 0; row <= keyboard_row_count; row++) {
  119. const uint8_t column_count = get_row_size(row);
  120. const UART_TextInputKey* keys = get_row(row);
  121. for(size_t column = 0; column < column_count; column++) {
  122. if(keys[column].text == ENTER_KEY) {
  123. canvas_set_color(canvas, ColorBlack);
  124. if(model->selected_row == row && model->selected_column == column) {
  125. canvas_draw_icon(
  126. canvas,
  127. keyboard_origin_x + keys[column].x,
  128. keyboard_origin_y + keys[column].y,
  129. &I_KeySaveSelected_24x11);
  130. } else {
  131. canvas_draw_icon(
  132. canvas,
  133. keyboard_origin_x + keys[column].x,
  134. keyboard_origin_y + keys[column].y,
  135. &I_KeySave_24x11);
  136. }
  137. } else if(keys[column].text == BACKSPACE_KEY) {
  138. canvas_set_color(canvas, ColorBlack);
  139. if(model->selected_row == row && model->selected_column == column) {
  140. canvas_draw_icon(
  141. canvas,
  142. keyboard_origin_x + keys[column].x,
  143. keyboard_origin_y + keys[column].y,
  144. &I_KeyBackspaceSelected_16x9);
  145. } else {
  146. canvas_draw_icon(
  147. canvas,
  148. keyboard_origin_x + keys[column].x,
  149. keyboard_origin_y + keys[column].y,
  150. &I_KeyBackspace_16x9);
  151. }
  152. } else {
  153. if(model->selected_row == row && model->selected_column == column) {
  154. canvas_set_color(canvas, ColorBlack);
  155. canvas_draw_box(
  156. canvas,
  157. keyboard_origin_x + keys[column].x - 1,
  158. keyboard_origin_y + keys[column].y - 8,
  159. 7,
  160. 10);
  161. canvas_set_color(canvas, ColorWhite);
  162. } else {
  163. canvas_set_color(canvas, ColorBlack);
  164. }
  165. canvas_draw_glyph(
  166. canvas,
  167. keyboard_origin_x + keys[column].x,
  168. keyboard_origin_y + keys[column].y,
  169. keys[column].text);
  170. }
  171. }
  172. }
  173. if(model->valadator_message_visible) {
  174. canvas_set_font(canvas, FontSecondary);
  175. canvas_set_color(canvas, ColorWhite);
  176. canvas_draw_box(canvas, 8, 10, 110, 48);
  177. canvas_set_color(canvas, ColorBlack);
  178. canvas_draw_icon(canvas, 10, 14, &I_WarningDolphin_45x42);
  179. canvas_draw_rframe(canvas, 8, 8, 112, 50, 3);
  180. canvas_draw_rframe(canvas, 9, 9, 110, 48, 2);
  181. elements_multiline_text(canvas, 62, 20, furi_string_get_cstr(model->validator_text));
  182. canvas_set_font(canvas, FontKeyboard);
  183. }
  184. }
  185. static void uart_hex_input_handle_up(UART_TextInput* uart_text_input, UART_TextInputModel* model) {
  186. UNUSED(uart_text_input);
  187. if(model->selected_row > 0) {
  188. model->selected_row--;
  189. if(model->selected_column > get_row_size(model->selected_row) - 6) {
  190. model->selected_column = model->selected_column + 1;
  191. }
  192. }
  193. }
  194. static void
  195. uart_hex_input_handle_down(UART_TextInput* uart_text_input, UART_TextInputModel* model) {
  196. UNUSED(uart_text_input);
  197. if(model->selected_row < keyboard_row_count - 1) {
  198. model->selected_row++;
  199. if(model->selected_column > get_row_size(model->selected_row) - 4) {
  200. model->selected_column = model->selected_column - 1;
  201. }
  202. }
  203. }
  204. static void
  205. uart_hex_input_handle_left(UART_TextInput* uart_text_input, UART_TextInputModel* model) {
  206. UNUSED(uart_text_input);
  207. if(model->selected_column > 0) {
  208. model->selected_column--;
  209. } else {
  210. model->selected_column = get_row_size(model->selected_row) - 1;
  211. }
  212. }
  213. static void
  214. uart_hex_input_handle_right(UART_TextInput* uart_text_input, UART_TextInputModel* model) {
  215. UNUSED(uart_text_input);
  216. if(model->selected_column < get_row_size(model->selected_row) - 1) {
  217. model->selected_column++;
  218. } else {
  219. model->selected_column = 0;
  220. }
  221. }
  222. static void uart_hex_input_handle_ok(
  223. UART_TextInput* uart_text_input,
  224. UART_TextInputModel* model,
  225. bool shift) {
  226. UNUSED(shift);
  227. char selected = get_selected_char(model);
  228. uint8_t text_length = strlen(model->text_buffer);
  229. if(selected == ENTER_KEY) {
  230. if(model->validator_callback &&
  231. (!model->validator_callback(
  232. model->text_buffer, model->validator_text, model->validator_callback_context))) {
  233. model->valadator_message_visible = true;
  234. furi_timer_start(uart_text_input->timer, furi_kernel_get_tick_frequency() * 4);
  235. } else if(model->callback != 0 && text_length > 0) {
  236. model->callback(model->callback_context);
  237. }
  238. } else if(selected == BACKSPACE_KEY) {
  239. uart_hex_input_backspace_cb(model);
  240. } else {
  241. if(model->clear_default_text) {
  242. text_length = 0;
  243. }
  244. if(text_length < (model->text_buffer_size - 1)) {
  245. model->text_buffer[text_length] = selected;
  246. model->text_buffer[text_length + 1] = 0;
  247. }
  248. }
  249. model->clear_default_text = false;
  250. }
  251. static bool uart_hex_input_view_input_callback(InputEvent* event, void* context) {
  252. UART_TextInput* uart_text_input = context;
  253. furi_assert(uart_text_input);
  254. bool consumed = false;
  255. // Acquire model
  256. UART_TextInputModel* model = view_get_model(uart_text_input->view);
  257. if((!(event->type == InputTypePress) && !(event->type == InputTypeRelease)) &&
  258. model->valadator_message_visible) {
  259. model->valadator_message_visible = false;
  260. consumed = true;
  261. } else if(event->type == InputTypeShort) {
  262. consumed = true;
  263. switch(event->key) {
  264. case InputKeyUp:
  265. uart_hex_input_handle_up(uart_text_input, model);
  266. break;
  267. case InputKeyDown:
  268. uart_hex_input_handle_down(uart_text_input, model);
  269. break;
  270. case InputKeyLeft:
  271. uart_hex_input_handle_left(uart_text_input, model);
  272. break;
  273. case InputKeyRight:
  274. uart_hex_input_handle_right(uart_text_input, model);
  275. break;
  276. case InputKeyOk:
  277. uart_hex_input_handle_ok(uart_text_input, model, false);
  278. break;
  279. default:
  280. consumed = false;
  281. break;
  282. }
  283. } else if(event->type == InputTypeLong) {
  284. consumed = true;
  285. switch(event->key) {
  286. case InputKeyUp:
  287. uart_hex_input_handle_up(uart_text_input, model);
  288. break;
  289. case InputKeyDown:
  290. uart_hex_input_handle_down(uart_text_input, model);
  291. break;
  292. case InputKeyLeft:
  293. uart_hex_input_handle_left(uart_text_input, model);
  294. break;
  295. case InputKeyRight:
  296. uart_hex_input_handle_right(uart_text_input, model);
  297. break;
  298. case InputKeyOk:
  299. uart_hex_input_handle_ok(uart_text_input, model, true);
  300. break;
  301. case InputKeyBack:
  302. uart_hex_input_backspace_cb(model);
  303. break;
  304. default:
  305. consumed = false;
  306. break;
  307. }
  308. } else if(event->type == InputTypeRepeat) {
  309. consumed = true;
  310. switch(event->key) {
  311. case InputKeyUp:
  312. uart_hex_input_handle_up(uart_text_input, model);
  313. break;
  314. case InputKeyDown:
  315. uart_hex_input_handle_down(uart_text_input, model);
  316. break;
  317. case InputKeyLeft:
  318. uart_hex_input_handle_left(uart_text_input, model);
  319. break;
  320. case InputKeyRight:
  321. uart_hex_input_handle_right(uart_text_input, model);
  322. break;
  323. case InputKeyBack:
  324. uart_hex_input_backspace_cb(model);
  325. break;
  326. default:
  327. consumed = false;
  328. break;
  329. }
  330. }
  331. // Commit model
  332. view_commit_model(uart_text_input->view, consumed);
  333. return consumed;
  334. }
  335. void uart_hex_input_timer_callback(void* context) {
  336. furi_assert(context);
  337. UART_TextInput* uart_text_input = context;
  338. with_view_model(
  339. uart_text_input->view,
  340. UART_TextInputModel * model,
  341. { model->valadator_message_visible = false; },
  342. true);
  343. }
  344. UART_TextInput* uart_hex_input_alloc() {
  345. UART_TextInput* uart_text_input = malloc(sizeof(UART_TextInput));
  346. uart_text_input->view = view_alloc();
  347. view_set_context(uart_text_input->view, uart_text_input);
  348. view_allocate_model(uart_text_input->view, ViewModelTypeLocking, sizeof(UART_TextInputModel));
  349. view_set_draw_callback(uart_text_input->view, uart_hex_input_view_draw_callback);
  350. view_set_input_callback(uart_text_input->view, uart_hex_input_view_input_callback);
  351. uart_text_input->timer =
  352. furi_timer_alloc(uart_hex_input_timer_callback, FuriTimerTypeOnce, uart_text_input);
  353. with_view_model(
  354. uart_text_input->view,
  355. UART_TextInputModel * model,
  356. { model->validator_text = furi_string_alloc(); },
  357. false);
  358. uart_text_input_reset(uart_text_input);
  359. return uart_text_input;
  360. }
  361. void uart_hex_input_free(UART_TextInput* uart_text_input) {
  362. furi_assert(uart_text_input);
  363. with_view_model(
  364. uart_text_input->view,
  365. UART_TextInputModel * model,
  366. { furi_string_free(model->validator_text); },
  367. false);
  368. // Send stop command
  369. furi_timer_stop(uart_text_input->timer);
  370. // Release allocated memory
  371. furi_timer_free(uart_text_input->timer);
  372. view_free(uart_text_input->view);
  373. free(uart_text_input);
  374. }
  375. void uart_hex_input_reset(UART_TextInput* uart_text_input) {
  376. furi_assert(uart_text_input);
  377. with_view_model(
  378. uart_text_input->view,
  379. UART_TextInputModel * model,
  380. {
  381. model->text_buffer_size = 0;
  382. model->header = "";
  383. model->selected_row = 0;
  384. model->selected_column = 0;
  385. model->clear_default_text = false;
  386. model->text_buffer = NULL;
  387. model->text_buffer_size = 0;
  388. model->callback = NULL;
  389. model->callback_context = NULL;
  390. model->validator_callback = NULL;
  391. model->validator_callback_context = NULL;
  392. furi_string_reset(model->validator_text);
  393. model->valadator_message_visible = false;
  394. },
  395. true);
  396. }
  397. View* uart_hex_input_get_view(UART_TextInput* uart_text_input) {
  398. furi_assert(uart_text_input);
  399. return uart_text_input->view;
  400. }
  401. void uart_hex_input_set_result_callback(
  402. UART_TextInput* uart_text_input,
  403. UART_TextInputCallback callback,
  404. void* callback_context,
  405. char* text_buffer,
  406. size_t text_buffer_size,
  407. bool clear_default_text) {
  408. with_view_model(
  409. uart_text_input->view,
  410. UART_TextInputModel * model,
  411. {
  412. model->callback = callback;
  413. model->callback_context = callback_context;
  414. model->text_buffer = text_buffer;
  415. model->text_buffer_size = text_buffer_size;
  416. model->clear_default_text = clear_default_text;
  417. if(text_buffer && text_buffer[0] != '\0') {
  418. // Set focus on Save
  419. model->selected_row = 1;
  420. model->selected_column = 8;
  421. }
  422. },
  423. true);
  424. }
  425. void uart_hex_input_set_validator(
  426. UART_TextInput* uart_text_input,
  427. UART_TextInputValidatorCallback callback,
  428. void* callback_context) {
  429. with_view_model(
  430. uart_text_input->view,
  431. UART_TextInputModel * model,
  432. {
  433. model->validator_callback = callback;
  434. model->validator_callback_context = callback_context;
  435. },
  436. true);
  437. }
  438. UART_TextInputValidatorCallback
  439. uart_hex_input_get_validator_callback(UART_TextInput* uart_text_input) {
  440. UART_TextInputValidatorCallback validator_callback = NULL;
  441. with_view_model(
  442. uart_text_input->view,
  443. UART_TextInputModel * model,
  444. { validator_callback = model->validator_callback; },
  445. false);
  446. return validator_callback;
  447. }
  448. void* uart_hex_input_get_validator_callback_context(UART_TextInput* uart_text_input) {
  449. void* validator_callback_context = NULL;
  450. with_view_model(
  451. uart_text_input->view,
  452. UART_TextInputModel * model,
  453. { validator_callback_context = model->validator_callback_context; },
  454. false);
  455. return validator_callback_context;
  456. }
  457. void uart_hex_input_set_header_text(UART_TextInput* uart_text_input, const char* text) {
  458. with_view_model(
  459. uart_text_input->view, UART_TextInputModel * model, { model->header = text; }, true);
  460. }