int_input.c 11 KB

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