How to set a new event-callback for tabview

Description

how to set a new event callback for a tabview instead of changing the code of “tab_btnm_event_cb” in file “lv_tabview.c”

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

What do you want to achieve?

For example, a tabview consists 2 tab, each tab includes some objects, when i first run the project, it shows as what I initialized. If I clicked or input some char(such as “Hello!”) in one tab(call it A tab), which includes a text_area, then I switch to another tab, then switch back to the A tab, I prefer to see the object in A tab is shown now as I initialized(the text_area is initialize for no words). How to achieve what I want?

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:

/*You code here*/

Screenshot and/or video

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

You should not change event cb for the internal tabview’s btns
( should not change tab_btnm_event_cb in file lv_tabview.c to another.)
Because it has many process in tab_btnm_event_cb for tabview’s btns.

But if you want to know tab’s name at current
when tabview is clicked,
you can run the following code.

  lv_obj_t* tabview = lv_tabview_create(lv_scr_act(), NULL);

    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");

    lv_obj_set_event_cb(tabview, [](lv_obj_t* tabview, lv_event_t event){
      if(event != LV_EVENT_VALUE_CHANGED) return;
      
      lv_tabview_ext_t* ext = (lv_tabview_ext_t*) lv_obj_get_ext_attr(tabview);
      const char* tab_name = ext->tab_name_ptr[lv_tabview_get_tab_act(tabview)];
      
      printf("Current Active Tab : %s\n", tab_name);
    });
1 Like

so how to achieve what I want? when I know the tab’s name, how to add my event_cb to the tabview?

thanks, i found your method works.

one more question, if I clicked a tab(B), it will switch one tab(A) to the other tab(B), when this situation happens, I prefer tab A to show a message box for users to confirm whether to turn to the tab B, just like their is a pause when 2 tabs switch, how to achieve it?

Scenario :

When a Tabview change from tab A to tab B,
a message box will display for confirming to change.
If “OK” , the tabview will change from tab A to tab B,
If “Cancel”, the tabview will return to tab A.

The Code

typedef struct {
  lv_tabview_ext_t tview;   // ancestor tabview ext
  lv_obj_t* locker;         // for lock tabview when mbox is shown
  lv_obj_t* mbox;           // message box
  int8_t    last_tab_id;    // last tab_id is clicked
} my_tabview_ext_t;


lv_obj_t* my_tabview_create(void){
  lv_obj_t* tabview = lv_tabview_create(lv_scr_act(), NULL);
    my_tabview_ext_t* ext = (my_tabview_ext_t*) lv_obj_allocate_ext_attr(tabview, sizeof(my_tabview_ext_t));
      ext->last_tab_id = 0;
      ext->locker = lv_obj_create(tabview, NULL);
        lv_obj_set_click(ext->locker, true);
        lv_obj_set_size(ext->locker, lv_obj_get_width(tabview), lv_obj_get_height(tabview));
        lv_obj_set_style(ext->locker, &lv_style_transp);
        lv_obj_set_hidden(ext->locker, true);
  
      static const char * mbox_btns[] ={"OK", "Cancel", ""};
      ext->mbox = lv_mbox_create(tabview, NULL);
        lv_mbox_set_text(ext->mbox, "Do you want to change tab A to tab B?");
        lv_mbox_add_btns(ext->mbox, mbox_btns);
        lv_obj_set_width(ext->mbox, 200);
        lv_obj_align(ext->mbox, NULL, LV_ALIGN_CENTER, 0, 0); /*Align to the corner*/
        lv_obj_set_hidden(ext->mbox,true);
        lv_obj_set_event_cb(ext->mbox, [](lv_obj_t* mbox, lv_event_t event){
          if(event != LV_EVENT_VALUE_CHANGED) return;

          lv_obj_t* tabview = lv_obj_get_parent(mbox);
          my_tabview_ext_t* ext = (my_tabview_ext_t*) lv_obj_get_ext_attr(tabview);
          
          const char* button_name = lv_mbox_get_active_btn_text(mbox);
          if( strcmp(button_name, "OK")==0){
            ext->last_tab_id = lv_tabview_get_tab_act(tabview);
          }else{   
            lv_tabview_set_tab_act(tabview, ext->last_tab_id, false);
          }

          // hidden mbox and tabview's locker
          lv_obj_set_hidden(mbox, true);
          lv_obj_set_hidden(ext->locker, true);
        });
        
    lv_obj_set_event_cb(tabview, [](lv_obj_t* tabview, lv_event_t event){
      if(event != LV_EVENT_VALUE_CHANGED) return;

      my_tabview_ext_t* ext = (my_tabview_ext_t*) lv_obj_get_ext_attr(tabview);
      const char* tab_name = ext->tview.tab_name_ptr[lv_tabview_get_tab_act(tabview)];
      
      printf("Current Active Tab : %s\n", tab_name);

      int cur_tab_id = lv_tabview_get_tab_act(tabview);
      if(ext->last_tab_id != cur_tab_id ){
        if( ext->last_tab_id ==0 && cur_tab_id == 1) {
          lv_obj_set_hidden(ext->locker,false);  // for user can't click any thing on tabview except mbox
          lv_obj_set_hidden(ext->mbox,false);    // shown mbox
        }else{
          ext->last_tab_id = cur_tab_id;
        }
      }
    });
  return tabview;
}

