How to navigate a list of buttons using an encoder

Description

I have a list in which I add some buttons using the lv_list_add_btn function. I can’t manage to navigate through it using a rotary encoder.

What MCU/Processor/Board and compiler are you using?

ESP32 with the latest ESP-IDF.

What LVGL version are you using?

I’m using the master branch, latest commit as of now (343605eba).

What do you want to achieve?

A scrollable list of button which can be focused with an encoder.

What have you tried so far?

I tried embedding the list in a group, associate the group with the encoder and set the group to edit mode, none worked.

Code to reproduce

This code can’t run in the simulator due to the encoder driver.

The encoder read function (prints the delta, so I assume it works):

bool hmi_encoder_read(lv_indev_drv_t *drv, lv_indev_data_t *data)
{
    data->enc_diff = hmi_encoder_moves();
    if (data->enc_diff != 0) {
        ESP_LOGD(TAG, "Encoder delta: %d", data->enc_diff);
        data->key = data->enc_diff < 0 ? LV_KEY_LEFT : LV_KEY_RIGHT;
    }

    // Encoder with no button
    data->state = LV_INDEV_STATE_REL;

    return false;
}

The menu:

tk_bottom_bar_configuration original_bottom_bar_configuration;

bool menu_open = false;
bool menu_flag = false;

lv_obj_t *menu;
lv_group_t *menu_group;
lv_obj_t *bottom_bar;

static void menu_event_cb(lv_obj_t *obj, lv_event_t event)
{
    ESP_LOGD(TAG, "Menu group event callback");
    if (event == LV_EVENT_KEY)
    {
        ESP_LOGD(TAG, "Menu group event callback received a key");
        uint32_t *key = lv_event_get_data();
        if (*key == LV_KEY_LEFT || *key == LV_KEY_UP || *key == LV_KEY_PREV)
        {
            ESP_LOGD(TAG, "Menu group event callback moving list up");
            lv_list_up(menu);
        }
        else if (*key == LV_KEY_RIGHT || *key == LV_KEY_DOWN || *key == LV_KEY_NEXT)
        {
            ESP_LOGD(TAG, "Menu group event callback moving list down");
            lv_list_down(menu);
        }
    }
}

void hide_menu(lv_obj_t *menu)
{

    // Hide the menu
    ...
}

void show_menu(tk_bottom_bar_configuration current_bb_conf, bool left)
{
    unsigned int items = left ? current_bb_conf.left_button.items_count : current_bb_conf.right_button.items_count;
    tk_menu_item *menu_items = left ? current_bb_conf.left_button.menu : current_bb_conf.right_button.menu;

    // Menu generation
    // TODO: Automatic resize
    menu = lv_list_create(lv_scr_act(), NULL);
    menu_group = lv_group_create();
    lv_group_add_obj(menu_group, menu);
    lv_list_set_scrollbar_mode(menu, LV_SCROLLBAR_MODE_AUTO);
    lv_list_set_anim_time(menu, 200);
    lv_obj_set_width(menu, 140);
    lv_obj_add_style(menu, LV_LIST_PART_BG, &tk_style_menu);
    lv_obj_add_style(menu, LV_LIST_PART_SCROLLABLE, &tk_style_menu);

    lv_obj_t *btn;
    ...
    for (int i = 0; i < items; i++)
    {
        btn = lv_list_add_btn(menu, NULL, menu_items[i].text);
        lv_obj_add_style(btn, LV_BTN_PART_MAIN, &tk_style_menu_button);
        lv_obj_set_style_local_pad_ver(btn, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 6);
        ...
    }

    // Event callback
    lv_indev_set_group(encoder_indev, menu_group);
    lv_group_set_editing(menu_group, true);
    lv_group_focus_obj(menu);
    lv_obj_set_event_cb(menu, menu_event_cb);

    // Update height
    ...

    // Update variables
    ...
    // Update bar
    ...
}

I even tried to add every single button in a group, expecting the focused button to change automatically, without calling lv_list_up(menu) or lv_list_down(menu).

Even like this one works, I feel like it’s very inelegant. From what I read in the code for lv_list, it seems that the buttons are added to an internal group, but I don’t understand how to access it.

Sorry if I missed something in the documentation, I didn’t find anything about lists and encoders.

Screenshot and/or video

Not so useful, but can give the idea of what I would like to obtain, maybe there are better suggestions than a list?

Video

Hi,

You don’t need a custom event callback to handle this. The list already handles this.
Is it working without your event callback?

I can’t see what properties are set in tk_style_menu_button but be sure to add some style for the FOCUSED state too.

Sorry for the delay, but in the end I redesigned the entire menu system taking into account refresh events and multiple groups. Just adding the list to a group works when you have set the driver to send the correct keys, no callbacks needed. The style was fine, and I don’t know what exactly went wrong, since I rewrote many things.

I’m really enjoying the library and working quite fast, thanks!

Glad to hear that the issue was finally disappeared. :slight_smile:

And also happy to hear that you like to work with LVGL ^^