Cannot get the lv_table widget to scroll

Description

Hello, I’m having an issue with the lv_table widget. I cannot get it to scroll the table off screen.

  • I’m using an embedded board , RPI PICO 2040, EEZ Studio LVGL project, VS Code LVGL 8.3. My LCD is 240 x 240 px, aprox 1.5" square.

  • I ported over the exact code from the Simple table example Table (lv_table) — LVGL documentation

  • I am using hardware keys, with user actions to send up/down/enter arrow key events to the table object. The object receives the keys.

  • The screen will show the selected cell up and down the list. I added some code in the draw_part_event cb() to highlight the selected cell. and I confirmed the row, column values were correct.

  • The example code was using a default font so everything was very small and the entire table fit on the screen so scrolling was not occurring.

  • I changed to a larger font that I was already using on my application, and changed to use only a single column table. This allowed about 4 cells to be displayed on the screen. And I tried to see if the scrolling would work.

  • When I reached the bottom of the screen it would not scroll. Instead each pressing of the down key just incremented the row and the highlight off the screen. Pressing the up key would move the highlight back on to screen.

  • At no time did the scroll bar move.

  • I literally tried every parameter in the lvgl scrolling and lv_table LVGL documentation.

  • I even tried turning on the Scrollable flags for the parent screen object of the table. It displayed scroll bars but had no effect.

I’ve included the relevant code and screen shots of my screen for the Table test . I would appreciate any ideas.

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

What LVGL version are you using?

What do you want to achieve?

What have you tried so far?

Code to reproduce

Add a code snippet which can run in the simulator. It should contain only the relevant code that compiles without errors when separated from your main code base.

The code block(s) should be formatted like:


<screens.c>
static void draw_part_event_cb(lv_event_t * e)
{
    lv_obj_t * obj = lv_event_get_target(e);
    lv_obj_draw_part_dsc_t * dsc = lv_event_get_draw_part_dsc(e);

    
    /*If the cells are drawn...*/
    if(dsc->part == LV_PART_ITEMS) {
        uint32_t row = dsc->id /  lv_table_get_col_cnt(obj);
        uint32_t col = dsc->id - row * lv_table_get_col_cnt(obj);

        /*Make the texts in the first cell center aligned*/
        if(row == 0) {
            dsc->label_dsc->align = LV_TEXT_ALIGN_CENTER;
            dsc->rect_dsc->bg_color = lv_color_mix(lv_palette_main(LV_PALETTE_BLUE), dsc->rect_dsc->bg_color, LV_OPA_20);
            dsc->rect_dsc->bg_opa = LV_OPA_COVER;
        }
        /*In the first column align the texts to the right*/
        else if(col == 0) {
            dsc->label_dsc->align = LV_TEXT_ALIGN_CENTER;
        }

        /*MAke every 2nd row blueish*/
        if((row != 0 && row % 2) == 0) {
            dsc->rect_dsc->bg_color = lv_color_mix(lv_palette_main(LV_PALETTE_BLUE), dsc->rect_dsc->bg_color, LV_OPA_90);
            dsc->rect_dsc->bg_opa = LV_OPA_COVER;
        }

        uint16_t sel_col;
        uint16_t sel_row;
        lv_table_get_selected_cell(obj, &sel_row, &sel_col);

        /*update color of row selectd*/
        if(sel_row == row && sel_col == col) {
            dsc->rect_dsc->bg_color = lv_color_mix(lv_palette_main(LV_PALETTE_YELLOW), dsc->rect_dsc->bg_color, LV_OPA_90);
            dsc->rect_dsc->bg_opa = LV_OPA_COVER;
        }
    }
}