Your Code


void setup() {
   // ..... your lvgl init first 

  lv_obj_t* tabview = my_tabview_create();
      lv_obj_t *tab1 = lv_tabview_add_tab(tabview, "Tab A");
      lv_obj_t *tab2 = lv_tabview_add_tab(tabview, "Tab B");
      lv_obj_t *tab3 = lv_tabview_add_tab(tabview, "Tab C");

   // when you want to delete the tabview
   //  by lv_obj_del(tabview);
   //  the tabview will be deleted all (mbox, any tab  ...etc... too)
}

void loop(){
   // ..... your lvgl loop here
}

I run the code you offered. the effect of your code is not what I expected. When I want to change Tab A to Tab B, it show the message box, and infact, the Tab B is shown, then if I cllicked “Cancel”, it return to the Tab A, it has a slide effect between the change. What I expect is when I click Tab B, it should not change to Tab B(the back ground should still in Tab A), only if I click “OK”, it change to Tab B.

Just like their exsists a pause, while in pause, tab should still be Tab A.

typedef struct {
lv_tabview_ext_t tview; // ancestor tabview ext
lv_obj_t* locker; // for lock tabview when mbox is shown
lv_obj_t* mbox; // message box
int8_t last_tab_id; // last tab_id is clicked
} my_tabview_ext_t;

static int cur_tab_id; //this variate should be static

