variable_item_list.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413
  1. #include "variable_item_list.h"
  2. #include "gui/canvas.h"
  3. #include <m-array.h>
  4. #include <furi.h>
  5. #include <gui/elements.h>
  6. #include <stdint.h>
  7. struct VariableItem {
  8. const char* label;
  9. uint8_t current_value_index;
  10. FuriString* current_value_text;
  11. uint8_t values_count;
  12. VariableItemChangeCallback change_callback;
  13. void* context;
  14. };
  15. ARRAY_DEF(VariableItemArray, VariableItem, M_POD_OPLIST);
  16. struct VariableItemList {
  17. View* view;
  18. VariableItemListEnterCallback callback;
  19. void* context;
  20. };
  21. typedef struct {
  22. VariableItemArray_t items;
  23. uint8_t position;
  24. uint8_t window_position;
  25. } VariableItemListModel;
  26. static void variable_item_list_process_up(VariableItemList* variable_item_list);
  27. static void variable_item_list_process_down(VariableItemList* variable_item_list);
  28. static void variable_item_list_process_left(VariableItemList* variable_item_list);
  29. static void variable_item_list_process_right(VariableItemList* variable_item_list);
  30. static void variable_item_list_process_ok(VariableItemList* variable_item_list);
  31. static void variable_item_list_draw_callback(Canvas* canvas, void* _model) {
  32. VariableItemListModel* model = _model;
  33. const uint8_t item_height = 16;
  34. const uint8_t item_width = 123;
  35. canvas_clear(canvas);
  36. uint8_t position = 0;
  37. VariableItemArray_it_t it;
  38. canvas_set_font(canvas, FontSecondary);
  39. for(VariableItemArray_it(it, model->items); !VariableItemArray_end_p(it);
  40. VariableItemArray_next(it)) {
  41. uint8_t item_position = position - model->window_position;
  42. uint8_t items_on_screen = 4;
  43. uint8_t y_offset = 0;
  44. if(item_position < items_on_screen) {
  45. const VariableItem* item = VariableItemArray_cref(it);
  46. uint8_t item_y = y_offset + (item_position * item_height);
  47. uint8_t item_text_y = item_y + item_height - 4;
  48. if(position == model->position) {
  49. canvas_set_color(canvas, ColorBlack);
  50. elements_slightly_rounded_box(canvas, 0, item_y + 1, item_width, item_height - 2);
  51. canvas_set_color(canvas, ColorWhite);
  52. } else {
  53. canvas_set_color(canvas, ColorBlack);
  54. }
  55. canvas_draw_str(canvas, 6, item_text_y, item->label);
  56. if(item->current_value_index > 0) {
  57. canvas_draw_str(canvas, 73, item_text_y, "<");
  58. }
  59. canvas_draw_str_aligned(
  60. canvas,
  61. (115 + 73) / 2 + 1,
  62. item_text_y,
  63. AlignCenter,
  64. AlignBottom,
  65. furi_string_get_cstr(item->current_value_text));
  66. if(item->current_value_index < (item->values_count - 1)) {
  67. canvas_draw_str(canvas, 115, item_text_y, ">");
  68. }
  69. }
  70. position++;
  71. }
  72. elements_scrollbar(canvas, model->position, VariableItemArray_size(model->items));
  73. }
  74. void variable_item_list_set_selected_item(VariableItemList* variable_item_list, uint8_t index) {
  75. with_view_model(
  76. variable_item_list->view,
  77. VariableItemListModel * model,
  78. {
  79. uint8_t position = index;
  80. if(position >= VariableItemArray_size(model->items)) {
  81. position = 0;
  82. }
  83. model->position = position;
  84. model->window_position = position;
  85. if(model->window_position > 0) {
  86. model->window_position -= 1;
  87. }
  88. if(VariableItemArray_size(model->items) <= 4) {
  89. model->window_position = 0;
  90. } else {
  91. if(model->window_position >= (VariableItemArray_size(model->items) - 4)) {
  92. model->window_position = (VariableItemArray_size(model->items) - 4);
  93. }
  94. }
  95. },
  96. true);
  97. }
  98. uint8_t variable_item_list_get_selected_item_index(VariableItemList* variable_item_list) {
  99. VariableItemListModel* model = view_get_model(variable_item_list->view);
  100. uint8_t idx = model->position;
  101. view_commit_model(variable_item_list->view, false);
  102. return idx;
  103. }
  104. static bool variable_item_list_input_callback(InputEvent* event, void* context) {
  105. VariableItemList* variable_item_list = context;
  106. furi_assert(variable_item_list);
  107. bool consumed = false;
  108. if(event->type == InputTypeShort) {
  109. switch(event->key) {
  110. case InputKeyUp:
  111. consumed = true;
  112. variable_item_list_process_up(variable_item_list);
  113. break;
  114. case InputKeyDown:
  115. consumed = true;
  116. variable_item_list_process_down(variable_item_list);
  117. break;
  118. case InputKeyLeft:
  119. consumed = true;
  120. variable_item_list_process_left(variable_item_list);
  121. break;
  122. case InputKeyRight:
  123. consumed = true;
  124. variable_item_list_process_right(variable_item_list);
  125. break;
  126. case InputKeyOk:
  127. variable_item_list_process_ok(variable_item_list);
  128. break;
  129. default:
  130. break;
  131. }
  132. } else if(event->type == InputTypeRepeat) {
  133. switch(event->key) {
  134. case InputKeyUp:
  135. consumed = true;
  136. variable_item_list_process_up(variable_item_list);
  137. break;
  138. case InputKeyDown:
  139. consumed = true;
  140. variable_item_list_process_down(variable_item_list);
  141. break;
  142. case InputKeyLeft:
  143. consumed = true;
  144. variable_item_list_process_left(variable_item_list);
  145. break;
  146. case InputKeyRight:
  147. consumed = true;
  148. variable_item_list_process_right(variable_item_list);
  149. break;
  150. default:
  151. break;
  152. }
  153. }
  154. return consumed;
  155. }
  156. void variable_item_list_process_up(VariableItemList* variable_item_list) {
  157. with_view_model(
  158. variable_item_list->view,
  159. VariableItemListModel * model,
  160. {
  161. uint8_t items_on_screen = 4;
  162. if(model->position > 0) {
  163. model->position--;
  164. if(((model->position - model->window_position) < 1) &&
  165. model->window_position > 0) {
  166. model->window_position--;
  167. }
  168. } else {
  169. model->position = VariableItemArray_size(model->items) - 1;
  170. if(model->position > (items_on_screen - 1)) {
  171. model->window_position = model->position - (items_on_screen - 1);
  172. }
  173. }
  174. },
  175. true);
  176. }
  177. void variable_item_list_process_down(VariableItemList* variable_item_list) {
  178. with_view_model(
  179. variable_item_list->view,
  180. VariableItemListModel * model,
  181. {
  182. uint8_t items_on_screen = 4;
  183. if(model->position < (VariableItemArray_size(model->items) - 1)) {
  184. model->position++;
  185. if((model->position - model->window_position) > (items_on_screen - 2) &&
  186. model->window_position <
  187. (VariableItemArray_size(model->items) - items_on_screen)) {
  188. model->window_position++;
  189. }
  190. } else {
  191. model->position = 0;
  192. model->window_position = 0;
  193. }
  194. },
  195. true);
  196. }
  197. VariableItem* variable_item_list_get_selected_item(VariableItemListModel* model) {
  198. VariableItem* item = NULL;
  199. VariableItemArray_it_t it;
  200. uint8_t position = 0;
  201. for(VariableItemArray_it(it, model->items); !VariableItemArray_end_p(it);
  202. VariableItemArray_next(it)) {
  203. if(position == model->position) {
  204. break;
  205. }
  206. position++;
  207. }
  208. item = VariableItemArray_ref(it);
  209. furi_assert(item);
  210. return item;
  211. }
  212. void variable_item_list_process_left(VariableItemList* variable_item_list) {
  213. with_view_model(
  214. variable_item_list->view,
  215. VariableItemListModel * model,
  216. {
  217. VariableItem* item = variable_item_list_get_selected_item(model);
  218. if(item->current_value_index > 0) {
  219. item->current_value_index--;
  220. if(item->change_callback) {
  221. item->change_callback(item);
  222. }
  223. }
  224. },
  225. true);
  226. }
  227. void variable_item_list_process_right(VariableItemList* variable_item_list) {
  228. with_view_model(
  229. variable_item_list->view,
  230. VariableItemListModel * model,
  231. {
  232. VariableItem* item = variable_item_list_get_selected_item(model);
  233. if(item->current_value_index < (item->values_count - 1)) {
  234. item->current_value_index++;
  235. if(item->change_callback) {
  236. item->change_callback(item);
  237. }
  238. }
  239. },
  240. true);
  241. }
  242. void variable_item_list_process_ok(VariableItemList* variable_item_list) {
  243. with_view_model(
  244. variable_item_list->view,
  245. VariableItemListModel * model,
  246. {
  247. if(variable_item_list->callback) {
  248. variable_item_list->callback(variable_item_list->context, model->position);
  249. }
  250. },
  251. false);
  252. }
  253. VariableItemList* variable_item_list_alloc() {
  254. VariableItemList* variable_item_list = malloc(sizeof(VariableItemList));
  255. variable_item_list->view = view_alloc();
  256. view_set_context(variable_item_list->view, variable_item_list);
  257. view_allocate_model(
  258. variable_item_list->view, ViewModelTypeLocking, sizeof(VariableItemListModel));
  259. view_set_draw_callback(variable_item_list->view, variable_item_list_draw_callback);
  260. view_set_input_callback(variable_item_list->view, variable_item_list_input_callback);
  261. with_view_model(
  262. variable_item_list->view,
  263. VariableItemListModel * model,
  264. {
  265. VariableItemArray_init(model->items);
  266. model->position = 0;
  267. model->window_position = 0;
  268. },
  269. true);
  270. return variable_item_list;
  271. }
  272. void variable_item_list_free(VariableItemList* variable_item_list) {
  273. furi_assert(variable_item_list);
  274. with_view_model(
  275. variable_item_list->view,
  276. VariableItemListModel * model,
  277. {
  278. VariableItemArray_it_t it;
  279. for(VariableItemArray_it(it, model->items); !VariableItemArray_end_p(it);
  280. VariableItemArray_next(it)) {
  281. furi_string_free(VariableItemArray_ref(it)->current_value_text);
  282. }
  283. VariableItemArray_clear(model->items);
  284. },
  285. false);
  286. view_free(variable_item_list->view);
  287. free(variable_item_list);
  288. }
  289. void variable_item_list_reset(VariableItemList* variable_item_list) {
  290. furi_assert(variable_item_list);
  291. with_view_model(
  292. variable_item_list->view,
  293. VariableItemListModel * model,
  294. {
  295. VariableItemArray_it_t it;
  296. for(VariableItemArray_it(it, model->items); !VariableItemArray_end_p(it);
  297. VariableItemArray_next(it)) {
  298. furi_string_free(VariableItemArray_ref(it)->current_value_text);
  299. }
  300. VariableItemArray_reset(model->items);
  301. },
  302. false);
  303. }
  304. View* variable_item_list_get_view(VariableItemList* variable_item_list) {
  305. furi_assert(variable_item_list);
  306. return variable_item_list->view;
  307. }
  308. VariableItem* variable_item_list_add(
  309. VariableItemList* variable_item_list,
  310. const char* label,
  311. uint8_t values_count,
  312. VariableItemChangeCallback change_callback,
  313. void* context) {
  314. VariableItem* item = NULL;
  315. furi_assert(label);
  316. furi_assert(variable_item_list);
  317. with_view_model(
  318. variable_item_list->view,
  319. VariableItemListModel * model,
  320. {
  321. item = VariableItemArray_push_new(model->items);
  322. item->label = label;
  323. item->values_count = values_count;
  324. item->change_callback = change_callback;
  325. item->context = context;
  326. item->current_value_index = 0;
  327. item->current_value_text = furi_string_alloc();
  328. },
  329. true);
  330. return item;
  331. }
  332. void variable_item_list_set_enter_callback(
  333. VariableItemList* variable_item_list,
  334. VariableItemListEnterCallback callback,
  335. void* context) {
  336. furi_assert(callback);
  337. with_view_model(
  338. variable_item_list->view,
  339. VariableItemListModel * model,
  340. {
  341. UNUSED(model);
  342. variable_item_list->callback = callback;
  343. variable_item_list->context = context;
  344. },
  345. false);
  346. }
  347. void variable_item_set_current_value_index(VariableItem* item, uint8_t current_value_index) {
  348. item->current_value_index = current_value_index;
  349. }
  350. void variable_item_set_values_count(VariableItem* item, uint8_t values_count) {
  351. item->values_count = values_count;
  352. }
  353. void variable_item_set_current_value_text(VariableItem* item, const char* current_value_text) {
  354. furi_string_set(item->current_value_text, current_value_text);
  355. }
  356. uint8_t variable_item_get_current_value_index(VariableItem* item) {
  357. return item->current_value_index;
  358. }
  359. void* variable_item_get_context(VariableItem* item) {
  360. return item->context;
  361. }