void create_screen_table_test() {
    void *flowState = getFlowState(0, 15);
    lv_obj_t *obj = lv_obj_create(0);
    objects.table_test = obj;
    lv_obj_set_pos(obj, 0, 0);
    lv_obj_set_size(obj, 240, 240);
    lv_obj_add_event_cb(obj, event_handler_cb_table_test_table_test, LV_EVENT_ALL, flowState);
    lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE);
    lv_obj_set_style_bg_color(obj, lv_color_hex(0xfff2f2f2), LV_PART_MAIN | LV_STATE_DEFAULT);
    {
        lv_obj_t *parent_obj = obj;

        {
            // table
            lv_obj_t *obj = lv_table_create(parent_obj);
            objects.table = obj;
            lv_obj_set_pos(obj, 0, 0);
            lv_obj_set_size(obj, 240, 480);
            //lv_obj_add_flag(objects.table, LV_OBJ_FLAG_SCROLLABLE);
            //lv_obj_add_flag(objects.table, LV_OBJ_FLAG_SCROLL_ON_FOCUS);
			//lv_obj_set_scrollbar_mode(objects.table, LV_SCROLLBAR_MODE_AUTO);
            //lv_obj_set_scroll_dir(objects.table, LV_DIR_VER);
            
            //lv_table_set_col_cnt(objects.table, 1);
            lv_table_set_col_width(objects.table, 0, 238);
            //lv_table_set_row_cnt(objects.table, 8);
            lv_obj_set_style_text_font(objects.table, &ui_font_dosis_reg_36, LV_PART_MAIN | LV_STATE_DEFAULT);

        }

    
        {
            // Ent_button_cloaked_5
            lv_obj_t *obj = lv_btn_create(parent_obj);
            objects.ent_button_cloaked_5 = obj;
            lv_obj_set_pos(obj, 262, -30);
            lv_obj_set_size(obj, 1, 1);
            lv_obj_add_event_cb(obj, event_handler_cb_table_test_ent_button_cloaked_5, LV_EVENT_ALL, flowState);
            lv_obj_clear_flag(obj, LV_OBJ_FLAG_CLICK_FOCUSABLE);
            lv_obj_set_style_border_color(obj, lv_color_hex(0xfff2f2f2), LV_PART_MAIN | LV_STATE_DEFAULT);
            lv_obj_set_style_bg_color(obj, lv_color_hex(0xfff2f2f2), LV_PART_MAIN | LV_STATE_DEFAULT);
            lv_obj_set_style_border_width(obj, 1, LV_PART_MAIN | LV_STATE_DEFAULT);
        }
        
        {
            // Dwn_button_cloaked_6
            lv_obj_t *obj = lv_btn_create(parent_obj);
            objects.dwn_button_cloaked_6 = obj;
            lv_obj_set_pos(obj, -300, -40);
            lv_obj_set_size(obj, 1, 1);
            lv_obj_add_event_cb(obj, event_handler_cb_table_test_dwn_button_cloaked_6, LV_EVENT_ALL, flowState);
            lv_obj_clear_flag(obj, LV_OBJ_FLAG_CLICK_FOCUSABLE);
            lv_obj_set_style_border_color(obj, lv_color_hex(0xfff2f2f2), LV_PART_MAIN | LV_STATE_DEFAULT);
            lv_obj_set_style_bg_color(obj, lv_color_hex(0xfff2f2f2), LV_PART_MAIN | LV_STATE_DEFAULT);
            lv_obj_set_style_border_width(obj, 1, LV_PART_MAIN | LV_STATE_DEFAULT);
        }
		{
            // Up_button_cloaked_2
            lv_obj_t *obj = lv_btn_create(parent_obj);
            objects.up_button_cloaked_2 = obj;
            lv_obj_set_pos(obj, 200, 275);
            lv_obj_set_size(obj, 1, 1);
            lv_obj_add_event_cb(obj, event_handler_cb_table_test_up_button_cloaked_2, LV_EVENT_ALL, flowState);
            lv_obj_clear_flag(obj, LV_OBJ_FLAG_CLICK_FOCUSABLE);
            lv_obj_set_style_border_color(obj, lv_color_hex(0xfff2f2f2), LV_PART_MAIN | LV_STATE_DEFAULT);
            lv_obj_set_style_bg_color(obj, lv_color_hex(0xfff2f2f2), LV_PART_MAIN | LV_STATE_DEFAULT);
            lv_obj_set_style_border_width(obj, 1, LV_PART_MAIN | LV_STATE_DEFAULT);
		}
        {
                    /*Fill the first column*/
                    lv_table_set_cell_value(objects.table, 0, 0, "Name");
                    lv_table_set_cell_value(objects.table, 1, 0, "Apple");
                    lv_table_set_cell_value(objects.table, 2, 0, "Banana");
                    lv_table_set_cell_value(objects.table, 3, 0, "Lemon");
                    lv_table_set_cell_value(objects.table, 4, 0, "Grape");
                    lv_table_set_cell_value(objects.table, 5, 0, "Melon");
                    lv_table_set_cell_value(objects.table, 6, 0, "Peach");
                    lv_table_set_cell_value(objects.table, 7, 0, "Nuts");
        
                     /*Set a smaller height to the table. It'll make it scrollable*/
                    lv_obj_set_height(objects.table, 240);
                    lv_obj_center(objects.table);
        
                    /*Fill the second column*/
                    // lv_table_set_cell_value(objects.table, 0, 1, "Price");
                    // lv_table_set_cell_value(objects.table, 1, 1, "$7");
                    // lv_table_set_cell_value(objects.table, 2, 1, "$4");
                    // lv_table_set_cell_value(objects.table, 3, 1, "$6");
                    // lv_table_set_cell_value(objects.table, 4, 1, "$2");
                    // lv_table_set_cell_value(objects.table, 5, 1, "$5");
                    // lv_table_set_cell_value(objects.table, 6, 1, "$1");
                    // lv_table_set_cell_value(objects.table, 7, 1, "$9");
        
                    
                    /*Add an event callback to to apply some custom drawing*/
                    //lv_obj_add_event_cb(objects.table, change_event_cb, LV_EVENT_VALUE_CHANGED, NULL);

                    lv_obj_add_event_cb(objects.table, draw_part_event_cb, LV_EVENT_DRAW_PART_BEGIN, NULL);
        }
    }
}