lv_obj_t* my_tabview_create(void)
{
lv_obj_t* tabview = lv_tabview_create(lv_scr_act(), NULL);
my_tabview_ext_t* ext = (my_tabview_ext_t*) lv_obj_allocate_ext_attr(tabview, sizeof(my_tabview_ext_t));
ext->last_tab_id = 0;
ext->locker = lv_obj_create(tabview, NULL);
lv_obj_set_click(ext->locker, true);
lv_obj_set_size(ext->locker, lv_obj_get_width(tabview), lv_obj_get_height(tabview));
lv_obj_set_style(ext->locker, &lv_style_transp);
lv_obj_set_hidden(ext->locker, true);

  static const char * mbox_btns[] ={"OK", "Cancel", ""};
  ext->mbox = lv_mbox_create(tabview, NULL);
    lv_mbox_set_text(ext->mbox, "Do you want to change tab A to tab B?");
    lv_mbox_add_btns(ext->mbox, mbox_btns);
    lv_obj_set_width(ext->mbox, 200);
    lv_obj_align(ext->mbox, NULL, LV_ALIGN_CENTER, 0, 0); /*Align to the corner*/
    lv_obj_set_hidden(ext->mbox,true);
    lv_obj_set_event_cb(ext->mbox, [](lv_obj_t* mbox, lv_event_t event){
      if(event != LV_EVENT_VALUE_CHANGED) return;

      lv_obj_t* tabview = lv_obj_get_parent(mbox);
      my_tabview_ext_t* ext = (my_tabview_ext_t*) lv_obj_get_ext_attr(tabview);
      
      const char* button_name = lv_mbox_get_active_btn_text(mbox);
      if( strcmp(button_name, "OK")==0){
        //ext->last_tab_id = lv_tabview_get_tab_act(tabview);            //change code
         ext->last_tab_id = cur_tab_id;                                         
         lv_tabview_set_tab_set_act(tabview, cur_tab_id, false);
      }else{   
        lv_tabview_set_tab_act(tabview, ext->last_tab_id, false);
      }

      // hidden mbox and tabview's locker
      lv_obj_set_hidden(mbox, true);
      lv_obj_set_hidden(ext->locker, true);
    });
    
lv_obj_set_event_cb(tabview, [](lv_obj_t* tabview, lv_event_t event){
  if(event != LV_EVENT_VALUE_CHANGED) return;

  my_tabview_ext_t* ext = (my_tabview_ext_t*) lv_obj_get_ext_attr(tabview);
  const char* tab_name = ext->tview.tab_name_ptr[lv_tabview_get_tab_act(tabview)];
  
  printf("Current Active Tab : %s\n", tab_name);

  cur_tab_id = lv_tabview_get_tab_act(tabview);
  if(ext->last_tab_id != cur_tab_id ){
    if( ext->last_tab_id ==0 && cur_tab_id == 1) {
   
    lv_tabview_set_tab_act(tabview, 0, LV_ANIM_ON);          //new code

     lv_obj_set_hidden(ext->locker,false);  // for user can't click any thing on tabview except mbox
      lv_obj_set_hidden(ext->mbox,false);    // shown mbox
    }else{
      ext->last_tab_id = cur_tab_id;
    }
  }
});

return tabview;
}

typedef struct {
lv_tabview_ext_t tview; // ancestor tabview ext
lv_obj_t* locker; // for lock tabview when mbox is shown
lv_obj_t* mbox; // message box
int8_t last_tab_id; // last tab_id is clicked
} my_tabview_ext_t;

static int cur_tab_id; //this variate should be static

lv_obj_t* my_tabview_create(void)
{
lv_obj_t* tabview = lv_tabview_create(lv_scr_act(), NULL);
my_tabview_ext_t* ext = (my_tabview_ext_t*) lv_obj_allocate_ext_attr(tabview, sizeof(my_tabview_ext_t));
ext->last_tab_id = 0;
ext->locker = lv_obj_create(tabview, NULL);
lv_obj_set_click(ext->locker, true);
lv_obj_set_size(ext->locker, lv_obj_get_width(tabview), lv_obj_get_height(tabview));
lv_obj_set_style(ext->locker, &lv_style_transp);
lv_obj_set_hidden(ext->locker, true);

static const char * mbox_btns[] ={"OK", "Cancel", ""};
      ext->mbox = lv_mbox_create(tabview, NULL);
        lv_mbox_set_text(ext->mbox, "Do you want to change tab A to tab B?");
        lv_mbox_add_btns(ext->mbox, mbox_btns);
        lv_obj_set_width(ext->mbox, 200);
        lv_obj_align(ext->mbox, NULL, LV_ALIGN_CENTER, 0, 0); /*Align to the corner*/
        lv_obj_set_hidden(ext->mbox,true);
        lv_obj_set_event_cb(ext->mbox, [](lv_obj_t* mbox, lv_event_t event){
          if(event != LV_EVENT_VALUE_CHANGED) return;

          lv_obj_t* tabview = lv_obj_get_parent(mbox);
          my_tabview_ext_t* ext = (my_tabview_ext_t*) lv_obj_get_ext_attr(tabview);
          
          const char* button_name = lv_mbox_get_active_btn_text(mbox);
          if( strcmp(button_name, "OK")==0){
            //ext->last_tab_id = lv_tabview_get_tab_act(tabview);
            ext->last_tab_id = cur_tab_id;
            lv_tabview_set_tab_set_act(tabview, cur_tab_id, false);
          }else{   
            lv_tabview_set_tab_act(tabview, ext->last_tab_id, false);
          }

          // hidden mbox and tabview's locker
          lv_obj_set_hidden(mbox, true);
          lv_obj_set_hidden(ext->locker, true);
        });
        
    lv_obj_set_event_cb(tabview, [](lv_obj_t* tabview, lv_event_t event){
      if(event != LV_EVENT_VALUE_CHANGED) return;

      my_tabview_ext_t* ext = (my_tabview_ext_t*) lv_obj_get_ext_attr(tabview);
      const char* tab_name = ext->tview.tab_name_ptr[lv_tabview_get_tab_act(tabview)];
      
      printf("Current Active Tab : %s\n", tab_name);

      cur_tab_id = lv_tabview_get_tab_act(tabview);
      if(ext->last_tab_id != cur_tab_id ){
        if( ext->last_tab_id ==0 && cur_tab_id == 1) {

         lv_tabview_set_tab_act(tabview, 0, LV_ANIM_ON);
          lv_obj_set_hidden(ext->locker,false);  // for user can't click any thing on tabview except mbox
          lv_obj_set_hidden(ext->mbox,false);    // shown mbox
        }else{
          ext->last_tab_id = cur_tab_id;
        }
      }
    });
  return tabview;
 }

