Tab buttons in Tabview

Each tab button in the tab view corresponds to a page, and I want to use the tab button as a normal button. For example, clicking it doesn’t switch to a page in the tab view, but listens for an event that I can use as a back button to return from the tab view to a main page.

It’s possible! E.g. in lv_example_tabview_1() add this:

static void event_cb(lv_event_t * e)
{
    lv_obj_t * btns = lv_event_get_current_target(e);
    lv_obj_t * tv = lv_event_get_user_data(e);
    if(lv_btnmatrix_get_selected_btn(btns) == 1) {
        printf("Hello!\n");
        lv_btnmatrix_set_btn_ctrl(btns, lv_tabview_get_tab_act(tv), LV_BTNMATRIX_CTRL_CHECKED);
        lv_event_stop_processing(e);
    }
}

...

lv_obj_add_event_cb(lv_tabview_get_tab_btns(tabview), event_cb, LV_EVENT_VALUE_CHANGED | LV_EVENT_PREPROCESS, tabview);

1 Like

This is really nice, this will solve a big problem for me. Thank you so much.

Hello, I just tried it, but lv_event_stop_processing(e) and LV_EVENT_PREPROCESS reported errors, do I need to include any header files? I am using lvgl v8.1.0.

It’s a v8.2 feature. Can you update lvgl?

1 Like

That’s true.
Of course.
Thank you very much for your help and confusion.

The good news is that I have implemented the function of the return button through the tab button in v8.1.0, I just removed lv_event_stop_processing(e) and LV_EVENT_PREPROCESS.
Excuse me, I also wanted to ask you a question about the tab view.
Create a Tabview, the default is the first tab button, but I want the page to be in the second tab button after the Tabview is created. How to do it?
I tried using lv_btnmatrix_set_btn_ctrl(tab_btns, 1, LV_BTNMATRIX_CTRL_CHECKED), but it didn’t work.

You can do this: lv_tabview_set_act(tv, 2, LV_ANIM_ON/OFF);

1 Like

This is ok, thank you very much, let me know the function of lv_tabview_set_act(tabview, id, LV_ANIM_ON/OFF). I need to pay attention to where this function is called.

1 Like

Hi
I had been playing around with this but get several issues:

  1. I cannot use “LV_EVENT_VALUE_CHANGED | LV_EVENT_PREPROCESS” - it throws an error
  2. I don’t seem to be able to transfer the tabview pointer as user data - I tried:

