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