Proper implementation for multiple buttons and their styles

Description

How to implement styles to be used on generic function that creates buttons based on config structure passed as operant.

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

esp32 , simulator

What LVGL version are you using?

8.3

What do you want to achieve?

I am trying to achive a button generator function. That we give a config struct and returns a button. Function should initialize styles for the button based on config but the styles can not be static cause the function should be used several times in order to create all buttons needed in program.

What have you tried so far?

I have 3 styles one normal button one for focused and one for pressed. In the config i am passing button size, text, font, text color, and other params and flags. The styles initially in the function where created as static and they worked only for one button.the style of the last button is overwriting all the rest. When i removed the static from the styles and let them to be block oriented that i have crash of function.

Code to reproduce


typedef struct {
	int32_t width;
	int32_t height;
	ButtonTask cbTask;
	bool checkable;
	char* text;
	const lv_font_t* textFont;
	lv_color_t textColor;
}button_t;

lv_obj_t* str_create_button(button_t btnConf,lv_obj_t* parent){


	static lv_style_t style_btn;
	lv_style_init(&style_btn);
	lv_style_set_width(&style_btn, btnConf.width);
	lv_style_set_height(&style_btn, btnConf.height);
	lv_style_set_text_font(&style_btn, btnConf.textFont);
	lv_style_set_text_color(&style_btn, btnConf.textColor);
	lv_style_set_outline_width(&style_btn, 0);
	lv_style_set_radius(&style_btn, btnConf.height/2);
	lv_style_set_bg_grad_color(&style_btn, lv_color_black());
	lv_style_set_bg_grad_dir(&style_btn, LV_GRAD_DIR_VER);


	static lv_style_t style_btn_fc;
	lv_style_init(&style_btn_fc);
	lv_style_set_outline_width(&style_btn_fc, 0);

	static lv_style_t style_btn_pr;
	lv_style_init(&style_btn_pr);
	lv_style_set_width(&style_btn_pr, btnConf.width);
	lv_style_set_height(&style_btn_pr, btnConf.height);
	lv_style_set_outline_width(&style_btn_pr, 0);
	lv_style_set_radius(&style_btn_pr, btnConf.height/2);
	lv_style_set_bg_color(&style_btn_pr, lv_palette_main(LV_PALETTE_RED));

	lv_style_set_bg_grad_color(&style_btn_pr, lv_palette_darken(LV_PALETTE_RED, 4));
	lv_style_set_bg_grad_dir(&style_btn_pr, LV_GRAD_DIR_VER);
	lv_style_set_shadow_color(&style_btn_pr, lv_palette_main(LV_PALETTE_RED));
	lv_style_set_shadow_width(&style_btn_pr, 2*btnConf.height);
	lv_style_set_shadow_opa(&style_btn_pr, LV_OPA_COVER);



	static const lv_style_prop_t btn_trans_props[] = {
		LV_STYLE_BG_COLOR,
		LV_STYLE_SHADOW_COLOR,
		LV_STYLE_SHADOW_WIDTH,
		LV_STYLE_SHADOW_OPA,
		0, /*End marker*/
	};

	static lv_style_transition_dsc_t btn_trans;
	lv_style_transition_dsc_init(&btn_trans, btn_trans_props, lv_anim_path_ease_out, 10, 0,NULL);
	lv_style_set_transition(&style_btn_pr, &btn_trans);
	lv_style_set_transition(&style_btn, &btn_trans);



	lv_obj_t* btn=lv_btn_create(parent);
	lv_obj_t* label=lv_label_create(btn);

	if(btnConf.checkable){
		lv_obj_add_flag(btn, LV_OBJ_FLAG_CHECKABLE);
	}

	lv_label_set_text(label, btnConf.text);
	lv_obj_center(label);



	lv_obj_add_event_cb(btn, str_button_event_handler, LV_EVENT_PRESSED, (void *)btnConf.cbTask);

	lv_obj_add_style(btn, &style_btn, LV_PART_MAIN | LV_STATE_DEFAULT);
	lv_obj_add_style(btn, &style_btn_fc, LV_PART_MAIN | LV_STATE_FOCUS_KEY);
	lv_obj_add_style(btn, &style_btn_pr, LV_PART_MAIN | LV_STATE_CHECKED);
	lv_obj_add_style(btn, &style_btn_pr, LV_PART_MAIN | LV_STATE_PRESSED);

	return btn;
}

Don’t know whether I correctly understand you.

Within the function str_create_button, you are removing the static?
E.g.

static lv_style_t style_btn

becomes

lv_style_t style_btn

I tried this and i get memory exceptions

Of course. Without the static the variables are now on stack, and valid only until you leave the function.

which means that assesment of style in any object does not store the info inside the object at all if i understand well cause you are not passing the address of the style if i am correct. So how can this be solved it is not so good to have all possible styles static in code. Imagine a complex ui with tones o buttons and several styles per state. Or all variables that are about size and text should be defined from lv_obj functions not from style setup.
I do not remember if font can be set from lv_obj.

Tried also to do a struct with style pointers in order to pass it also

typedef struct{
	lv_style_t* style_btn;
	lv_style_t* style_btn_fc;
	lv_style_t* style_btn_pr;
}buttonStyles_t;