static void fn_settings(lv_event_t * e) {
lv_obj_t * tabview = (lv_obj_t *) lv_event_get_user_data(e);
lv_obj_t * btns = lv_tabview_get_tab_btns(tabview);

if (lv_btnmatrix_get_selected_btn(btns) == 1) {


}

registering the function:
lv_obj_add_event_cb(lv_tabview_get_tab_btns(tabview), fn_settings, LV_EVENT_PREPROCESS, tabview);

sorry - my error messages are in local language - will try to change that

here the error messages:
error C2664: ‘_lv_event_dsc_t *lv_obj_add_event_cb(_lv_obj_t *,lv_event_cb_t,lv_event_code_t,void *)’: cannot convert argument 3 from ‘int’ to ‘lv_event_code_t’

the other thing miraculously works now …

Actually I am wondering if what I am trying to do is the best way:

I have an application with a main window and want a button in the top left corner to change to settings
In settings - and only there - I want to have tabview (on the left side)

My plan is, to use tabview all the time and hide the settings tabs when in “run” mode. I would put a button with an image on the top left tab. In run mode only that tab is visible. Pushing that button switches to the last used settings tab.

It works as planned however the reason doing things this way is to save space used by the tab header. Now I realize that I cannot easily use this space for anything because the tab itsself only has access to the right area

Hi, I have basically the same question. Unfortunately your solution seems not to work for me. I am using LVGL v9.0 and I would like to use the tabview buttons as softkeys. By default they select the tab as it is done normally.

If some conditions are met (which shall not be the point here) I would like to redraw the tabview buttons and assign other functionality (e.g. left/right or similar). In this case I want to suppress the automatic tab switch which would normally occur. How can i suppress the event of the tab switching? I tried to add the “LV_EVENT_PREPROCESS” event to the callback to make sure that my event gets called before the class default event processing. In the event_cb I then try to clear the event with “lv_event_stop_processing()”.

It didn’t work, the tabs are still getting switched. What am I doing wrong? Here is some snippet which hopefully helps understanding my intention.

static void TabChanged_event_cb(lv_event_t * e){
	lv_event_code_t code = lv_event_get_code(e);

	switch(code){
		case LV_EVENT_VALUE_CHANGED:
			if(some_condition){
				//don't switch tabs, suppress event
	    		lv_event_stop_processing(e);
			}
			else
				//switch tabs, let library handle the event
			break;
	}
}

...

/* create tabview */
tv = lv_tabview_create(lv_screen_active());

lv_obj_t* tab1 = lv_tabview_add_tab(tv, "tab1");
lv_obj_t* tab2 = lv_tabview_add_tab(tv, "tab2");
lv_obj_t* tab3 = lv_tabview_add_tab(tv, "tab3");

lv_obj_add_event_cb(tv, TabChanged_event_cb, LV_EVENT_ALL | LV_EVENT_PREPROCESS, NULL);

Any help is highly appreciated. Thanks.

Hi,

In v9 the the tab buttons are real buttons and not a button matrix. The event is also added to the buttons directly. See here lvgl/src/widgets/tabview/lv_tabview.c at master · lvgl/lvgl · GitHub

To get the buttons you can use lv_tabview_get_tab_bar(obj) and lv_obj_get_child(tabbar, idk).

Hi,

Okay, so is it possible to suppress the callback for the button in certain situation? I tried it in the following way and it still didn’t work:

static void TabBtn_event_cb(lv_event_t * e){
//button clicked
	if(some_condition){
		//don't switch tabs, suppress event
    		lv_event_stop_processing(e);
	}
	else {
		//switch tabs, let library handle the event
       }
}

...

/* create tabview */
tv = lv_tabview_create(lv_screen_active());

lv_obj_t* tab1 = lv_tabview_add_tab(tv, "tab1");
lv_obj_t* tab2 = lv_tabview_add_tab(tv, "tab2");
lv_obj_t* tab3 = lv_tabview_add_tab(tv, "tab3");

lv_obj_t* tabbar = lv_tabview_get_tab_bar(tv);
lv_obj_t* tabBtn1 = lv_obj_get_child(tabbar, 0);
lv_obj_t* tabBtn2 = lv_obj_get_child(tabbar, 1);
lv_obj_t* tabBtn3 = lv_obj_get_child(tabbar, 2);

lv_obj_add_event_cb(tabBtn1, TabBtn_event_cb, LV_EVENT_CLICKED| LV_EVENT_PREPROCESS, NULL);
lv_obj_add_event_cb(tabBtn2, TabBtn_event_cb, LV_EVENT_CLICKED| LV_EVENT_PREPROCESS, NULL);
lv_obj_add_event_cb(tabBtn3, TabBtn_event_cb, LV_EVENT_CLICKED| LV_EVENT_PREPROCESS, NULL);

By using LV_EVENT_PREPROCESS my callback gets called before the button callback attached by the library. But it looks like lv_event_stop_processing() doesn’t do what I expect it to do. I would like to suppress following event callbacks which would react to a certain event (in my case i want to stop processing LV_EVENT_CLICKED events after my callback so that the button callback from the library doesn’t even get invoked).

Thank you for your help

Oh, it’s really a regression in LVGL that lv_event_stop_processing is not working. Could you try this patch?

diff --git a/src/core/lv_obj_event.c b/src/core/lv_obj_event.c
index 0553f3d77..0d4e4abad 100644
--- a/src/core/lv_obj_event.c
+++ b/src/core/lv_obj_event.c
@@ -354,19 +354,19 @@ static lv_result_t event_send_core(lv_event_t * e)
     lv_event_list_t * list = target->spec_attr ?  &target->spec_attr->event_list : NULL;
 
     res = lv_event_send(list, e, true);
-    if(res != LV_RESULT_OK) return res;
+    if(res != LV_RESULT_OK || e->stop_processing) return res;
 
     res = lv_obj_event_base(NULL, e);
-    if(res != LV_RESULT_OK) return res;
+    if(res != LV_RESULT_OK || e->stop_processing) return res;
 
     res = lv_event_send(list, e, false);
-    if(res != LV_RESULT_OK) return res;
+    if(res != LV_RESULT_OK || e->stop_processing) return res;
 
     lv_obj_t * parent = lv_obj_get_parent(e->current_target);
     if(parent && event_is_bubbled(e)) {
         e->current_target = parent;
         res = event_send_core(e);
-        if(res != LV_RESULT_OK) return res;
+        if(res != LV_RESULT_OK || e->stop_processing || e->stop_bubbling) return res;
     }
 
     return res;

I’ve also opened a Pull request: fix(event): stop event event processing when requested by kisvegabor · Pull Request #6113 · lvgl/lvgl · GitHub

Thank you very much,

This is the feature I was looking for :slight_smile:

I would rather call it a bugfix, but I’m happy that it’s working :slight_smile: