menu.c 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. #include "menu.h"
  2. #include <gui/elements.h>
  3. #include <assets_icons.h>
  4. #include <furi.h>
  5. #include <m-array.h>
  6. struct Menu {
  7. View* view;
  8. };
  9. typedef struct {
  10. const char* label;
  11. IconAnimation* icon;
  12. uint32_t index;
  13. MenuItemCallback callback;
  14. void* callback_context;
  15. } MenuItem;
  16. ARRAY_DEF(MenuItemArray, MenuItem, M_POD_OPLIST);
  17. #define M_OPL_MenuItemArray_t() ARRAY_OPLIST(MenuItemArray, M_POD_OPLIST)
  18. typedef struct {
  19. MenuItemArray_t items;
  20. size_t position;
  21. } MenuModel;
  22. static void menu_process_up(Menu* menu);
  23. static void menu_process_down(Menu* menu);
  24. static void menu_process_ok(Menu* menu);
  25. static void menu_draw_callback(Canvas* canvas, void* _model) {
  26. MenuModel* model = _model;
  27. canvas_clear(canvas);
  28. size_t position = model->position;
  29. size_t items_count = MenuItemArray_size(model->items);
  30. if(items_count) {
  31. MenuItem* item;
  32. size_t shift_position;
  33. // First line
  34. canvas_set_font(canvas, FontSecondary);
  35. shift_position = (0 + position + items_count - 1) % items_count;
  36. item = MenuItemArray_get(model->items, shift_position);
  37. if(item->icon) {
  38. canvas_draw_icon_animation(canvas, 4, 3, item->icon);
  39. }
  40. canvas_draw_str(canvas, 22, 14, item->label);
  41. // Second line main
  42. canvas_set_font(canvas, FontPrimary);
  43. shift_position = (1 + position + items_count - 1) % items_count;
  44. item = MenuItemArray_get(model->items, shift_position);
  45. if(item->icon) {
  46. canvas_draw_icon_animation(canvas, 4, 25, item->icon);
  47. }
  48. canvas_draw_str(canvas, 22, 36, item->label);
  49. // Third line
  50. canvas_set_font(canvas, FontSecondary);
  51. shift_position = (2 + position + items_count - 1) % items_count;
  52. item = MenuItemArray_get(model->items, shift_position);
  53. if(item->icon) {
  54. canvas_draw_icon_animation(canvas, 4, 47, item->icon);
  55. }
  56. canvas_draw_str(canvas, 22, 58, item->label);
  57. // Frame and scrollbar
  58. elements_frame(canvas, 0, 21, 128 - 5, 21);
  59. elements_scrollbar(canvas, position, items_count);
  60. } else {
  61. canvas_draw_str(canvas, 2, 32, "Empty");
  62. elements_scrollbar(canvas, 0, 0);
  63. }
  64. }
  65. static bool menu_input_callback(InputEvent* event, void* context) {
  66. Menu* menu = context;
  67. bool consumed = false;
  68. if(event->type == InputTypeShort) {
  69. if(event->key == InputKeyUp) {
  70. consumed = true;
  71. menu_process_up(menu);
  72. } else if(event->key == InputKeyDown) {
  73. consumed = true;
  74. menu_process_down(menu);
  75. } else if(event->key == InputKeyOk) {
  76. consumed = true;
  77. menu_process_ok(menu);
  78. }
  79. } else if(event->type == InputTypeRepeat) {
  80. if(event->key == InputKeyUp) {
  81. consumed = true;
  82. menu_process_up(menu);
  83. } else if(event->key == InputKeyDown) {
  84. consumed = true;
  85. menu_process_down(menu);
  86. }
  87. }
  88. return consumed;
  89. }
  90. static void menu_enter(void* context) {
  91. Menu* menu = context;
  92. with_view_model(
  93. menu->view,
  94. MenuModel * model,
  95. {
  96. MenuItem* item = MenuItemArray_get(model->items, model->position);
  97. if(item && item->icon) {
  98. icon_animation_start(item->icon);
  99. }
  100. },
  101. false);
  102. }
  103. static void menu_exit(void* context) {
  104. Menu* menu = context;
  105. with_view_model(
  106. menu->view,
  107. MenuModel * model,
  108. {
  109. MenuItem* item = MenuItemArray_get(model->items, model->position);
  110. if(item && item->icon) {
  111. icon_animation_stop(item->icon);
  112. }
  113. },
  114. false);
  115. }
  116. Menu* menu_alloc() {
  117. Menu* menu = malloc(sizeof(Menu));
  118. menu->view = view_alloc(menu->view);
  119. view_set_context(menu->view, menu);
  120. view_allocate_model(menu->view, ViewModelTypeLocking, sizeof(MenuModel));
  121. view_set_draw_callback(menu->view, menu_draw_callback);
  122. view_set_input_callback(menu->view, menu_input_callback);
  123. view_set_enter_callback(menu->view, menu_enter);
  124. view_set_exit_callback(menu->view, menu_exit);
  125. with_view_model(
  126. menu->view,
  127. MenuModel * model,
  128. {
  129. MenuItemArray_init(model->items);
  130. model->position = 0;
  131. },
  132. true);
  133. return menu;
  134. }
  135. void menu_free(Menu* menu) {
  136. furi_assert(menu);
  137. menu_reset(menu);
  138. with_view_model(
  139. menu->view, MenuModel * model, { MenuItemArray_clear(model->items); }, false);
  140. view_free(menu->view);
  141. free(menu);
  142. }
  143. View* menu_get_view(Menu* menu) {
  144. furi_assert(menu);
  145. return (menu->view);
  146. }
  147. void menu_add_item(
  148. Menu* menu,
  149. const char* label,
  150. const Icon* icon,
  151. uint32_t index,
  152. MenuItemCallback callback,
  153. void* context) {
  154. furi_assert(menu);
  155. furi_assert(label);
  156. MenuItem* item = NULL;
  157. with_view_model(
  158. menu->view,
  159. MenuModel * model,
  160. {
  161. item = MenuItemArray_push_new(model->items);
  162. item->label = label;
  163. item->icon = icon ? icon_animation_alloc(icon) : icon_animation_alloc(&A_Plugins_14);
  164. view_tie_icon_animation(menu->view, item->icon);
  165. item->index = index;
  166. item->callback = callback;
  167. item->callback_context = context;
  168. },
  169. true);
  170. }
  171. void menu_reset(Menu* menu) {
  172. furi_assert(menu);
  173. with_view_model(
  174. menu->view,
  175. MenuModel * model,
  176. {
  177. for
  178. M_EACH(item, model->items, MenuItemArray_t) {
  179. icon_animation_stop(item->icon);
  180. icon_animation_free(item->icon);
  181. }
  182. MenuItemArray_reset(model->items);
  183. model->position = 0;
  184. },
  185. true);
  186. }
  187. void menu_set_selected_item(Menu* menu, uint32_t index) {
  188. with_view_model(
  189. menu->view,
  190. MenuModel * model,
  191. {
  192. if(index < MenuItemArray_size(model->items)) {
  193. model->position = index;
  194. }
  195. },
  196. true);
  197. }
  198. static void menu_process_up(Menu* menu) {
  199. with_view_model(
  200. menu->view,
  201. MenuModel * model,
  202. {
  203. MenuItem* item = MenuItemArray_get(model->items, model->position);
  204. if(item && item->icon) {
  205. icon_animation_stop(item->icon);
  206. }
  207. if(model->position > 0) {
  208. model->position--;
  209. } else {
  210. model->position = MenuItemArray_size(model->items) - 1;
  211. }
  212. item = MenuItemArray_get(model->items, model->position);
  213. if(item && item->icon) {
  214. icon_animation_start(item->icon);
  215. }
  216. },
  217. true);
  218. }
  219. static void menu_process_down(Menu* menu) {
  220. with_view_model(
  221. menu->view,
  222. MenuModel * model,
  223. {
  224. MenuItem* item = MenuItemArray_get(model->items, model->position);
  225. if(item && item->icon) {
  226. icon_animation_stop(item->icon);
  227. }
  228. if(model->position < MenuItemArray_size(model->items) - 1) {
  229. model->position++;
  230. } else {
  231. model->position = 0;
  232. }
  233. item = MenuItemArray_get(model->items, model->position);
  234. if(item && item->icon) {
  235. icon_animation_start(item->icon);
  236. }
  237. },
  238. true);
  239. }
  240. static void menu_process_ok(Menu* menu) {
  241. MenuItem* item = NULL;
  242. with_view_model(
  243. menu->view,
  244. MenuModel * model,
  245. {
  246. if(model->position < MenuItemArray_size(model->items)) {
  247. item = MenuItemArray_get(model->items, model->position);
  248. }
  249. },
  250. true);
  251. if(item && item->callback) {
  252. item->callback(item->callback_context, item->index);
  253. }
  254. }