//  actions.c - user actions code to send the key events to the widget
void action_send_enterkey_to_table(lv_event_t *e) {
    uint32_t t = LV_KEY_ENTER;
    lv_event_send(objects.table, LV_EVENT_KEY, &t);
    
 }

 void action_send_dwnkey_to_table(lv_event_t *e) {
    uint32_t t = LV_KEY_DOWN;
    lv_event_send(objects.table, LV_EVENT_KEY, &t);
    
 }

 void action_send_upkey_to_table(lv_event_t *e) {
    uint32_t t = LV_KEY_UP;
    lv_event_send(objects.table, LV_EVENT_KEY, &t);
    
 }

Screenshot and/or video

If possible, add screenshots and/or videos about the current state.



Hello,

If I read your code right, you are using the direction buttons to move the selected cell, which is then highlighted in the draw event. I think changing the selected cell in itself does not trigger a scroll event so to speak. I have no knowledge of input using buttons but perhaps you will have to do the scrolling yourself. I think for the table, changing the selected cell does not scroll it.

lv_obj_scroll_by() will allow you to scroll the table manually by x amount of pixels.

Thanks for replying, Tinus. I don’t think the keys are the problem because when I press the up or down key it increments/decrements the row value even if cells are off the screen. I’ve also tried using the * LV_EVENT_VALUE_CHANGED Sent when a new cell is selected with keys. But that doesn’t help either.

The table documentation also has this comment. “If the width or height is set to a smaller number than the “intrinsic” size then the table becomes scrollable.” But this doesn’t work for me either.

The table is scrollable, I can tell by the little scroll bar on the right. What I’m trying to say is that the selected row value does not tell the table object to scroll any. Pressing the buttons only selects a cell.

On a touch screen it also doesn’t scroll when you select (that is: tap) on a cell that is partly out of reach. Hence why I recommend you scroll manually with lv_obj_scroll_by().

EDIT: here is an example

typedef struct {
   SCROLL_UP,
   SCROLL_DOWN,
   SCROLL_LEFT,
   SCROLL_RIGHT
};

#define SCROLL_ANIM     1  // Use scrolling animation

#if SCROLL_ANIM
static bool scrollBusy = false;
#endif

/* 
dir:  direction
dist: scroll distance in pixels
*/
static bool scrollObj(lv_obj_t* obj, const scroll_dir_t dir, uint16_t dist)
{
#if SCROLL_ANIM
    if (scrollBusy == true) { return false; }
#endif

    if (obj == NULL) { return false; }

    int32_t remaining = 0;
    bool    scrollEnd = false;

    switch (dir) {
        case SCROLL_UP:
            remaining = lv_obj_get_scroll_top(obj);
            break;
        case SCROLL_DOWN:
            remaining = lv_obj_get_scroll_bottom(obj);
            break;
        case SCROLL_LEFT:
            remaining = lv_obj_get_scroll_left(obj);
            remaining -= (remaining % 2);
            break;
        case SCROLL_RIGHT:
            remaining = lv_obj_get_scroll_right(obj);
            remaining += (remaining % 2);
            break;
        default:
            break;
    }

    if (remaining > 0)
    {
#if SCROLL_ANIM
        scrollBusy = true;
#endif
        if (remaining >= dist)
        {
            if ((remaining - dist) < SCROLL_END_TRESHOLD)
            {
                scrollEnd = true;
            }
            remaining = (int32_t)dist;
        }
        else
        {
            scrollEnd = true;
        }
    }
    else
    {
        scrollEnd = true;
    }

    switch (dir) {
        case SCROLL_UP:
            lv_obj_scroll_by(obj, 0, remaining, SCROLL_ANIM);
            break;
        case SCROLL_LEFT:
            lv_obj_scroll_by(obj, remaining, 0, SCROLL_ANIM);
            break;
        case SCROLL_DOWN:
            lv_obj_scroll_by(obj, 0, -remaining, SCROLL_ANIM);
            break;
        case SCROLL_RIGHT:
            lv_obj_scroll_by(obj, -remaining, 0, SCROLL_ANIM);
            break;
        default:
            break;
    }

    return scrollEnd;
}

You will have to figure out when the selected cell is out of reach for your display and scroll down x pixels to have it be centered, at the top, whatever

Thanks. Tinus I will use your example for the manual way of scrolling.

There is one mystery, however. If you try using the Simple Example on Table (lv_table) — LVGL documentation You can use the up/down/right/left keys on your keyboard to scroll one cell at a time and it will scroll to the next off screen cell. And there is no other code included to do that.

Really? Perhaps it does work…

Could you try setting the font of your table before you set the size?