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