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) && (model->window_position > 0)) {
  165. model->window_position--;
  166. }
  167. } else {
  168. model->position = VariableItemArray_size(model->items) - 1;
  169. if(model->position > (items_on_screen - 1)) {
  170. model->window_position = model->position - (items_on_screen - 1);
  171. }
  172. }
  173. },
  174. true);
  175. }
  176. void variable_item_list_process_down(VariableItemList* variable_item_list) {
  177. with_view_model(
  178. variable_item_list->view,
  179. VariableItemListModel * model,
  180. {
  181. uint8_t items_on_screen = 4;
  182. if(model->position < (VariableItemArray_size(model->items) - 1)) {
  183. model->position++;
  184. if((model->position - model->window_position) > (items_on_screen - 2) &&
  185. model->window_position <
  186. (VariableItemArray_size(model->items) - items_on_screen)) {
  187. model->window_position++;
  188. }
  189. } else {
  190. model->position = 0;
  191. model->window_position = 0;
  192. }
  193. },
  194. true);
  195. }
  196. VariableItem* variable_item_list_get_selected_item(VariableItemListModel* model) {
  197. VariableItem* item = NULL;
  198. VariableItemArray_it_t it;
  199. uint8_t position = 0;
  200. for(VariableItemArray_it(it, model->items); !VariableItemArray_end_p(it);
  201. VariableItemArray_next(it)) {
  202. if(position == model->position) {
  203. break;
  204. }
  205. position++;
  206. }
  207. item = VariableItemArray_ref(it);
  208. furi_assert(item);
  209. return item;
  210. }
  211. void variable_item_list_process_left(VariableItemList* variable_item_list) {
  212. with_view_model(
  213. variable_item_list->view,
  214. VariableItemListModel * model,
  215. {
  216. VariableItem* item = variable_item_list_get_selected_item(model);
  217. if(item->current_value_index > 0) {
  218. item->current_value_index--;
  219. if(item->change_callback) {
  220. item->change_callback(item);
  221. }
  222. }
  223. },
  224. true);
  225. }
  226. void variable_item_list_process_right(VariableItemList* variable_item_list) {
  227. with_view_model(
  228. variable_item_list->view,
  229. VariableItemListModel * model,
  230. {
  231. VariableItem* item = variable_item_list_get_selected_item(model);
  232. if(item->current_value_index < (item->values_count - 1)) {
  233. item->current_value_index++;
  234. if(item->change_callback) {
  235. item->change_callback(item);
  236. }
  237. }
  238. },
  239. true);
  240. }
  241. void variable_item_list_process_ok(VariableItemList* variable_item_list) {
  242. with_view_model(
  243. variable_item_list->view,
  244. VariableItemListModel * model,
  245. {
  246. if(variable_item_list->callback) {
  247. variable_item_list->callback(variable_item_list->context, model->position);
  248. }
  249. },
  250. false);
  251. }
  252. VariableItemList* variable_item_list_alloc() {
  253. VariableItemList* variable_item_list = malloc(sizeof(VariableItemList));
  254. variable_item_list->view = view_alloc();
  255. view_set_context(variable_item_list->view, variable_item_list);
  256. view_allocate_model(
  257. variable_item_list->view, ViewModelTypeLocking, sizeof(VariableItemListModel));
  258. view_set_draw_callback(variable_item_list->view, variable_item_list_draw_callback);
  259. view_set_input_callback(variable_item_list->view, variable_item_list_input_callback);
  260. with_view_model(
  261. variable_item_list->view,
  262. VariableItemListModel * model,
  263. {
  264. VariableItemArray_init(model->items);
  265. model->position = 0;
  266. model->window_position = 0;
  267. },
  268. true);
  269. return variable_item_list;
  270. }
  271. void variable_item_list_free(VariableItemList* variable_item_list) {
  272. furi_assert(variable_item_list);
  273. with_view_model(
  274. variable_item_list->view,
  275. VariableItemListModel * model,
  276. {
  277. VariableItemArray_it_t it;
  278. for(VariableItemArray_it(it, model->items); !VariableItemArray_end_p(it);
  279. VariableItemArray_next(it)) {
  280. furi_string_free(VariableItemArray_ref(it)->current_value_text);
  281. }
  282. VariableItemArray_clear(model->items);
  283. },
  284. false);
  285. view_free(variable_item_list->view);
  286. free(variable_item_list);
  287. }
  288. void variable_item_list_reset(VariableItemList* variable_item_list) {
  289. furi_assert(variable_item_list);
  290. with_view_model(
  291. variable_item_list->view,
  292. VariableItemListModel * model,
  293. {
  294. VariableItemArray_it_t it;
  295. for(VariableItemArray_it(it, model->items); !VariableItemArray_end_p(it);
  296. VariableItemArray_next(it)) {
  297. furi_string_free(VariableItemArray_ref(it)->current_value_text);
  298. }
  299. VariableItemArray_reset(model->items);
  300. },
  301. false);
  302. }
  303. View* variable_item_list_get_view(VariableItemList* variable_item_list) {
  304. furi_assert(variable_item_list);
  305. return variable_item_list->view;
  306. }
  307. VariableItem* variable_item_list_add(
  308. VariableItemList* variable_item_list,
  309. const char* label,
  310. uint8_t values_count,
  311. VariableItemChangeCallback change_callback,
  312. void* context) {
  313. VariableItem* item = NULL;
  314. furi_assert(label);
  315. furi_assert(variable_item_list);
  316. with_view_model(
  317. variable_item_list->view,
  318. VariableItemListModel * model,
  319. {
  320. item = VariableItemArray_push_new(model->items);
  321. item->label = label;
  322. item->values_count = values_count;
  323. item->change_callback = change_callback;
  324. item->context = context;
  325. item->current_value_index = 0;
  326. item->current_value_text = furi_string_alloc();
  327. },
  328. true);
  329. return item;
  330. }
  331. void variable_item_list_set_enter_callback(
  332. VariableItemList* variable_item_list,
  333. VariableItemListEnterCallback callback,
  334. void* context) {
  335. furi_assert(callback);
  336. with_view_model(
  337. variable_item_list->view,
  338. VariableItemListModel * model,
  339. {
  340. UNUSED(model);
  341. variable_item_list->callback = callback;
  342. variable_item_list->context = context;
  343. },
  344. false);
  345. }
  346. void variable_item_set_current_value_index(VariableItem* item, uint8_t current_value_index) {
  347. item->current_value_index = current_value_index;
  348. }
  349. void variable_item_set_values_count(VariableItem* item, uint8_t values_count) {
  350. item->values_count = values_count;
  351. }
  352. void variable_item_set_current_value_text(VariableItem* item, const char* current_value_text) {
  353. furi_string_set(item->current_value_text, current_value_text);
  354. }
  355. uint8_t variable_item_get_current_value_index(VariableItem* item) {
  356. return item->current_value_index;
  357. }
  358. void* variable_item_get_context(VariableItem* item) {
  359. return item->context;
  360. }