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