I adjust the code, and now it can achieve what I expect. You can try it to see the effect.

Great!
Set don’t move first by

lv_tabview_set_tab_act(tabview, 0, LV_ANIM_ON);

and set changing to the tab after that when user click “OK”/“Cancel” on the message box.

hi, I have a new question about tabview.:sweat_smile: my code is showed below:


....

lv_obj_t* page = lv_page_create(tab3, NULL);
lv_page_set_sb_mode(page, LV_SB_MODE_OFF);

static lv_style_t style_page;
lv_style_copy(&style_page, &lv_style_transp);
lv_page_set_style(page, LV_PAGE_STYLE_BG, &style_page);
lv_obj_set_size(page, 880, 400);
lv_obj_align(page, tab3, LV_ALIGN_IN_BOTTOM_RIGHT, 0, 0);

....

when I align the page to its parent tab3, I found their will show the scrollbar, just like the picture I uploaded.

how to disable the scrollbar of the tab. Instead of ajust the x/y coordinate shift after alignment.

Eash tab when you add to a tabview, it is a page.

https://docs.littlevgl.com/en/html/object-types/tabview.html#_CPPv418lv_tabview_add_tabP8lv_obj_tPKc

Then you act to any tab like a page.
If you don’t want the tab disable the scrollbar.
set it like this.

lv_page_set_sb_mode(tab3, LV_SB_MODE_OFF);

And more over that as my experience,
for more suitable for using, you may set the tab’s padding to 0.
( padding.top, right, left, bottom at LV_PAGE_STYLE_SCRL )

hi, I found a new question about tabview:rofl:
I create a tabview, and add 5 buttons, then I discover the thin rectangle on indicating the current tab don’t align with the button from Tab 3;

