variable-item-list.c 12 KB

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