int_input.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  1. #include "int_input.h"
  2. #include <gui/elements.h>
  3. #include <furi.h>
  4. #include "meal_pager_icons.h"
  5. /** IntInput type */
  6. struct IntInput {
  7. View* view;
  8. };
  9. typedef struct {
  10. const char text;
  11. const uint8_t x;
  12. const uint8_t y;
  13. } IntInputKey;
  14. typedef struct {
  15. const char* header;
  16. char* text_buffer;
  17. size_t text_buffer_size;
  18. bool clear_default_text;
  19. IntInputCallback callback;
  20. void* callback_context;
  21. int8_t selected_row;
  22. uint8_t selected_column;
  23. } IntInputModel;
  24. static const uint8_t keyboard_origin_x = 7;
  25. static const uint8_t keyboard_origin_y = 31;
  26. static const uint8_t keyboard_row_count = 2;
  27. static const uint8_t enter_symbol = '\r';
  28. static const uint8_t backspace_symbol = '\b';
  29. static const IntInputKey keyboard_keys_row_1[] = {
  30. {'0', 0, 12},
  31. {'1', 11, 12},
  32. {'2', 22, 12},
  33. {'3', 33, 12},
  34. {'4', 44, 12},
  35. {backspace_symbol, 103, 4},
  36. };
  37. static const IntInputKey keyboard_keys_row_2[] = {
  38. {'5', 0, 26},
  39. {'6', 11, 26},
  40. {'7', 22, 26},
  41. {'8', 33, 26},
  42. {'9', 44, 26},
  43. {enter_symbol, 95, 17},
  44. };
  45. /** Get row size
  46. *
  47. * @param row_index Index of row
  48. *
  49. * @return uint8_t Row size
  50. */
  51. static uint8_t int_input_get_row_size(uint8_t row_index) {
  52. uint8_t row_size = 0;
  53. switch(row_index + 1) {
  54. case 1:
  55. row_size = COUNT_OF(keyboard_keys_row_1);
  56. break;
  57. case 2:
  58. row_size = COUNT_OF(keyboard_keys_row_2);
  59. break;
  60. default:
  61. furi_crash();
  62. }
  63. return row_size;
  64. }
  65. /** Get row pointer
  66. *
  67. * @param row_index Index of row
  68. *
  69. * @return const IntInputKey* Row pointer
  70. */
  71. static const IntInputKey* int_input_get_row(uint8_t row_index) {
  72. const IntInputKey* row = NULL;
  73. switch(row_index + 1) {
  74. case 1:
  75. row = keyboard_keys_row_1;
  76. break;
  77. case 2:
  78. row = keyboard_keys_row_2;
  79. break;
  80. default:
  81. furi_crash();
  82. }
  83. return row;
  84. }
  85. /** Draw input box (common view)
  86. *
  87. * @param canvas The canvas
  88. * @param model The model
  89. */
  90. static void int_input_draw_input(Canvas* canvas, IntInputModel* model) {
  91. const uint8_t text_x = 8;
  92. const uint8_t text_y = 25;
  93. elements_slightly_rounded_frame(canvas, 6, 14, 116, 15);
  94. const char* text = model->text_buffer;
  95. canvas_draw_str(canvas, text_x, text_y, text);
  96. }
  97. static void int_input_backspace_cb(IntInputModel* model) {
  98. uint8_t text_length = model->clear_default_text ? 1 : strlen(model->text_buffer);
  99. if(text_length > 0) {
  100. model->text_buffer[text_length - 1] = 0;
  101. }
  102. }
  103. /** Handle up button
  104. *
  105. * @param model The model
  106. */
  107. static void int_input_handle_up(IntInputModel* model) {
  108. if(model->selected_row > 0) {
  109. model->selected_row--;
  110. }
  111. }
  112. /** Handle down button
  113. *
  114. * @param model The model
  115. */
  116. static void int_input_handle_down(IntInputModel* model) {
  117. if(model->selected_row < keyboard_row_count - 1) {
  118. model->selected_row += 1;
  119. }
  120. }
  121. /** Handle left button
  122. *
  123. * @param model The model
  124. */
  125. static void int_input_handle_left(IntInputModel* model) {
  126. if(model->selected_column > 0) {
  127. model->selected_column--;
  128. } else {
  129. model->selected_column = int_input_get_row_size(model->selected_row) - 1;
  130. }
  131. }
  132. /** Handle right button
  133. *
  134. * @param model The model
  135. */
  136. static void int_input_handle_right(IntInputModel* model) {
  137. if(model->selected_column < int_input_get_row_size(model->selected_row) - 1) {
  138. model->selected_column++;
  139. } else {
  140. model->selected_column = 0;
  141. }
  142. }
  143. /** Handle OK button
  144. *
  145. * @param model The model
  146. */
  147. static void int_input_handle_ok(IntInputModel* model) {
  148. char selected = int_input_get_row(model->selected_row)[model->selected_column].text;
  149. size_t text_length = strlen(model->text_buffer);
  150. if(selected == enter_symbol) {
  151. model->callback(model->callback_context);
  152. } else if(selected == backspace_symbol) {
  153. int_input_backspace_cb(model);
  154. } else {
  155. if(model->clear_default_text) {
  156. text_length = 0;
  157. }
  158. if(text_length < (model->text_buffer_size - 1)) {
  159. model->text_buffer[text_length] = selected;
  160. model->text_buffer[text_length + 1] = 0;
  161. }
  162. }
  163. model->clear_default_text = false;
  164. }
  165. /** Draw callback
  166. *
  167. * @param canvas The canvas
  168. * @param _model The model
  169. */
  170. static void int_input_view_draw_callback(Canvas* canvas, void* _model) {
  171. IntInputModel* model = _model;
  172. uint8_t text_length = model->text_buffer ? strlen(model->text_buffer) : 0;
  173. UNUSED(text_length);
  174. canvas_clear(canvas);
  175. canvas_set_color(canvas, ColorBlack);
  176. int_input_draw_input(canvas, model);
  177. canvas_set_font(canvas, FontSecondary);
  178. canvas_draw_str(canvas, 2, 9, model->header);
  179. canvas_set_font(canvas, FontKeyboard);
  180. // Draw keyboard
  181. for(uint8_t row = 0; row < keyboard_row_count; row++) {
  182. const uint8_t column_count = int_input_get_row_size(row);
  183. const IntInputKey* keys = int_input_get_row(row);
  184. for(size_t column = 0; column < column_count; column++) {
  185. if(keys[column].text == enter_symbol) {
  186. canvas_set_color(canvas, ColorBlack);
  187. if(model->selected_row == row && model->selected_column == column) {
  188. canvas_draw_icon(
  189. canvas,
  190. keyboard_origin_x + keys[column].x,
  191. keyboard_origin_y + keys[column].y,
  192. &I_KeySaveSelected_24x11);
  193. } else {
  194. canvas_draw_icon(
  195. canvas,
  196. keyboard_origin_x + keys[column].x,
  197. keyboard_origin_y + keys[column].y,
  198. &I_KeySave_24x11);
  199. }
  200. } else if(keys[column].text == backspace_symbol) {
  201. canvas_set_color(canvas, ColorBlack);
  202. if(model->selected_row == row && model->selected_column == column) {
  203. canvas_draw_icon(
  204. canvas,
  205. keyboard_origin_x + keys[column].x,
  206. keyboard_origin_y + keys[column].y,
  207. &I_KeyBackspaceSelected_16x9);
  208. } else {
  209. canvas_draw_icon(
  210. canvas,
  211. keyboard_origin_x + keys[column].x,
  212. keyboard_origin_y + keys[column].y,
  213. &I_KeyBackspace_16x9);
  214. }
  215. } else {
  216. if(model->selected_row == row && model->selected_column == column) {
  217. canvas_set_color(canvas, ColorBlack);
  218. canvas_draw_box(
  219. canvas,
  220. keyboard_origin_x + keys[column].x - 3,
  221. keyboard_origin_y + keys[column].y - 10,
  222. 11,
  223. 13);
  224. canvas_set_color(canvas, ColorWhite);
  225. } else if(model->selected_row == -1 && row == 0 && model->selected_column == column) {
  226. canvas_set_color(canvas, ColorBlack);
  227. canvas_draw_frame(
  228. canvas,
  229. keyboard_origin_x + keys[column].x - 3,
  230. keyboard_origin_y + keys[column].y - 10,
  231. 11,
  232. 13);
  233. } else {
  234. canvas_set_color(canvas, ColorBlack);
  235. }
  236. canvas_draw_glyph(
  237. canvas,
  238. keyboard_origin_x + keys[column].x,
  239. keyboard_origin_y + keys[column].y,
  240. keys[column].text);
  241. }
  242. }
  243. }
  244. }
  245. /** Input callback
  246. *
  247. * @param event The event
  248. * @param context The context
  249. *
  250. * @return true
  251. * @return false
  252. */
  253. static bool int_input_view_input_callback(InputEvent* event, void* context) {
  254. IntInput* int_input = context;
  255. furi_assert(int_input);
  256. bool consumed = false;
  257. // Fetch the model
  258. IntInputModel* model = view_get_model(int_input->view);
  259. if(event->type == InputTypeShort || event->type == InputTypeLong ||
  260. event->type == InputTypeRepeat) {
  261. consumed = true;
  262. switch(event->key) {
  263. case InputKeyLeft:
  264. int_input_handle_left(model);
  265. break;
  266. case InputKeyRight:
  267. int_input_handle_right(model);
  268. break;
  269. case InputKeyUp:
  270. int_input_handle_up(model);
  271. break;
  272. case InputKeyDown:
  273. int_input_handle_down(model);
  274. break;
  275. case InputKeyOk:
  276. int_input_handle_ok(model);
  277. break;
  278. default:
  279. consumed = false;
  280. break;
  281. }
  282. }
  283. // commit view
  284. view_commit_model(int_input->view, consumed);
  285. return consumed;
  286. }
  287. void int_input_reset(IntInput* int_input) {
  288. FURI_LOG_D("INT_INPUT", "Resetting Model");
  289. furi_assert(int_input);
  290. with_view_model(
  291. int_input->view,
  292. IntInputModel * model,
  293. {
  294. model->header = "";
  295. model->selected_row = 0;
  296. model->selected_column = 0;
  297. model->clear_default_text = false;
  298. model->text_buffer = "";
  299. model->text_buffer_size = 0;
  300. model->callback = NULL;
  301. model->callback_context = NULL;
  302. },
  303. true);
  304. }
  305. IntInput* int_input_alloc() {
  306. IntInput* int_input = malloc(sizeof(IntInput));
  307. int_input->view = view_alloc();
  308. view_set_context(int_input->view, int_input);
  309. view_allocate_model(int_input->view, ViewModelTypeLocking, sizeof(IntInputModel));
  310. view_set_draw_callback(int_input->view, int_input_view_draw_callback);
  311. view_set_input_callback(int_input->view, int_input_view_input_callback);
  312. int_input_reset(int_input);
  313. return int_input;
  314. }
  315. void int_input_free(IntInput* int_input) {
  316. furi_assert(int_input);
  317. view_free(int_input->view);
  318. free(int_input);
  319. }
  320. View* int_input_get_view(IntInput* int_input) {
  321. furi_assert(int_input);
  322. return int_input->view;
  323. }
  324. void int_input_set_result_callback(
  325. IntInput* int_input,
  326. IntInputCallback callback,
  327. void* callback_context,
  328. char* text_buffer,
  329. size_t text_buffer_size,
  330. bool clear_default_text) {
  331. with_view_model(
  332. int_input->view,
  333. IntInputModel * model,
  334. {
  335. model->callback = callback;
  336. model->callback_context = callback_context;
  337. model->text_buffer = text_buffer;
  338. model->text_buffer_size = text_buffer_size;
  339. model->clear_default_text = clear_default_text;
  340. },
  341. true);
  342. }
  343. void int_input_set_header_text(IntInput* int_input, const char* text) {
  344. with_view_model(int_input->view, IntInputModel * model, { model->header = text; }, true);
  345. }