menu.c 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. #include "menu.h"
  2. #include <m-array.h>
  3. #include <gui/elements.h>
  4. #include <assets_icons.h>
  5. #include <furi.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. view_free(menu->view);
  139. free(menu);
  140. }
  141. View* menu_get_view(Menu* menu) {
  142. furi_assert(menu);
  143. return (menu->view);
  144. }
  145. void menu_add_item(
  146. Menu* menu,
  147. const char* label,
  148. const Icon* icon,
  149. uint32_t index,
  150. MenuItemCallback callback,
  151. void* context) {
  152. furi_assert(menu);
  153. furi_assert(label);
  154. MenuItem* item = NULL;
  155. with_view_model(
  156. menu->view,
  157. MenuModel * model,
  158. {
  159. item = MenuItemArray_push_new(model->items);
  160. item->label = label;
  161. item->icon = icon ? icon_animation_alloc(icon) : icon_animation_alloc(&A_Plugins_14);
  162. view_tie_icon_animation(menu->view, item->icon);
  163. item->index = index;
  164. item->callback = callback;
  165. item->callback_context = context;
  166. },
  167. true);
  168. }
  169. void menu_reset(Menu* menu) {
  170. furi_assert(menu);
  171. with_view_model(
  172. menu->view,
  173. MenuModel * model,
  174. {
  175. for
  176. M_EACH(item, model->items, MenuItemArray_t) {
  177. icon_animation_stop(item->icon);
  178. icon_animation_free(item->icon);
  179. }
  180. MenuItemArray_reset(model->items);
  181. model->position = 0;
  182. },
  183. true);
  184. }
  185. void menu_set_selected_item(Menu* menu, uint32_t index) {
  186. with_view_model(
  187. menu->view,
  188. MenuModel * model,
  189. {
  190. if(index < MenuItemArray_size(model->items)) {
  191. model->position = index;
  192. }
  193. },
  194. true);
  195. }
  196. static void menu_process_up(Menu* menu) {
  197. with_view_model(
  198. menu->view,
  199. MenuModel * model,
  200. {
  201. MenuItem* item = MenuItemArray_get(model->items, model->position);
  202. if(item && item->icon) {
  203. icon_animation_stop(item->icon);
  204. }
  205. if(model->position > 0) {
  206. model->position--;
  207. } else {
  208. model->position = MenuItemArray_size(model->items) - 1;
  209. }
  210. item = MenuItemArray_get(model->items, model->position);
  211. if(item && item->icon) {
  212. icon_animation_start(item->icon);
  213. }
  214. },
  215. true);
  216. }
  217. static void menu_process_down(Menu* menu) {
  218. with_view_model(
  219. menu->view,
  220. MenuModel * model,
  221. {
  222. MenuItem* item = MenuItemArray_get(model->items, model->position);
  223. if(item && item->icon) {
  224. icon_animation_stop(item->icon);
  225. }
  226. if(model->position < MenuItemArray_size(model->items) - 1) {
  227. model->position++;
  228. } else {
  229. model->position = 0;
  230. }
  231. item = MenuItemArray_get(model->items, model->position);
  232. if(item && item->icon) {
  233. icon_animation_start(item->icon);
  234. }
  235. },
  236. true);
  237. }
  238. static void menu_process_ok(Menu* menu) {
  239. MenuItem* item = NULL;
  240. with_view_model(
  241. menu->view,
  242. MenuModel * model,
  243. {
  244. if(model->position < MenuItemArray_size(model->items)) {
  245. item = MenuItemArray_get(model->items, model->position);
  246. }
  247. },
  248. true);
  249. if(item && item->callback) {
  250. item->callback(item->callback_context, item->index);
  251. }
  252. }