typedef struct {
	int32_t width;
	int32_t height;
	ButtonTask cbTask;
	bool checkable;
	char* text;
	const lv_font_t* textFont;
	lv_color_t textColor;
	buttonStyles_t buttonStyles;
}button_t;

//And then in the function to do this 
	lv_style_t* style_btn=btnConf.buttonStyles.style_btn;
	lv_style_init(style_btn);
	lv_style_set_width(style_btn, btnConf.width);
	lv_style_set_height(style_btn, btnConf.height);
	lv_style_set_text_font(style_btn, btnConf.textFont);
	lv_style_set_text_color(style_btn, btnConf.textColor);
	lv_style_set_outline_width(style_btn, 0);
	lv_style_set_radius(style_btn, btnConf.height/2);
	lv_style_set_bg_grad_color(style_btn, lv_color_black());
	lv_style_set_bg_grad_dir(style_btn, LV_GRAD_DIR_VER);

But i get error again:

=================================================================
==4599==ERROR: AddressSanitizer: SEGV on unknown address (pc 0x00010c5d9687 bp 0x7ff7b3a8fd30 sp 0x7ff7b3a8fcf0 T0)
==4599==The signal is caused by a READ memory access.
==4599==Hint: this fault was caused by a dereference of a high value address (see register values below). Dissassemble the provided pc to learn which register was used.
#0 0x10c5d9687 in lv_style_init lv_style.c:153
#1 0x10c47281c in str_create_button str_button.c:33

It will be difficult to explain the C basics to newbie in only a few words.

The absence of ‘static’ does not mean something is ‘dynamic’.
If you want something created dynamically you need malloc.
Malloc will return a memory address (of the specified size), which will be
reserved for the caller/user as long as free (with the memory address) is called.
This means, that your program is responsible for calling free (after a malloc) again,
to free any not more used memory.

There are basically three/four different kinds of getting a memory area.

Via defining a variable/structure in a sourcefile:

Outside of any function:
The appropriate memory location is reserved via compiler/linker.

lv_style_t style_btn;

Inside of a function (without static):
The appropriate memory location is reserved on stack space, and is only valid until the function is left again.

void my_function (void)
{
  lv_style_t style_btn;
}

Inside of a function (with static):
The appropriate memory location is reserved by the compiler/linker exclusively for the that function.
You can’t reference that style_btn from anywhere outside this function. Although the memory address can be passed to any other function, and nothing will happen if the function is left. The memory is still reserved.

void my_function (void)
{
  static lv_style_t style_btn;
  ...
}

Inside a function with malloc:
With this method you are responsible for freeing the memory again when it is not more used.
Else your heap might go out of memory…

lv_style_t* ptStylePtr = NULL;

void my_function (void)
{
  ptStylePtr = (lv_style_t*) malloc (sizeof (lv_style_t)):
  ...
}

Programming in C is really different to progamming in e.g. python.
In C your code is responsible for everything.

irrespectively of any newbie comments probably you did not exactly understood the meaning of dynamic that i referred which was about the setup of different instances button.
What i missed is that in the btn we are assign the address of the style so if on next btn creation you update the style itself since it is static the you update everything.

The answer i seek starts after all these or i did not connect the dots properly.

From all these things i understand that in order to have buttons running with different setup you can not free the memory as long as the screen is live , which means that on switch between screens it is better to recreate the screen.

Second it is better not to set the all dynamic setup (font, text, color that are included in the label of the btn) not in the style like with lv_obj_set_style_text_font (will check maybe i am still wrong). These generic static styles should be generic and independent and there is no need to be called everytime.

With the usage of malloc to create a pointer that offcourse at some time you have to free ,and the assignment of the pointer to the ptStylePtr what is going to happen if you call my function 5 times? I assume that the pointer will be new every time and the memory will be new. If you have previously assigned this ptStylePrt to one button then next time you update the address isn’t it that the previous button will look now also to the new position also?

Maybe this is the proper question that i seek

It seems that if i do the next i get result:

// commenting these parts for the initial style setup and set it back to static
	static lv_style_t style_btn;
	lv_style_init(&style_btn);
//	lv_style_set_width(style_btn, btnConf.width);
//	lv_style_set_height(style_btn, btnConf.height);
//	lv_style_set_text_font(style_btn, btnConf.textFont);
//	lv_style_set_text_color(style_btn, btnConf.textColor);
	lv_style_set_outline_width(&style_btn, 0);
//	lv_style_set_radius(&style_btn, btnConf.height/2);
	lv_style_set_bg_grad_color(&style_btn, lv_color_black());
	lv_style_set_bg_grad_dir(&style_btn, LV_GRAD_DIR_VER);

// on creation of btn i have to set all dynamic values
	lv_obj_t* btn=lv_btn_create(parent);
	lv_obj_t* label=lv_label_create(btn);


	if(btnConf.checkable){
		lv_obj_add_flag(btn, LV_OBJ_FLAG_CHECKABLE);
	}

	lv_label_set_text(label, btnConf.text);

	lv_obj_set_style_width(btn, btnConf.width,0);
	lv_obj_set_style_height(btn, btnConf.height,0);
	lv_obj_set_style_radius(btn, btnConf.height/2,0);


	lv_obj_set_style_text_font(label, btnConf.textFont, 0);
	lv_obj_set_style_text_color(label, btnConf.textColor,0);

	lv_obj_center(label);

If someone has smoother solution please give info