lv_obj_t* Create_Tabview(lv_obj_t* parent, lv_coord_t w, lv_coord_t h)
{
    /* tabview初始化 */
    Tabview_Style_Init();

    /* 创建tabview */
    lv_obj_t * tabview = lv_tabview_create(parent, NULL);
    lv_obj_set_size(tabview, w, h);

    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");
    lv_obj_t* tab4 = lv_tabview_add_tab(tabview, "Tab 4");
    lv_obj_t* tab5 = lv_tabview_add_tab(tabview, "Tab 5");

    lv_page_set_sb_mode(tab1, LV_SB_MODE_OFF);
    lv_page_set_sb_mode(tab2, LV_SB_MODE_OFF);
    lv_page_set_sb_mode(tab3, LV_SB_MODE_OFF);
    lv_page_set_sb_mode(tab4, LV_SB_MODE_OFF);
    lv_page_set_sb_mode(tab5, LV_SB_MODE_OFF);

    /* 设置tabview的样式 */
    lv_tabview_set_style(tabview, LV_TABVIEW_STYLE_BG, &style_tv_bg);
    lv_tabview_set_style(tabview, LV_TABVIEW_STYLE_INDIC, &style_tv_indic);
    lv_tabview_set_style(tabview, LV_TABVIEW_STYLE_BTN_BG, &style_tv_btn_bg);
    lv_tabview_set_style(tabview, LV_TABVIEW_STYLE_BTN_REL, &style_tv_btn_rel);
    lv_tabview_set_style(tabview, LV_TABVIEW_STYLE_BTN_PR, &style_tv_btn_pr);
    lv_tabview_set_style(tabview, LV_TABVIEW_STYLE_BTN_TGL_REL, &style_tv_btn_tgl_rel);
    lv_tabview_set_style(tabview, LV_TABVIEW_STYLE_BTN_TGL_PR, &style_tv_btn_tgl_pr);

    //lv_obj_set_event_cb(tabview, tabview_event_cb);

    Tab1_create(tab1);
    Tab2_create(tab2);
    Tab3_create(tab3);
    Tab4_create(tab4);
    Tab5_create(tab5);

    return tabview;
}

as shown in the picture

how to achieve this effect?

You should input more information / more detail like by the forum’s issue-frame.
At your detail and last code, it seems not to be introduced to the point of the issue.


Description

What MCU/Processor/Board and compiler 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:

/*You code here*/

Screenshot and/or video

1 Like

hi, I adjust my code, and now the effect is nearly what I expect. now, I still have 2 problem.

1、as the picture I uploaded, the thin rectangle on indicating the current tab don’t align with the tab button accurately, see the circle in picture.
2、how to set the tab button width, I read the doc it should to set the height of header is calculated like: font height and padding.top and padding.bottom from LV_TABVIEW_STYLE_BTN_REL + padding.top and padding bottom from LV_TABVIEW_STYLE_BTN_BG* . But I found it don’t work when I set the buttons positon in the left.

Here is my code:


...
static bool Tabview_Style_Init(void)
{
    /* 初始化tabview的背景样式 */
    lv_style_copy(&style_tv_bg, &lv_style_plain);
    style_tv_bg.body.padding.top = 0;
    style_tv_bg.body.padding.bottom = 0;
    style_tv_bg.body.padding.left = 0;
    style_tv_bg.body.padding.right = 0;
    style_tv_bg.body.main_color = TAB_BG_COLOR;
    style_tv_bg.body.grad_color = TAB_BG_COLOR;

    /* 初始化tabview的btn指示框样式 */
    lv_style_copy(&style_tv_indic, &lv_style_plain_color);
    style_tv_indic.body.padding.top = 0;
    style_tv_indic.body.padding.bottom = 0;
    style_tv_indic.body.padding.left = 0;
    style_tv_indic.body.padding.right = 0;
    style_tv_indic.body.padding.inner = 0;

    /* 初始化tabview的button背景样式 */
    lv_style_copy(&style_tv_btn_bg, &lv_style_transp);
    style_tv_btn_bg.body.padding.left = 0;
    style_tv_btn_bg.body.padding.top = 0;
    style_tv_btn_bg.body.padding.bottom = 0;
    style_tv_btn_bg.body.padding.inner = 2;

    /* 初始化tabview的button释放样式 */
    lv_style_copy(&style_tv_btn_rel, &lv_style_btn_rel);
    style_tv_btn_rel.body.radius = 0;
    style_tv_btn_rel.body.border.part = LV_BORDER_NONE;
    style_tv_btn_rel.body.main_color = BTN_MAIN_COLOR;
    style_tv_btn_rel.body.grad_color = BTN_GRAD_COLOR;
    style_tv_btn_rel.text.color = BTN_TEXT_COLOR_REL;

    /* 初始化tabview的button按下样式 */
    lv_style_copy(&style_tv_btn_pr, &lv_style_btn_pr);
    style_tv_btn_pr.body.radius = 0;
    style_tv_btn_pr.body.border.part = LV_BORDER_NONE;
    style_tv_btn_pr.body.main_color = BTN_MAIN_COLOR_TGL;
    style_tv_btn_pr.body.grad_color = BTN_GRAD_COLOR_TGL;
    style_tv_btn_pr.body.opa = LV_OPA_100;
    style_tv_btn_pr.text.color = BTN_TEXT_COLOR_TGL;

    /* 初始化tabview的button切换释放样式 */
    lv_style_copy(&style_tv_btn_tgl_rel, &lv_style_btn_tgl_rel);
    style_tv_btn_tgl_rel.body.radius = 0;
    style_tv_btn_tgl_rel.body.border.part = LV_BORDER_NONE;
    style_tv_btn_tgl_rel.body.main_color = BTN_MAIN_COLOR_TGL;
    style_tv_btn_tgl_rel.body.grad_color = BTN_GRAD_COLOR_TGL;
    style_tv_btn_tgl_rel.body.opa = LV_OPA_100;
    style_tv_btn_tgl_rel.text.color = BTN_TEXT_COLOR_TGL;

    /* 初始化tabview的button切换按下样式 */
    lv_style_copy(&style_tv_btn_tgl_pr, &lv_style_btn_tgl_pr);
    style_tv_btn_tgl_pr.body.radius = 0;
    style_tv_btn_tgl_pr.body.border.part = LV_BORDER_NONE;

    return true;
}

