Lv_table - Sticky header, is it possible?

Description

I’d like to create a table that has a sticky header. Ie the content other than the header can be scrollerd. Is this possible

@kisvegabor, @embeddedt, @pete-pjb, @bader do you think this is possible? Thanks!

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

Simulator/Linux (PI)

What LVGL version are you using?

8.3

What do you want to achieve?

As described

What have you tried so far?

Review docs/forum

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.

None as of yet

Screenshot and/or video

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

I was thinking about it to add this feature, but I realized that the sticky header can be achieved by creating two tables:

  1. a table with one row, it will be the header
  2. a table with the real content below the header

What do you think?

I think that (based on my current table style) it would look rather strange due to the rounding on the corners.

I think it would be far better if it was baked in and had a simple API to enable it.

lv_table_set_StickyHeader(table, numberofheaderrows)

Or something along those lines.

What do you think?

You can have a rounded gray lv_obj as parent and 2 mostly transparent tables.

We should support the same horizontally too.

Wow this will be nice!

If you could that would be awesome!!

Hello.
Perhaps my solution is not optimal, but I use this code to create a table with fixed headers.
I hope this helps someone.

#include <stdio.h>
#include <stdlib.h>
#include “…/MediaResource.h”
#include “lvgl/lvgl.h”
//#include “…/SystemConfig.h”
//#include “…/My_LVGL.h”
#include “Table.h”

#define SIZE_X_CELL 60
#define SIZE_Y_HEADER_H 30
#define SIZE_X_HEADER_V 30
#define POS_X_HEADER_H 45
#define POS_Y_HEADER_V 32
#define POS_X_TABLE 30
#define POS_Y_TABLE 31

