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