variable_item_list.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  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. string_t 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. 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, (VariableItemListModel * model) {
  77. uint8_t position = index;
  78. if(position >= VariableItemArray_size(model->items)) {
  79. position = 0;
  80. }
  81. model->position = position;
  82. model->window_position = position;
  83. if(model->window_position > 0) {
  84. model->window_position -= 1;
  85. }
  86. if(VariableItemArray_size(model->items) <= 4) {
  87. model->window_position = 0;
  88. } else {
  89. if(model->window_position >= (VariableItemArray_size(model->items) - 4)) {
  90. model->window_position = (VariableItemArray_size(model->items) - 4);
  91. }
  92. }
  93. return true;
  94. });
  95. }
  96. uint8_t variable_item_list_get_selected_item_index(VariableItemList* variable_item_list) {
  97. VariableItemListModel* model = view_get_model(variable_item_list->view);
  98. uint8_t idx = model->position;
  99. view_commit_model(variable_item_list->view, false);
  100. return idx;
  101. }
  102. static bool variable_item_list_input_callback(InputEvent* event, void* context) {
  103. VariableItemList* variable_item_list = context;
  104. furi_assert(variable_item_list);
  105. bool consumed = false;
  106. if(event->type == InputTypeShort) {
  107. switch(event->key) {
  108. case InputKeyUp:
  109. consumed = true;
  110. variable_item_list_process_up(variable_item_list);
  111. break;
  112. case InputKeyDown:
  113. consumed = true;
  114. variable_item_list_process_down(variable_item_list);
  115. break;
  116. case InputKeyLeft:
  117. consumed = true;
  118. variable_item_list_process_left(variable_item_list);
  119. break;
  120. case InputKeyRight:
  121. consumed = true;
  122. variable_item_list_process_right(variable_item_list);
  123. break;
  124. case InputKeyOk:
  125. variable_item_list_process_ok(variable_item_list);
  126. break;
  127. default:
  128. break;
  129. }
  130. } else if(event->type == InputTypeRepeat) {
  131. switch(event->key) {
  132. case InputKeyUp:
  133. consumed = true;
  134. variable_item_list_process_up(variable_item_list);
  135. break;
  136. case InputKeyDown:
  137. consumed = true;
  138. variable_item_list_process_down(variable_item_list);
  139. break;
  140. case InputKeyLeft:
  141. consumed = true;
  142. variable_item_list_process_left(variable_item_list);
  143. break;
  144. case InputKeyRight:
  145. consumed = true;
  146. variable_item_list_process_right(variable_item_list);
  147. break;
  148. default:
  149. break;
  150. }
  151. }
  152. return consumed;
  153. }
  154. void variable_item_list_process_up(VariableItemList* variable_item_list) {
  155. with_view_model(
  156. variable_item_list->view, (VariableItemListModel * model) {
  157. uint8_t items_on_screen = 4;
  158. if(model->position > 0) {
  159. model->position--;
  160. if(((model->position - model->window_position) < 1) &&
  161. model->window_position > 0) {
  162. model->window_position--;
  163. }
  164. } else {
  165. model->position = VariableItemArray_size(model->items) - 1;
  166. if(model->position > (items_on_screen - 1)) {
  167. model->window_position = model->position - (items_on_screen - 1);
  168. }
  169. }
  170. return true;
  171. });
  172. }
  173. void variable_item_list_process_down(VariableItemList* variable_item_list) {
  174. with_view_model(
  175. variable_item_list->view, (VariableItemListModel * model) {
  176. uint8_t items_on_screen = 4;
  177. if(model->position < (VariableItemArray_size(model->items) - 1)) {
  178. model->position++;
  179. if((model->position - model->window_position) > (items_on_screen - 2) &&
  180. model->window_position <
  181. (VariableItemArray_size(model->items) - items_on_screen)) {
  182. model->window_position++;
  183. }
  184. } else {
  185. model->position = 0;
  186. model->window_position = 0;
  187. }
  188. return true;
  189. });
  190. }
  191. VariableItem* variable_item_list_get_selected_item(VariableItemListModel* model) {
  192. VariableItem* item = NULL;
  193. VariableItemArray_it_t it;
  194. uint8_t position = 0;
  195. for(VariableItemArray_it(it, model->items); !VariableItemArray_end_p(it);
  196. VariableItemArray_next(it)) {
  197. if(position == model->position) {
  198. break;
  199. }
  200. position++;
  201. }
  202. item = VariableItemArray_ref(it);
  203. furi_assert(item);
  204. return item;
  205. }
  206. void variable_item_list_process_left(VariableItemList* variable_item_list) {
  207. with_view_model(
  208. variable_item_list->view, (VariableItemListModel * model) {
  209. VariableItem* item = variable_item_list_get_selected_item(model);
  210. if(item->current_value_index > 0) {
  211. item->current_value_index--;
  212. if(item->change_callback) {
  213. item->change_callback(item);
  214. }
  215. }
  216. return true;
  217. });
  218. }
  219. void variable_item_list_process_right(VariableItemList* variable_item_list) {
  220. with_view_model(
  221. variable_item_list->view, (VariableItemListModel * model) {
  222. VariableItem* item = variable_item_list_get_selected_item(model);
  223. if(item->current_value_index < (item->values_count - 1)) {
  224. item->current_value_index++;
  225. if(item->change_callback) {
  226. item->change_callback(item);
  227. }
  228. }
  229. return true;
  230. });
  231. }
  232. void variable_item_list_process_ok(VariableItemList* variable_item_list) {
  233. with_view_model(
  234. variable_item_list->view, (VariableItemListModel * model) {
  235. if(variable_item_list->callback) {
  236. variable_item_list->callback(variable_item_list->context, model->position);
  237. }
  238. return false;
  239. });
  240. }
  241. VariableItemList* variable_item_list_alloc() {
  242. VariableItemList* variable_item_list = furi_alloc(sizeof(VariableItemList));
  243. variable_item_list->view = view_alloc();
  244. view_set_context(variable_item_list->view, variable_item_list);
  245. view_allocate_model(
  246. variable_item_list->view, ViewModelTypeLocking, sizeof(VariableItemListModel));
  247. view_set_draw_callback(variable_item_list->view, variable_item_list_draw_callback);
  248. view_set_input_callback(variable_item_list->view, variable_item_list_input_callback);
  249. with_view_model(
  250. variable_item_list->view, (VariableItemListModel * model) {
  251. VariableItemArray_init(model->items);
  252. model->position = 0;
  253. model->window_position = 0;
  254. return true;
  255. });
  256. return variable_item_list;
  257. }
  258. void variable_item_list_free(VariableItemList* variable_item_list) {
  259. furi_assert(variable_item_list);
  260. with_view_model(
  261. variable_item_list->view, (VariableItemListModel * model) {
  262. VariableItemArray_it_t it;
  263. for(VariableItemArray_it(it, model->items); !VariableItemArray_end_p(it);
  264. VariableItemArray_next(it)) {
  265. string_clear(VariableItemArray_ref(it)->current_value_text);
  266. }
  267. VariableItemArray_clear(model->items);
  268. return false;
  269. });
  270. view_free(variable_item_list->view);
  271. free(variable_item_list);
  272. }
  273. void variable_item_list_reset(VariableItemList* variable_item_list) {
  274. furi_assert(variable_item_list);
  275. with_view_model(
  276. variable_item_list->view, (VariableItemListModel * model) {
  277. VariableItemArray_it_t it;
  278. for(VariableItemArray_it(it, model->items); !VariableItemArray_end_p(it);
  279. VariableItemArray_next(it)) {
  280. string_clear(VariableItemArray_ref(it)->current_value_text);
  281. }
  282. VariableItemArray_reset(model->items);
  283. return false;
  284. });
  285. }
  286. View* variable_item_list_get_view(VariableItemList* variable_item_list) {
  287. furi_assert(variable_item_list);
  288. return variable_item_list->view;
  289. }
  290. VariableItem* variable_item_list_add(
  291. VariableItemList* variable_item_list,
  292. const char* label,
  293. uint8_t values_count,
  294. VariableItemChangeCallback change_callback,
  295. void* context) {
  296. VariableItem* item = NULL;
  297. furi_assert(label);
  298. furi_assert(variable_item_list);
  299. with_view_model(
  300. variable_item_list->view, (VariableItemListModel * model) {
  301. item = VariableItemArray_push_new(model->items);
  302. item->label = label;
  303. item->values_count = values_count;
  304. item->change_callback = change_callback;
  305. item->context = context;
  306. item->current_value_index = 0;
  307. string_init(item->current_value_text);
  308. return true;
  309. });
  310. return item;
  311. }
  312. void variable_item_list_set_enter_callback(
  313. VariableItemList* variable_item_list,
  314. VariableItemListEnterCallback callback,
  315. void* context) {
  316. furi_assert(callback);
  317. with_view_model(
  318. variable_item_list->view, (VariableItemListModel * model) {
  319. variable_item_list->callback = callback;
  320. variable_item_list->context = context;
  321. return false;
  322. });
  323. }
  324. void variable_item_set_current_value_index(VariableItem* item, uint8_t current_value_index) {
  325. item->current_value_index = current_value_index;
  326. }
  327. void variable_item_set_current_value_text(VariableItem* item, const char* current_value_text) {
  328. string_set_str(item->current_value_text, current_value_text);
  329. }
  330. uint8_t variable_item_get_current_value_index(VariableItem* item) {
  331. return item->current_value_index;
  332. }
  333. void* variable_item_get_context(VariableItem* item) {
  334. return item->context;
  335. }