//******************************************************************
struct
{
lv_obj_t *PanelDecor; // декоративная панелька
lv_obj_t *PanelTable; // панелька с таблицей
lv_obj_t *TableMain; // основная таблица
lv_obj_t *Header_H; // таблица заголовок горизонтальный
lv_obj_t *Panel_H; // таблица заголовок горизонтальный
lv_obj_t *Header_V; // таблица заголовок горизонтальный
lv_obj_t *Panel_V; // таблица заголовка горизонтального
int Rows;
int Cols;
}Table = {0};
static void _cb_Table(lv_event_t * e)
{
lv_event_code_t event_code = lv_event_get_code(e);
lv_obj_t * obj = lv_event_get_target(e);
switch(event_code)
{
case LV_EVENT_SHORT_CLICKED: {break;}
case LV_EVENT_LONG_PRESSED: {break;}
case LV_EVENT_PRESSING:
{
lv_indev_t * indev = lv_indev_get_act();
if(indev == NULL) return;

		lv_point_t vect;
		lv_indev_get_vect(indev, &vect);
		lv_coord_t x_table = lv_obj_get_x(obj);
		lv_coord_t y_table = lv_obj_get_y(obj);
		lv_coord_t x_header_h = lv_obj_get_x(Table.Header_H);
		lv_coord_t y_header_h = lv_obj_get_y(Table.Header_H);
		lv_coord_t x_header_v = lv_obj_get_x(Table.Header_V);
		lv_coord_t y_header_v = lv_obj_get_y(Table.Header_V);
		
		x_table += vect.x;
		x_header_h += vect.x; 
		if(x_table >=0) 
		{
			x_table = 0;
			x_header_h = 0; 
		}
		int limit = -((Table.Cols * SIZE_X_CELL)/2 + SIZE_X_CELL/2)  ;
		if(x_table < limit) 
		{
			x_table = limit;
			x_header_h = limit; 
		}
		
		y_table += vect.y;
		y_header_v += vect.y; 
		limit = -(Table.Rows *SIZE_Y_HEADER_H)/2;
		if(y_table >= 0) 
		{
			y_table = 0;
			y_header_v = 0; 
		}
		if(y_table < limit)
		{
			y_table = limit;
			y_header_v = limit; 
		}
		
		lv_obj_set_pos(obj, x_table, y_table);	
		lv_obj_set_pos(Table.Header_H, x_header_h, y_header_h);	
		lv_obj_set_pos(Table.Header_V, x_header_v, y_header_v);	
		break;
	}
}

}
//***********************
void CrateTable(lv_obj_t *PanelMain, int rows, int cols)
{
char Buf_Print[100];
Table.Cols = cols;
Table.Rows = rows;
if(Table.PanelDecor) // удалить старую таблицу
{
lv_obj_del(Table.PanelDecor);
}
// панель декоративная
Table.PanelDecor = lv_obj_create(PanelMain);
lv_obj_set_height(Table.PanelDecor, 254);
lv_obj_set_width(Table.PanelDecor, 300);
lv_obj_set_x(Table.PanelDecor, 10);
lv_obj_set_y(Table.PanelDecor, 25);
lv_obj_clear_flag(Table.PanelDecor, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_set_style_bg_color(Table.PanelDecor, lv_color_hex(0x051d2f), LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_border_width(Table.PanelDecor, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_radius(Table.PanelDecor, 12, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_left(Table.PanelDecor, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_right(Table.PanelDecor, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_top(Table.PanelDecor, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_bottom(Table.PanelDecor, 0, LV_PART_MAIN | LV_STATE_DEFAULT);

    // панель прозрачная с таблицей создается первой что бы быть внизу
Table.PanelTable = lv_obj_create(Table.PanelDecor);
lv_obj_set_height(Table.PanelTable, 800); // !!! изменить должна быть больше видимой области
lv_obj_set_width(Table.PanelTable, 800); // !!! изменить
lv_obj_set_x(Table.PanelTable, POS_X_TABLE);
lv_obj_set_y(Table.PanelTable, POS_Y_TABLE);
lv_obj_clear_flag(Table.PanelTable, LV_OBJ_FLAG_SCROLLABLE);		      
lv_obj_set_style_bg_opa(Table.PanelTable, 0, LV_PART_MAIN | LV_STATE_DEFAULT); // прозрачная панель
lv_obj_set_style_border_width(Table.PanelTable, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_radius(Table.PanelTable, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_left(Table.PanelTable, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_right(Table.PanelTable, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_top(Table.PanelTable, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_bottom(Table.PanelTable, 0, LV_PART_MAIN | LV_STATE_DEFAULT);

// собственно таблица
Table.TableMain = lv_table_create(Table.PanelTable);
lv_obj_set_x(Table.TableMain, 0);
lv_obj_set_y(Table.TableMain, 0);
lv_obj_set_align(Table.TableMain, LV_ALIGN_TOP_LEFT);
lv_obj_set_style_border_side(Table.TableMain, LV_BORDER_SIDE_FULL,  LV_PART_ITEMS);
lv_obj_set_style_border_width(Table.TableMain, 1, LV_PART_ITEMS);	
lv_obj_set_style_border_width(Table.TableMain, 1, LV_PART_MAIN);	
lv_obj_set_style_border_color(Table.TableMain, lv_color_hex(0x122b3e), LV_PART_ITEMS);
lv_obj_set_style_border_color(Table.TableMain, lv_color_hex(0x122b3e), LV_PART_MAIN);
lv_obj_set_style_bg_color(Table.TableMain, lv_color_hex(0x051d2f), LV_PART_ITEMS | LV_STATE_DEFAULT);
lv_obj_set_style_bg_color(Table.TableMain, lv_color_hex(0x1d80ca), LV_PART_ITEMS | LV_STATE_FOCUSED | LV_STATE_PRESSED);
lv_obj_add_flag(Table.TableMain, LV_OBJ_FLAG_CLICKABLE);      
lv_obj_add_flag(Table.TableMain, LV_OBJ_FLAG_EVENT_BUBBLE);     
lv_obj_set_style_text_font(Table.TableMain, &RubicMedium16, LV_PART_ITEMS);
lv_obj_set_style_text_align(Table.TableMain, LV_ALIGN_CENTER, LV_PART_ITEMS);

lv_obj_set_style_pad_left(Table.TableMain, 10, LV_PART_ITEMS);
lv_obj_set_style_pad_right(Table.TableMain, 0, LV_PART_ITEMS);
lv_obj_set_style_pad_top(Table.TableMain, 5, LV_PART_ITEMS);
lv_obj_set_style_pad_bottom(Table.TableMain, 5, LV_PART_ITEMS);

lv_obj_add_flag(Table.TableMain, LV_OBJ_FLAG_CLICKABLE);      
lv_obj_add_event_cb(Table.TableMain, _cb_Table, LV_EVENT_ALL, NULL);	

for(int row = 0;row < rows;++row)
{
	for(int col = 0;col < cols;++col)
	{
		if((row == 2) && (col == 2)) sprintf(Buf_Print, "25.7");
		else					     sprintf(Buf_Print, "%c%d", 'A'+col, row);
		lv_table_set_cell_value(Table.TableMain, row, col, Buf_Print);
	}
	lv_table_set_col_width(Table.TableMain, row, SIZE_X_CELL);
}	

// Панелька для вертикального заголовка
Table.Panel_V = lv_obj_create(Table.PanelDecor);
lv_obj_set_height(Table.Panel_V, rows * SIZE_Y_HEADER_H);
lv_obj_set_width(Table.Panel_V, SIZE_X_HEADER_V);
lv_obj_set_x(Table.Panel_V, 0);
lv_obj_set_y(Table.Panel_V, POS_Y_HEADER_V);
lv_obj_clear_flag(Table.Panel_V, LV_OBJ_FLAG_SCROLLABLE);				      
lv_obj_set_style_bg_color(Table.Panel_V, lv_color_hex(0x051d2f), LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_border_width(Table.Panel_V, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_radius(Table.Panel_V, 12, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_left(Table.Panel_V, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_right(Table.Panel_V, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_top(Table.Panel_V, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_bottom(Table.Panel_V, 0, LV_PART_MAIN | LV_STATE_DEFAULT);

// Заголовок ( таблица ) вертикальная
Table.Header_V = lv_table_create(Table.Panel_V);
lv_obj_set_x(Table.Header_V, 0);
lv_obj_set_y(Table.Header_V, 0);

lv_obj_set_style_bg_color(Table.Header_V, lv_color_hex(0x051d2f), LV_PART_ITEMS);
lv_obj_set_style_text_color(Table.Header_V, lv_color_hex(0x5582a3), LV_PART_ITEMS);
lv_obj_set_style_text_font(Table.Header_V, &RubicMedium16, LV_PART_ITEMS);
lv_obj_set_style_border_width(Table.Header_V, 0, LV_PART_ITEMS);	
lv_obj_set_style_border_width(Table.Header_V, 0, LV_PART_MAIN);	
lv_obj_set_style_border_side(Table.Header_V, LV_BORDER_SIDE_NONE,  LV_PART_ITEMS); // Без рамки
lv_obj_set_style_pad_left(Table.Header_V, 5, LV_PART_ITEMS);
lv_obj_set_style_pad_right(Table.Header_V, 0, LV_PART_ITEMS);
lv_obj_set_style_pad_top(Table.Header_V, 5, LV_PART_ITEMS);
lv_obj_set_style_pad_bottom(Table.Header_V, 5, LV_PART_ITEMS);

lv_table_set_row_cnt(Table.Header_V, rows);		
lv_table_set_col_cnt(Table.Header_V, 1);				
for(int row = 0;row < rows;++row)
{
	sprintf(Buf_Print, "%d", row);
	lv_table_set_cell_value(Table.Header_V, row, 0, Buf_Print);
}
lv_table_set_col_width(Table.Header_V, 0, SIZE_X_HEADER_V);


// Панелька для горизонтального заголовка
Table.Panel_H = lv_obj_create(Table.PanelDecor);
lv_obj_set_x(Table.Panel_H, POS_X_HEADER_H);
lv_obj_set_y(Table.Panel_H, 0);
lv_obj_set_height(Table.Panel_H, SIZE_Y_HEADER_H);
lv_obj_set_width(Table.Panel_H, 250);
lv_obj_clear_flag(Table.Panel_H, LV_OBJ_FLAG_SCROLLABLE);			
lv_obj_set_style_bg_color(Table.Panel_H, lv_color_hex(0x051d2f), LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_border_width(Table.Panel_H, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_radius(Table.Panel_H, 12, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_left(Table.Panel_H, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_right(Table.Panel_H, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_top(Table.Panel_H, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_bottom(Table.Panel_H, 0, LV_PART_MAIN | LV_STATE_DEFAULT);

// Заголовок ( таблица ) горизонтальная
Table.Header_H = lv_table_create(Table.Panel_H);
lv_obj_set_x(Table.Header_H, 0);
lv_obj_set_y(Table.Header_H, 0);
lv_obj_set_style_bg_color(Table.Header_H, lv_color_hex(0x051d2f), LV_PART_ITEMS);
lv_obj_set_style_text_color(Table.Header_H, lv_color_hex(0x5582a3), LV_PART_ITEMS);
lv_obj_set_style_text_font(Table.Header_H, &RubicMedium16, LV_PART_ITEMS);
lv_obj_set_style_border_width(Table.Header_H, 0, LV_PART_ITEMS);	
lv_obj_set_style_border_width(Table.Header_H, 0, LV_PART_MAIN);	
lv_obj_set_style_border_side(Table.Header_H, LV_BORDER_SIDE_NONE,  LV_PART_ITEMS); // Без рамки
lv_obj_set_style_radius(Table.Header_H, 12, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_pad_left(Table.Header_H, 5, LV_PART_ITEMS);
lv_obj_set_style_pad_right(Table.Header_H, 0, LV_PART_ITEMS);
lv_obj_set_style_pad_top(Table.Header_H, 5, LV_PART_ITEMS);
lv_obj_set_style_pad_bottom(Table.Header_H, 5, LV_PART_ITEMS);
lv_table_set_row_cnt(Table.Header_H, 1);				// 1 строка
lv_table_set_col_cnt(Table.Header_H, cols);	
for(int col = 0;col < cols;++col)
{
	sprintf(Buf_Print, "%c", 'A'+col);
	lv_table_set_cell_value(Table.Header_H, 0, col, Buf_Print);
	lv_table_set_col_width(Table.Header_H, col, SIZE_X_CELL);
}

}