1 /**
2  * @file lv_menu.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 #include "lv_menu.h"
10 
11 #if LV_USE_MENU
12 
13 /*********************
14  *      DEFINES
15  *********************/
16 #define MY_CLASS &lv_menu_class
17 
18 #include "../../../core/lv_obj.h"
19 #include "../../layouts/flex/lv_flex.h"
20 #include "../../../widgets/lv_label.h"
21 #include "../../../widgets/lv_btn.h"
22 #include "../../../widgets/lv_img.h"
23 
24 /**********************
25  *      TYPEDEFS
26  **********************/
27 
28 /**********************
29  *  STATIC PROTOTYPES
30  **********************/
31 static void lv_menu_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
32 static void lv_menu_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
33 static void lv_menu_page_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
34 static void lv_menu_page_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
35 static void lv_menu_cont_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
36 static void lv_menu_section_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
37 
38 const lv_obj_class_t lv_menu_class = {
39     .constructor_cb = lv_menu_constructor,
40     .destructor_cb = lv_menu_destructor,
41     .base_class = &lv_obj_class,
42     .width_def = (LV_DPI_DEF * 3) / 2,
43     .height_def = LV_DPI_DEF * 2,
44     .instance_size = sizeof(lv_menu_t)
45 };
46 const lv_obj_class_t lv_menu_page_class = {
47     .constructor_cb = lv_menu_page_constructor,
48     .destructor_cb = lv_menu_page_destructor,
49     .base_class = &lv_obj_class,
50     .width_def = LV_PCT(100),
51     .height_def = LV_SIZE_CONTENT,
52     .instance_size = sizeof(lv_menu_page_t)
53 };
54 
55 const lv_obj_class_t lv_menu_cont_class = {
56     .constructor_cb = lv_menu_cont_constructor,
57     .base_class = &lv_obj_class,
58     .width_def = LV_PCT(100),
59     .height_def = LV_SIZE_CONTENT
60 };
61 
62 const lv_obj_class_t lv_menu_section_class = {
63     .constructor_cb = lv_menu_section_constructor,
64     .base_class = &lv_obj_class,
65     .width_def = LV_PCT(100),
66     .height_def = LV_SIZE_CONTENT
67 };
68 
69 const lv_obj_class_t lv_menu_separator_class = {
70     .base_class = &lv_obj_class,
71     .width_def = LV_SIZE_CONTENT,
72     .height_def = LV_SIZE_CONTENT
73 };
74 
75 const lv_obj_class_t lv_menu_sidebar_cont_class = {
76     .base_class = &lv_obj_class
77 };
78 
79 const lv_obj_class_t lv_menu_main_cont_class = {
80     .base_class = &lv_obj_class
81 };
82 
83 const lv_obj_class_t lv_menu_main_header_cont_class = {
84     .base_class = &lv_obj_class
85 };
86 
87 const lv_obj_class_t lv_menu_sidebar_header_cont_class = {
88     .base_class = &lv_obj_class
89 };
90 
91 static void lv_menu_refr(lv_obj_t * obj);
92 static void lv_menu_refr_sidebar_header_mode(lv_obj_t * obj);
93 static void lv_menu_refr_main_header_mode(lv_obj_t * obj);
94 static void lv_menu_load_page_event_cb(lv_event_t * e);
95 static void lv_menu_obj_del_event_cb(lv_event_t * e);
96 static void lv_menu_back_event_cb(lv_event_t * e);
97 static void lv_menu_value_changed_event_cb(lv_event_t * e);
98 /**********************
99  *  STATIC VARIABLES
100  **********************/
101 
102 /**********************
103  *      MACROS
104  **********************/
105 
106 /**********************
107  *   GLOBAL FUNCTIONS
108  **********************/
109 bool lv_menu_item_back_btn_is_root(lv_obj_t * menu, lv_obj_t * obj);
110 void lv_menu_clear_history(lv_obj_t * obj);
111 
lv_menu_create(lv_obj_t * parent)112 lv_obj_t * lv_menu_create(lv_obj_t * parent)
113 {
114     LV_LOG_INFO("begin");
115     lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
116     lv_obj_class_init_obj(obj);
117     return obj;
118 }
119 
lv_menu_page_create(lv_obj_t * parent,char * title)120 lv_obj_t * lv_menu_page_create(lv_obj_t * parent, char * title)
121 {
122     LV_LOG_INFO("begin");
123     lv_obj_t * obj = lv_obj_class_create_obj(&lv_menu_page_class, parent);
124     lv_obj_class_init_obj(obj);
125 
126     lv_menu_page_t * page = (lv_menu_page_t *)obj;
127     if(title) {
128         page->title = lv_mem_alloc(strlen(title) + 1);
129         LV_ASSERT_MALLOC(page->title);
130         if(page->title == NULL) return NULL;
131         strcpy(page->title, title);
132     }
133     else {
134         page->title = NULL;
135     }
136 
137     return obj;
138 }
139 
lv_menu_cont_create(lv_obj_t * parent)140 lv_obj_t * lv_menu_cont_create(lv_obj_t * parent)
141 {
142     LV_LOG_INFO("begin");
143     lv_obj_t * obj = lv_obj_class_create_obj(&lv_menu_cont_class, parent);
144     lv_obj_class_init_obj(obj);
145     return obj;
146 }
147 
lv_menu_section_create(lv_obj_t * parent)148 lv_obj_t * lv_menu_section_create(lv_obj_t * parent)
149 {
150     LV_LOG_INFO("begin");
151     lv_obj_t * obj = lv_obj_class_create_obj(&lv_menu_section_class, parent);
152     lv_obj_class_init_obj(obj);
153     return obj;
154 }
155 
lv_menu_separator_create(lv_obj_t * parent)156 lv_obj_t * lv_menu_separator_create(lv_obj_t * parent)
157 {
158     LV_LOG_INFO("begin");
159     lv_obj_t * obj = lv_obj_class_create_obj(&lv_menu_separator_class, parent);
160     lv_obj_class_init_obj(obj);
161     return obj;
162 }
163 
lv_menu_refr(lv_obj_t * obj)164 void lv_menu_refr(lv_obj_t * obj)
165 {
166     LV_ASSERT_OBJ(obj, MY_CLASS);
167 
168     lv_menu_t * menu = (lv_menu_t *)obj;
169     lv_ll_t * history_ll = &(menu->history_ll);
170 
171     /* The current menu */
172     lv_menu_history_t * act_hist = _lv_ll_get_head(history_ll);
173 
174     lv_obj_t * page = NULL;
175 
176     if(act_hist != NULL) {
177         page = act_hist->page;
178         /* Delete the current item from the history */
179         _lv_ll_remove(history_ll, act_hist);
180         lv_mem_free(act_hist);
181         menu->cur_depth--;
182     }
183 
184     /* Set it */
185     lv_menu_set_page(obj, page);
186 }
187 
188 /*=====================
189  * Setter functions
190  *====================*/
191 
lv_menu_set_page(lv_obj_t * obj,lv_obj_t * page)192 void lv_menu_set_page(lv_obj_t * obj, lv_obj_t * page)
193 {
194     LV_ASSERT_OBJ(obj, MY_CLASS);
195 
196     lv_menu_t * menu = (lv_menu_t *)obj;
197 
198     /* Guard against setting the same page again */
199     if(menu->main_page == page) {
200         return;
201     }
202 
203     /* Hide previous page */
204     if(menu->main_page != NULL) {
205         lv_obj_set_parent(menu->main_page, menu->storage);
206     }
207 
208     if(page != NULL) {
209         /* Add a new node */
210         lv_ll_t * history_ll = &(menu->history_ll);
211         lv_menu_history_t * new_node = _lv_ll_ins_head(history_ll);
212         LV_ASSERT_MALLOC(new_node);
213         new_node->page = page;
214         menu->cur_depth++;
215 
216         /* Place page in main */
217         lv_obj_set_parent(page, menu->main);
218     }
219     else {
220         /* Empty page, clear history */
221         lv_menu_clear_history(obj);
222     }
223 
224     menu->main_page = page;
225 
226     /* If there is a selected tab, update checked state */
227     if(menu->selected_tab != NULL) {
228         if(menu->sidebar_page != NULL) {
229             lv_obj_add_state(menu->selected_tab, LV_STATE_CHECKED);
230         }
231         else {
232             lv_obj_clear_state(menu->selected_tab, LV_STATE_CHECKED);
233         }
234     }
235 
236     /* Back btn management */
237     if(menu->sidebar_page != NULL) {
238         /* With sidebar enabled */
239         if(menu->sidebar_generated) {
240             if(menu->mode_root_back_btn == LV_MENU_ROOT_BACK_BTN_ENABLED) {
241                 /* Root back btn is always shown if enabled*/
242                 lv_obj_clear_flag(menu->sidebar_header_back_btn, LV_OBJ_FLAG_HIDDEN);
243                 lv_obj_add_flag(menu->sidebar_header_back_btn, LV_OBJ_FLAG_CLICKABLE);
244             }
245             else {
246                 lv_obj_add_flag(menu->sidebar_header_back_btn, LV_OBJ_FLAG_HIDDEN);
247                 lv_obj_clear_flag(menu->sidebar_header_back_btn, LV_OBJ_FLAG_CLICKABLE);
248             }
249         }
250 
251         if(menu->cur_depth >= 2) {
252             lv_obj_clear_flag(menu->main_header_back_btn, LV_OBJ_FLAG_HIDDEN);
253             lv_obj_add_flag(menu->main_header_back_btn, LV_OBJ_FLAG_CLICKABLE);
254         }
255         else {
256             lv_obj_add_flag(menu->main_header_back_btn, LV_OBJ_FLAG_HIDDEN);
257             lv_obj_clear_flag(menu->main_header_back_btn, LV_OBJ_FLAG_CLICKABLE);
258         }
259     }
260     else {
261         /* With sidebar disabled */
262         if(menu->cur_depth >= 2 || menu->mode_root_back_btn == LV_MENU_ROOT_BACK_BTN_ENABLED) {
263             lv_obj_clear_flag(menu->main_header_back_btn, LV_OBJ_FLAG_HIDDEN);
264             lv_obj_add_flag(menu->main_header_back_btn, LV_OBJ_FLAG_CLICKABLE);
265         }
266         else {
267             lv_obj_add_flag(menu->main_header_back_btn, LV_OBJ_FLAG_HIDDEN);
268             lv_obj_clear_flag(menu->main_header_back_btn, LV_OBJ_FLAG_CLICKABLE);
269         }
270     }
271 
272     lv_event_send((lv_obj_t *)menu, LV_EVENT_VALUE_CHANGED, NULL);
273 
274     lv_menu_refr_main_header_mode(obj);
275 }
276 
lv_menu_set_sidebar_page(lv_obj_t * obj,lv_obj_t * page)277 void lv_menu_set_sidebar_page(lv_obj_t * obj, lv_obj_t * page)
278 {
279     LV_ASSERT_OBJ(obj, MY_CLASS);
280 
281     lv_menu_t * menu = (lv_menu_t *)obj;
282 
283     /* Sidebar management*/
284     if(page != NULL) {
285         /* Sidebar should be enabled */
286         if(!menu->sidebar_generated) {
287             /* Create sidebar */
288             lv_obj_t * sidebar_cont = lv_obj_class_create_obj(&lv_menu_sidebar_cont_class, obj);
289             lv_obj_class_init_obj(sidebar_cont);
290             lv_obj_move_to_index(sidebar_cont, 1);
291             lv_obj_set_size(sidebar_cont, LV_PCT(30), LV_PCT(100));
292             lv_obj_set_flex_flow(sidebar_cont, LV_FLEX_FLOW_COLUMN);
293             lv_obj_add_flag(sidebar_cont, LV_OBJ_FLAG_EVENT_BUBBLE);
294             lv_obj_clear_flag(sidebar_cont, LV_OBJ_FLAG_CLICKABLE);
295             menu->sidebar = sidebar_cont;
296 
297             lv_obj_t * sidebar_header = lv_obj_class_create_obj(&lv_menu_sidebar_header_cont_class, sidebar_cont);
298             lv_obj_class_init_obj(sidebar_header);
299             lv_obj_set_size(sidebar_header, LV_PCT(100), LV_SIZE_CONTENT);
300             lv_obj_set_flex_flow(sidebar_header, LV_FLEX_FLOW_ROW);
301             lv_obj_set_flex_align(sidebar_header, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
302             lv_obj_clear_flag(sidebar_header, LV_OBJ_FLAG_CLICKABLE);
303             lv_obj_add_flag(sidebar_header, LV_OBJ_FLAG_EVENT_BUBBLE);
304             menu->sidebar_header = sidebar_header;
305 
306             lv_obj_t * sidebar_header_back_btn = lv_btn_create(menu->sidebar_header);
307             lv_obj_add_event_cb(sidebar_header_back_btn, lv_menu_back_event_cb, LV_EVENT_CLICKED, menu);
308             lv_obj_add_flag(sidebar_header_back_btn, LV_OBJ_FLAG_EVENT_BUBBLE);
309             lv_obj_set_flex_flow(sidebar_header_back_btn, LV_FLEX_FLOW_ROW);
310             menu->sidebar_header_back_btn = sidebar_header_back_btn;
311 
312             lv_obj_t * sidebar_header_back_icon = lv_img_create(menu->sidebar_header_back_btn);
313             lv_img_set_src(sidebar_header_back_icon, LV_SYMBOL_LEFT);
314 
315             lv_obj_t * sidebar_header_title = lv_label_create(menu->sidebar_header);
316             lv_obj_add_flag(sidebar_header_title, LV_OBJ_FLAG_HIDDEN);
317             menu->sidebar_header_title = sidebar_header_title;
318 
319             menu->sidebar_generated = true;
320         }
321 
322         lv_obj_set_parent(page, menu->sidebar);
323 
324         lv_menu_refr_sidebar_header_mode(obj);
325     }
326     else {
327         /* Sidebar should be disabled */
328         if(menu->sidebar_generated) {
329             lv_obj_set_parent(menu->sidebar_page, menu->storage);
330             lv_obj_del(menu->sidebar);
331 
332             menu->sidebar_generated = false;
333         }
334     }
335 
336     menu->sidebar_page = page;
337     lv_menu_refr(obj);
338 }
339 
lv_menu_set_mode_header(lv_obj_t * obj,lv_menu_mode_header_t mode_header)340 void lv_menu_set_mode_header(lv_obj_t * obj, lv_menu_mode_header_t mode_header)
341 {
342     LV_ASSERT_OBJ(obj, MY_CLASS);
343 
344     lv_menu_t * menu = (lv_menu_t *)obj;
345 
346     if(menu->mode_header != mode_header) {
347         menu->mode_header = mode_header;
348         lv_menu_refr_main_header_mode(obj);
349         if(menu->sidebar_generated) lv_menu_refr_sidebar_header_mode(obj);
350     }
351 }
352 
lv_menu_set_mode_root_back_btn(lv_obj_t * obj,lv_menu_mode_root_back_btn_t mode_root_back_btn)353 void lv_menu_set_mode_root_back_btn(lv_obj_t * obj, lv_menu_mode_root_back_btn_t mode_root_back_btn)
354 {
355     LV_ASSERT_OBJ(obj, MY_CLASS);
356 
357     lv_menu_t * menu = (lv_menu_t *)obj;
358 
359     if(menu->mode_root_back_btn != mode_root_back_btn) {
360         menu->mode_root_back_btn = mode_root_back_btn;
361         lv_menu_refr(obj);
362     }
363 }
364 
lv_menu_set_load_page_event(lv_obj_t * menu,lv_obj_t * obj,lv_obj_t * page)365 void lv_menu_set_load_page_event(lv_obj_t * menu, lv_obj_t * obj, lv_obj_t * page)
366 {
367     LV_ASSERT_OBJ(menu, MY_CLASS);
368 
369     lv_obj_add_flag(obj, LV_OBJ_FLAG_CLICKABLE);
370     lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE);
371     lv_obj_add_flag(obj, LV_OBJ_FLAG_SCROLL_ON_FOCUS);
372 
373     /* Remove old event */
374     if(lv_obj_remove_event_cb(obj, lv_menu_load_page_event_cb)) {
375         lv_event_send(obj, LV_EVENT_DELETE, NULL);
376         lv_obj_remove_event_cb(obj, lv_menu_obj_del_event_cb);
377     }
378 
379     lv_menu_load_page_event_data_t * event_data = lv_mem_alloc(sizeof(lv_menu_load_page_event_data_t));
380     event_data->menu = menu;
381     event_data->page = page;
382 
383     lv_obj_add_event_cb(obj, lv_menu_load_page_event_cb, LV_EVENT_CLICKED, event_data);
384     lv_obj_add_event_cb(obj, lv_menu_obj_del_event_cb, LV_EVENT_DELETE, event_data);
385 }
386 
387 /*=====================
388  * Getter functions
389  *====================*/
lv_menu_get_cur_main_page(lv_obj_t * obj)390 lv_obj_t * lv_menu_get_cur_main_page(lv_obj_t * obj)
391 {
392     LV_ASSERT_OBJ(obj, MY_CLASS);
393 
394     lv_menu_t * menu = (lv_menu_t *)obj;
395     return menu->main_page;
396 }
397 
lv_menu_get_cur_sidebar_page(lv_obj_t * obj)398 lv_obj_t * lv_menu_get_cur_sidebar_page(lv_obj_t * obj)
399 {
400     LV_ASSERT_OBJ(obj, MY_CLASS);
401 
402     lv_menu_t * menu = (lv_menu_t *)obj;
403     return menu->sidebar_page;
404 }
405 
lv_menu_get_main_header(lv_obj_t * obj)406 lv_obj_t * lv_menu_get_main_header(lv_obj_t * obj)
407 {
408     LV_ASSERT_OBJ(obj, MY_CLASS);
409 
410     lv_menu_t * menu = (lv_menu_t *)obj;
411     return menu->main_header;
412 }
413 
lv_menu_get_main_header_back_btn(lv_obj_t * obj)414 lv_obj_t * lv_menu_get_main_header_back_btn(lv_obj_t * obj)
415 {
416     LV_ASSERT_OBJ(obj, MY_CLASS);
417 
418     lv_menu_t * menu = (lv_menu_t *)obj;
419     return menu->main_header_back_btn;
420 }
421 
lv_menu_get_sidebar_header(lv_obj_t * obj)422 lv_obj_t * lv_menu_get_sidebar_header(lv_obj_t * obj)
423 {
424     LV_ASSERT_OBJ(obj, MY_CLASS);
425 
426     lv_menu_t * menu = (lv_menu_t *)obj;
427     return menu->sidebar_header;
428 }
429 
lv_menu_get_sidebar_header_back_btn(lv_obj_t * obj)430 lv_obj_t * lv_menu_get_sidebar_header_back_btn(lv_obj_t * obj)
431 {
432     LV_ASSERT_OBJ(obj, MY_CLASS);
433 
434     lv_menu_t * menu = (lv_menu_t *)obj;
435     return menu->sidebar_header_back_btn;
436 }
437 
lv_menu_back_btn_is_root(lv_obj_t * menu,lv_obj_t * obj)438 bool lv_menu_back_btn_is_root(lv_obj_t * menu, lv_obj_t * obj)
439 {
440     LV_ASSERT_OBJ(menu, MY_CLASS);
441 
442     if(obj == ((lv_menu_t *)menu)->sidebar_header_back_btn) {
443         return true;
444     }
445 
446     if(obj == ((lv_menu_t *)menu)->main_header_back_btn && ((lv_menu_t *)menu)->prev_depth <= 1) {
447         return true;
448     }
449 
450     return false;
451 }
452 
lv_menu_clear_history(lv_obj_t * obj)453 void lv_menu_clear_history(lv_obj_t * obj)
454 {
455     LV_ASSERT_OBJ(obj, MY_CLASS);
456 
457     lv_menu_t * menu = (lv_menu_t *)obj;
458     lv_ll_t * history_ll = &(menu->history_ll);
459 
460     _lv_ll_clear(history_ll);
461 
462     menu->cur_depth = 0;
463 }
464 
465 /**********************
466  *   STATIC FUNCTIONS
467  **********************/
468 
lv_menu_constructor(const lv_obj_class_t * class_p,lv_obj_t * obj)469 static void lv_menu_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
470 {
471     LV_UNUSED(class_p);
472     LV_TRACE_OBJ_CREATE("begin");
473 
474     lv_obj_set_layout(obj, LV_LAYOUT_FLEX);
475     lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_ROW);
476 
477     lv_menu_t * menu = (lv_menu_t *)obj;
478 
479     menu->mode_header = LV_MENU_HEADER_TOP_FIXED;
480     menu->mode_root_back_btn = LV_MENU_ROOT_BACK_BTN_DISABLED;
481     menu->cur_depth = 0;
482     menu->prev_depth = 0;
483     menu->sidebar_generated = false;
484 
485     _lv_ll_init(&(menu->history_ll), sizeof(lv_menu_history_t));
486 
487     menu->storage = lv_obj_create(obj);
488     lv_obj_add_flag(menu->storage, LV_OBJ_FLAG_HIDDEN);
489 
490     menu->sidebar = NULL;
491     menu->sidebar_header = NULL;
492     menu->sidebar_header_back_btn = NULL;
493     menu->sidebar_header_title = NULL;
494     menu->sidebar_page = NULL;
495 
496     lv_obj_t * main_cont = lv_obj_class_create_obj(&lv_menu_main_cont_class, obj);
497     lv_obj_class_init_obj(main_cont);
498     lv_obj_set_height(main_cont, LV_PCT(100));
499     lv_obj_set_flex_grow(main_cont, 1);
500     lv_obj_set_flex_flow(main_cont, LV_FLEX_FLOW_COLUMN);
501     lv_obj_add_flag(main_cont, LV_OBJ_FLAG_EVENT_BUBBLE);
502     lv_obj_clear_flag(main_cont, LV_OBJ_FLAG_CLICKABLE);
503     menu->main = main_cont;
504 
505     lv_obj_t * main_header = lv_obj_class_create_obj(&lv_menu_main_header_cont_class, main_cont);
506     lv_obj_class_init_obj(main_header);
507     lv_obj_set_size(main_header, LV_PCT(100), LV_SIZE_CONTENT);
508     lv_obj_set_flex_flow(main_header, LV_FLEX_FLOW_ROW);
509     lv_obj_set_flex_align(main_header, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
510     lv_obj_clear_flag(main_header, LV_OBJ_FLAG_CLICKABLE);
511     lv_obj_add_flag(main_header, LV_OBJ_FLAG_EVENT_BUBBLE);
512     menu->main_header = main_header;
513 
514     /* Create the default simple back btn and title */
515     lv_obj_t * main_header_back_btn = lv_btn_create(menu->main_header);
516     lv_obj_add_event_cb(main_header_back_btn, lv_menu_back_event_cb, LV_EVENT_CLICKED, menu);
517     lv_obj_add_flag(main_header_back_btn, LV_OBJ_FLAG_EVENT_BUBBLE);
518     lv_obj_set_flex_flow(main_header_back_btn, LV_FLEX_FLOW_ROW);
519     menu->main_header_back_btn = main_header_back_btn;
520 
521     lv_obj_t * main_header_back_icon = lv_img_create(menu->main_header_back_btn);
522     lv_img_set_src(main_header_back_icon, LV_SYMBOL_LEFT);
523 
524     lv_obj_t * main_header_title = lv_label_create(menu->main_header);
525     lv_obj_add_flag(main_header_title, LV_OBJ_FLAG_HIDDEN);
526     menu->main_header_title = main_header_title;
527 
528     menu->main_page = NULL;
529     menu->selected_tab = NULL;
530 
531     lv_obj_add_event_cb(obj, lv_menu_value_changed_event_cb, LV_EVENT_VALUE_CHANGED, menu);
532 
533     LV_TRACE_OBJ_CREATE("finished");
534 }
535 
lv_menu_destructor(const lv_obj_class_t * class_p,lv_obj_t * obj)536 static void lv_menu_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
537 {
538     LV_UNUSED(class_p);
539     LV_TRACE_OBJ_CREATE("begin");
540 
541     lv_menu_t * menu = (lv_menu_t *)obj;
542     lv_ll_t * history_ll = &(menu->history_ll);
543 
544     _lv_ll_clear(history_ll);
545 
546     LV_TRACE_OBJ_CREATE("finished");
547 }
548 
lv_menu_page_constructor(const lv_obj_class_t * class_p,lv_obj_t * obj)549 static void lv_menu_page_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
550 {
551     LV_UNUSED(class_p);
552 
553     lv_menu_t * menu = (lv_menu_t *)lv_obj_get_parent(obj);
554 
555     lv_obj_set_parent(obj, ((lv_menu_t *)menu)->storage);
556     lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_COLUMN);
557     lv_obj_set_flex_align(obj, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
558     lv_obj_add_flag(obj, LV_OBJ_FLAG_EVENT_BUBBLE);
559 }
560 
lv_menu_page_destructor(const lv_obj_class_t * class_p,lv_obj_t * obj)561 static void lv_menu_page_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
562 {
563     LV_UNUSED(class_p);
564 
565     lv_menu_page_t * page = (lv_menu_page_t *)obj;
566 
567     if(page->title != NULL) {
568         lv_mem_free(page->title);
569         page->title = NULL;
570     }
571 }
572 
lv_menu_cont_constructor(const lv_obj_class_t * class_p,lv_obj_t * obj)573 static void lv_menu_cont_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
574 {
575     LV_UNUSED(class_p);
576     lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_ROW);
577     lv_obj_set_flex_align(obj, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
578     lv_obj_clear_flag(obj, LV_OBJ_FLAG_CLICKABLE);
579 }
580 
lv_menu_section_constructor(const lv_obj_class_t * class_p,lv_obj_t * obj)581 static void lv_menu_section_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
582 {
583     LV_UNUSED(class_p);
584     lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_COLUMN);
585     lv_obj_clear_flag(obj, LV_OBJ_FLAG_CLICKABLE);
586 }
587 
lv_menu_refr_sidebar_header_mode(lv_obj_t * obj)588 static void lv_menu_refr_sidebar_header_mode(lv_obj_t * obj)
589 {
590     LV_ASSERT_OBJ(obj, MY_CLASS);
591 
592     lv_menu_t * menu = (lv_menu_t *)obj;
593 
594     if(menu->sidebar_header == NULL || menu->sidebar_page == NULL) return;
595 
596     switch(menu->mode_header) {
597         case LV_MENU_HEADER_TOP_FIXED:
598             /* Content should fill the remaining space */
599             lv_obj_move_to_index(menu->sidebar_header, 0);
600             lv_obj_set_flex_grow(menu->sidebar_page, 1);
601             break;
602         case LV_MENU_HEADER_TOP_UNFIXED:
603             lv_obj_move_to_index(menu->sidebar_header, 0);
604             lv_obj_set_flex_grow(menu->sidebar_page, 0);
605             break;
606         case LV_MENU_HEADER_BOTTOM_FIXED:
607             lv_obj_move_to_index(menu->sidebar_header, 1);
608             lv_obj_set_flex_grow(menu->sidebar_page, 1);
609             break;
610     }
611 
612     lv_obj_refr_size(menu->sidebar_header);
613     lv_obj_refr_size(menu->sidebar_page);
614 
615     if(lv_obj_get_content_height(menu->sidebar_header) == 0) {
616         lv_obj_add_flag(menu->sidebar_header, LV_OBJ_FLAG_HIDDEN);
617     }
618     else {
619         lv_obj_clear_flag(menu->sidebar_header, LV_OBJ_FLAG_HIDDEN);
620     }
621 }
622 
lv_menu_refr_main_header_mode(lv_obj_t * obj)623 static void lv_menu_refr_main_header_mode(lv_obj_t * obj)
624 {
625     LV_ASSERT_OBJ(obj, MY_CLASS);
626 
627     lv_menu_t * menu = (lv_menu_t *)obj;
628 
629     if(menu->main_header == NULL || menu->main_page == NULL) return;
630 
631     switch(menu->mode_header) {
632         case LV_MENU_HEADER_TOP_FIXED:
633             /* Content should fill the remaining space */
634             lv_obj_move_to_index(menu->main_header, 0);
635             lv_obj_set_flex_grow(menu->main_page, 1);
636             break;
637         case LV_MENU_HEADER_TOP_UNFIXED:
638             lv_obj_move_to_index(menu->main_header, 0);
639             lv_obj_set_flex_grow(menu->main_page, 0);
640             break;
641         case LV_MENU_HEADER_BOTTOM_FIXED:
642             lv_obj_move_to_index(menu->main_header, 1);
643             lv_obj_set_flex_grow(menu->main_page, 1);
644             break;
645     }
646 
647     lv_obj_refr_size(menu->main_header);
648     lv_obj_refr_size(menu->main_page);
649     lv_obj_update_layout(menu->main_header);
650 
651     if(lv_obj_get_content_height(menu->main_header) == 0) {
652         lv_obj_add_flag(menu->main_header, LV_OBJ_FLAG_HIDDEN);
653     }
654     else {
655         lv_obj_clear_flag(menu->main_header, LV_OBJ_FLAG_HIDDEN);
656     }
657 }
658 
lv_menu_load_page_event_cb(lv_event_t * e)659 static void lv_menu_load_page_event_cb(lv_event_t * e)
660 {
661     lv_obj_t * obj = lv_event_get_target(e);
662     lv_menu_load_page_event_data_t * event_data = lv_event_get_user_data(e);
663     lv_menu_t * menu = (lv_menu_t *)(event_data->menu);
664     lv_obj_t * page = event_data->page;
665 
666     if(menu->sidebar_page != NULL) {
667         /* Check if clicked obj is in the sidebar */
668         bool sidebar = false;
669         lv_obj_t * parent = obj;
670 
671         while(parent) {
672             if(parent == (lv_obj_t *)menu) break;
673             if(parent == menu->sidebar) {
674                 sidebar = true;
675                 break;
676             }
677             parent = lv_obj_get_parent(parent);
678         }
679 
680         if(sidebar) {
681             /* Clear checked state of previous obj */
682             if(menu->selected_tab != obj && menu->selected_tab != NULL) {
683                 lv_obj_clear_state(menu->selected_tab, LV_STATE_CHECKED);
684             }
685 
686             lv_menu_clear_history((lv_obj_t *)menu);
687 
688             menu->selected_tab = obj;
689         }
690     }
691 
692     lv_menu_set_page((lv_obj_t *)menu, page);
693 
694     if(lv_group_get_default() != NULL && menu->sidebar_page == NULL) {
695         /* Sidebar is not supported for now*/
696         lv_group_focus_next(lv_group_get_default());
697     }
698 }
699 
lv_menu_obj_del_event_cb(lv_event_t * e)700 static void lv_menu_obj_del_event_cb(lv_event_t * e)
701 {
702     lv_menu_load_page_event_data_t * event_data = lv_event_get_user_data(e);
703     lv_mem_free(event_data);
704 }
705 
lv_menu_back_event_cb(lv_event_t * e)706 static void lv_menu_back_event_cb(lv_event_t * e)
707 {
708     lv_event_code_t code = lv_event_get_code(e);
709     /* LV_EVENT_CLICKED */
710     if(code == LV_EVENT_CLICKED) {
711         lv_obj_t * obj = lv_event_get_target(e);
712         lv_menu_t * menu = (lv_menu_t *)lv_event_get_user_data(e);
713 
714         if(!(obj == menu->main_header_back_btn || obj == menu->sidebar_header_back_btn)) return;
715 
716         menu->prev_depth = menu->cur_depth; /* Save the previous value for user event handler */
717 
718         if(lv_menu_back_btn_is_root((lv_obj_t *)menu, obj)) return;
719 
720         lv_ll_t * history_ll = &(menu->history_ll);
721 
722         /* The current menu */
723         lv_menu_history_t * act_hist = _lv_ll_get_head(history_ll);
724 
725         /* The previous menu */
726         lv_menu_history_t * prev_hist = _lv_ll_get_next(history_ll, act_hist);
727 
728         if(prev_hist != NULL) {
729             /* Previous menu exists */
730             /* Delete the current item from the history */
731             _lv_ll_remove(history_ll, act_hist);
732             lv_mem_free(act_hist);
733             menu->cur_depth--;
734             /* Create the previous menu.
735             *  Remove it from the history because `lv_menu_set_page` will add it again */
736             _lv_ll_remove(history_ll, prev_hist);
737             menu->cur_depth--;
738             lv_menu_set_page(&(menu->obj), prev_hist->page);
739 
740             lv_mem_free(prev_hist);
741         }
742     }
743 }
744 
lv_menu_value_changed_event_cb(lv_event_t * e)745 static void lv_menu_value_changed_event_cb(lv_event_t * e)
746 {
747     lv_obj_t * obj = lv_event_get_user_data(e);
748     lv_menu_t * menu = (lv_menu_t *)obj;
749 
750     lv_menu_page_t * main_page = (lv_menu_page_t *)lv_menu_get_cur_main_page(obj);
751     if(main_page != NULL && menu->main_header_title != NULL) {
752         if(main_page->title != NULL) {
753             lv_label_set_text(menu->main_header_title, main_page->title);
754             lv_obj_clear_flag(menu->main_header_title, LV_OBJ_FLAG_HIDDEN);
755         }
756         else {
757             lv_obj_add_flag(menu->main_header_title, LV_OBJ_FLAG_HIDDEN);
758         }
759     }
760 
761     lv_menu_page_t * sidebar_page = (lv_menu_page_t *)lv_menu_get_cur_sidebar_page(obj);
762     if(sidebar_page != NULL && menu->sidebar_header_title != NULL) {
763         if(sidebar_page->title != NULL) {
764             lv_label_set_text(menu->sidebar_header_title, sidebar_page->title);
765             lv_obj_clear_flag(menu->sidebar_header_title, LV_OBJ_FLAG_HIDDEN);
766         }
767         else {
768             lv_obj_add_flag(menu->sidebar_header_title, LV_OBJ_FLAG_HIDDEN);
769         }
770     }
771 }
772 #endif /*LV_USE_MENU*/
773