Drop Down Lists not handling lists with more than 256 items properly

Description

We have a dropdown list that can contain up to 2000 items in it, but when paging down it reaches 256 and the scrollbar indicates it’s at the end of the list but you can still page down and the item is displayed in main part of the dropdown but the dropdown list part stays at 256.

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

I can reproduce this on my Linux PC using SDL and on the actual target which is SAMA5D1 board running Linux.

What LVGL version are you using?

v8.3

What do you want to achieve?

I would like to be able to page all the way down to the end.

What have you tried so far?

I can’t find any solution. I’ve checked through the configuration files but can’t find any limit on drop down lists.

Code to reproduce

Here is example code to reproduce on a Linux PC, it creates 2000 items, 0000, 0001, … 1999 and uses left and right arrow keys to page up and down by 10.

#include "lvgl.h"
#include "lv_drivers/sdl/sdl.h"

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>

#define ITEM_COUNT 2000
#define SDL_HOR_RES 320
#define SDL_VER_RES 240

static lv_color_t buf1[SDL_HOR_RES * SDL_VER_RES];
static lv_disp_draw_buf_t draw_buf;
static lv_indev_drv_t indev_drv;
static lv_group_t *group;

static void keyboard_event_cb(lv_event_t *e)
{
    lv_event_code_t code = lv_event_get_code(e);
    lv_obj_t *cb = lv_event_get_target(e);

    if (code == LV_EVENT_KEY) {
        uint32_t key = lv_event_get_key(e);
        switch (key) {
            case LV_KEY_UP:
                lv_dropdown_set_selected(cb, lv_dropdown_get_selected(cb) - 1);
                break;
            case LV_KEY_DOWN:
                lv_dropdown_set_selected(cb, lv_dropdown_get_selected(cb) + 1);
                break;
            case LV_KEY_LEFT: {
                int sel = lv_dropdown_get_selected(cb) - 10;
                lv_dropdown_set_selected(cb, sel < 0 ? 0 : sel);
                break;
            }
            case LV_KEY_RIGHT: {
                int sel = lv_dropdown_get_selected(cb) + 10;
                if (sel >= ITEM_COUNT) sel = ITEM_COUNT - 1;
                lv_dropdown_set_selected(cb, sel);
                break;
            }
        }
    }
}

static char options[(ITEM_COUNT * 5)]; // Each item is "0000\n" (4 chars + 1 for '\0')

static void create_combobox(void)
{
    lv_obj_t *cb = lv_dropdown_create(lv_scr_act());
    lv_obj_set_size(cb, 150, 40);
    lv_obj_align(cb, LV_ALIGN_CENTER, 0, 0);


    for (int i = 0; i < ITEM_COUNT; i++) {
        snprintf(&options[i*5], 6, "%04d\n", i);
    }
    options[(ITEM_COUNT * 5)-1] = '\0';

    lv_dropdown_set_options_static(cb, options);
    lv_obj_add_event_cb(cb, keyboard_event_cb, LV_EVENT_KEY, NULL);

    // Add dropdown to the group to receive keyboard input
    lv_group_add_obj(group, cb);
    lv_group_focus_obj(cb);
}

int main(void)
{
    lv_init();


    /* Use the 'sdl' driver which creates window on PC's monitor to simulate a display*/
    sdl_init();

    lv_disp_draw_buf_init(&draw_buf, buf1, NULL, SDL_HOR_RES * SDL_VER_RES);

    static lv_disp_drv_t disp_drv;
    lv_disp_drv_init(&disp_drv);

    disp_drv.draw_buf = &draw_buf;
    disp_drv.flush_cb = sdl_display_flush;
    disp_drv.hor_res = SDL_HOR_RES;
    disp_drv.ver_res = SDL_VER_RES;
    lv_disp_drv_register(&disp_drv);

    lv_indev_drv_init(&indev_drv);
    indev_drv.type = LV_INDEV_TYPE_KEYPAD;
    indev_drv.read_cb = sdl_keyboard_read;
    lv_indev_t *kb_indev = lv_indev_drv_register(&indev_drv);

    // Create a group for keyboard input
    group = lv_group_create();
    lv_indev_set_group(kb_indev, group);

    create_combobox();

    while(!sdl_quit_qry) {
        lv_timer_handler();
        usleep(5000); // Sleep for 5 milliseconds
    }

    return 0;
}

uint32_t
custom_tick_get(void)
{
    static uint64_t start_ms = 0;
    struct timeval tv_now;
    uint64_t now_ms;
    uint32_t time_ms;

    if (start_ms == 0) {
        struct timeval tv_start;
        gettimeofday(&tv_start, NULL);
        start_ms = (tv_start.tv_sec * 1000000 + tv_start.tv_usec) / 1000;
    }

    gettimeofday(&tv_now, NULL);
    now_ms = (tv_now.tv_sec * 1000000 + tv_now.tv_usec) / 1000;

    time_ms = now_ms - start_ms;
    return time_ms;
}

I managed to fix it, it was the maximum height on the label being used to display the items, I can only get 1000 items but I think this is due to the height being an lv_coord_t or int16_t.

static lv_obj_t * get_label(const lv_obj_t * obj)
{
    lv_dropdown_t * dropdown = (lv_dropdown_t *)obj;
    if(dropdown->list == NULL) return NULL;

    return lv_obj_get_child(dropdown->list, 0);
}

static void create_combobox(void)
{
    ...
    lv_obj_t * label = get_label(cb);
    lv_obj_set_style_max_height(label, 32000, LV_PART_MAIN);
    ...
}

To get 2000 working you need to define LV_USE_LARGE_COORD to 1 in lv_conf.h

/Extend the default -32k…32k coordinate range to -4M…4M by using int32_t for coordinates instead of int16_t/

#define LV_USE_LARGE_COORD 1

1 Like