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