acid-drop

- Hacking the planet from a LilyGo T-Deck using custom firmware
git clone git://git.acid.vegas/acid-drop.git
Log | Files | Refs | Archive | README | LICENSE

lv_menu.c (24321B)

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