lv_obj_t* Create_Tabview(lv_obj_t* parent, lv_coord_t w, lv_coord_t h)
{
    /* tabview初始化 */
    Tabview_Style_Init();

    /* 创建tabview */
    lv_obj_t * tabview = lv_tabview_create(parent, NULL);
    lv_obj_set_size(tabview, w, h);
    lv_tabview_set_btns_pos(tabview, LV_TABVIEW_BTNS_POS_LEFT);

    lv_obj_t* tab1 = lv_tabview_add_tab(tabview, " 系统\n结构图");
    lv_obj_t* tab2 = lv_tabview_add_tab(tabview, "实时\n数据");
    lv_obj_t* tab3 = lv_tabview_add_tab(tabview, "报警\n状态");
    lv_obj_t* tab4 = lv_tabview_add_tab(tabview, "数据\n查询");
    lv_obj_t* tab5 = lv_tabview_add_tab(tabview, "系统\n设置");

    TabAttribute_Init(tab1);
    TabAttribute_Init(tab2);
    TabAttribute_Init(tab3);
    TabAttribute_Init(tab4);
    TabAttribute_Init(tab5);

    /* 设置tabview的样式 */
    lv_tabview_set_style(tabview, LV_TABVIEW_STYLE_BG, &style_tv_bg);
    lv_tabview_set_style(tabview, LV_TABVIEW_STYLE_INDIC, &style_tv_indic);
    lv_tabview_set_style(tabview, LV_TABVIEW_STYLE_BTN_BG, &style_tv_btn_bg);
    lv_tabview_set_style(tabview, LV_TABVIEW_STYLE_BTN_REL, &style_tv_btn_rel);
    lv_tabview_set_style(tabview, LV_TABVIEW_STYLE_BTN_PR, &style_tv_btn_pr);
    lv_tabview_set_style(tabview, LV_TABVIEW_STYLE_BTN_TGL_REL, &style_tv_btn_tgl_rel);
    lv_tabview_set_style(tabview, LV_TABVIEW_STYLE_BTN_TGL_PR, &style_tv_btn_tgl_pr);

    //lv_obj_set_event_cb(tabview, tabview_event_cb);

    Tab1_create(tab1);
    Tab2_create(tab2);
    Tab3_create(tab3);
    Tab4_create(tab4);
    Tab5_create(tab5);

    return tabview;
}

...

and it’s the effect picture:

To set width you should use:

    style_tv_btn_rel.body.padding.left = 20;
    style_tv_btn_rel.body.padding.right = 20;

I’ve fixed the indicator position in dev-6.1

1 Like

Can you give the link to the 6.1 version?