Hardware button and Tabview

Hello everyone. I am doing my first steps with LVGL and, even if documentation is well written, I have some problems to reach my trivial target: switch among views of a tabview simply using an hardware button.
I have two sections of code, one referring to the external button and one to the tabview:

/********************************************************/
/*                    button section
/********************************************************/
void Display::display_button_init(void)
{
	//HW Button
	static lv_indev_t *indev;
	lv_indev_drv_t indev_drv;

	lv_indev_drv_init(&indev_drv);

	indev_drv.read_cb = button_read;
	indev_drv.type = LV_INDEV_TYPE_BUTTON;
	indev = lv_indev_drv_register(&indev_drv);

	lv_obj_t *btn = lv_btn_create(NULL, NULL);
	lv_obj_set_event_cb(btn, btn_event_cb);
}

void Display::display_print(lv_obj_t *tab, const string &text)
{
    lv_obj_t *label = lv_label_create(tab, NULL);
    lv_label_set_text(label, text.c_str());
}

/********************************************************/
/*                    tab section
/********************************************************/
void Display:tab(void)
{
    //Create a Tab view object
    lv_obj_t *tabview;
    tabview = lv_tabview_create(lv_scr_act(), NULL);

    //Add 3 tabs (the tabs are pages)
    lv_obj_t *tab1 = lv_tabview_add_tab(tabview, "Tab 1");
    lv_obj_t *tab2 = lv_tabview_add_tab(tabview, "Tab 2");
    lv_obj_t *tab3 = lv_tabview_add_tab(tabview, "Tab 3");

    /*Add content to the tabs*/
    display_print(tab1, "First tab");
    display_print(tab2, "Second tab");
    display_print(tab3, "Third tab");
}

bool button_read(lv_indev_drv_t *indev_drv, lv_indev_data_t *data)
{
	Button hw_btn;
    static int8_t last_btn = 0;
    int8_t btn_pr = hw_btn.is_view_to_be_changed();

    if(btn_pr > 0)
    {
		last_btn = btn_pr;
		data->state = LV_INDEV_STATE_PR;
    }
    else
       data->state = LV_INDEV_STATE_REL;

    data->btn_id = last_btn;

    return false;
}

void btn_event_cb(lv_obj_t * btn, lv_event_t event)
{
    if(event == LV_EVENT_CLICKED)
    	cout << "pressed" << endl;
}

I can correctly see tabview on my screen (obviously only the first view) and detect the my button state using the button_read callback. Simply I don’t understand how to link these two sections.
Moreover there is no call to btn_event_cb callback (cannot see “pressed” string) when I press my external button.
Could you give me any suggestion, maybe using a simple snippet of code?

Finally, i would prefer to use the “feeback_cb” instead of my read_cb in order to don’t stress CPU but, again, cannot find any example.
Thank you for your time

You might want to change your input device to KEYPAD or ENCODER (Depending on your use case) instead of BUTTON. BUTTON requires you to specify the x and y points to where you want to click, which may not be ideal for what you want to achieve.

indev_drv.type = LV_INDEV_TYPE_KEYPAD;

You need to create a group and set it with your input device.

group = lv_group_create();
lv_indev_set_group(indev, group);

Add the tabview obj to your group

lv_group_add_obj(group, tabview);

You need to return a key state to lv_indev_data_t *data. For tabview navigation, it’s LEFT/RIGHT keys to select tabs and ENTER to click.

    if(btn_pr > 0)
    {
		last_btn = btn_pr;
		data->state = LV_INDEV_STATE_PR;
        data->key = function_that_returns_a_key_state();
    }

More information about keys, groups and input devices can be found here

Thank you for your reply @nwb. I could consider keypad/encoder for navigation. Let’s consider that I need to move just in one direction (view1 -> view2 -> view3 -> view1 ->…) . Don’t you think button as input device is enough? Even if it requires a specific point (x,y), it can be whatever you want on the screen and it will be always the same.
I solved the problem I had on btn_event_cb. Now this is my code:

void Display::display_button_init(void)
{
	//UI Button
	static lv_obj_t *btn = lv_btn_create(lv_scr_act(), NULL);
	lv_obj_set_event_cb(btn, btn_event_cb);

	//HW Button
	static lv_indev_t *indev;
	lv_indev_drv_t indev_drv;
	lv_indev_drv_init(&indev_drv);

	indev_drv.read_cb = button_read;
	indev_drv.type = LV_INDEV_TYPE_BUTTON;
	indev = lv_indev_drv_register(&indev_drv);

	static lv_point_t points_array[] = { { 0, 0 } };
	lv_indev_set_button_points(indev, points_array);
}

As said in my first post, i’d like to use feedback_fb for my indev_drv, but it does not work. That’s what I tried:

void Display::display_button_init(void)
{
	//HW Button
	static lv_indev_t *indev;
	lv_indev_drv_t indev_drv;
	lv_indev_drv_init(&indev_drv);

	indev_drv.feedback_cb = btn_feedback_event_cb;
	indev_drv.type = LV_INDEV_TYPE_BUTTON;
	indev = lv_indev_drv_register(&indev_drv);

	static lv_point_t points_array[] = { { 0, 0 } };
	lv_indev_set_button_points(indev, points_array);
}

void btn_feedback_event_cb(lv_indev_drv_t *indev_drv, lv_event_t event)
{
	cout << "btn_feedback_event_cb" << endl;
    switch(event)
    {
        case LV_EVENT_PRESSED:
            printf("Pressed\n");
            break;

        case LV_EVENT_SHORT_CLICKED:
            printf("Short clicked\n");
            break;

        case LV_EVENT_CLICKED:
            printf("Clicked\n");
            break;

        case LV_EVENT_LONG_PRESSED:
            printf("Long press\n");
            break;

        case LV_EVENT_LONG_PRESSED_REPEAT:
            printf("Long press repeat\n");
            break;

        case LV_EVENT_RELEASED:
            printf("Released\n");
            break;

        default:
        	cout << "nothing" << endl;
        	break;
    }
}

I try meanwhile to understand if keypad/encoder is better than button as you suggested.
Tnx

Apologies, I misunderstood your intent.

Not sure what is your exact full use case but if its purely just to click an UI button to navigate through the tabs, it should suffice.

Based on my understanding, wouldn’t you need to read the input device data to trigger an action? Not confident on this, but I dont think your intent is possible.

The total flow if both read_cb and feedback_cb is implemented.
LVGL constantly polls for input device data -> HW Physically pressed -> read_cb triggers, input device state changes to PRESSED -> UI Button state changes to PRESSED -> input driv feedback_cb triggered.

Would appreciate it if anyone can correct me.

As written in the official documentation, feedback_cb should be an alternative to read_cb and feedback_cb should be “event driven” instead of using polling. Maybe I completely misunderstood its use.

feedback_cb is not intended to be used as a replacement to read_cb.

feedback_cb is a mechanism for LVGL to provide data to the input device, so it can find out about events it is sending to objects. It cannot be used to read data from the input device.

The intended use case is to provide the user with feedback when a touch, etc. occurs, such as an audible click.

Thank you for your explanation