Proper recognition and set of styles per state in button

Description

Attaching styles in button strange behaviour.

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

ESP32

What LVGL version are you using?

Simulator lvgl 8.3

What do you want to achieve?

Different styles for base state and pressed state. Cause the button has gradient to change bg from blue to red and button is checkable then after button is release you get a mix gradient that starts with blue and ends with red. Normal behaviour should have been that on check state the button is completely red.

What have you tried so far?

Attaching 2 styles on LV_PART_MAIN | LV_STATE_DEFAULT for the main button and
LV_PART_MAIN | LV_STATE_PRESSED for pressed. Tried to read state of button on callback for any event and i get value of 38 and 39 or which seems to be PRESSED | FOCUSED | FOKUS_KEY (| CHECKED for 39). Maybe the main question i why the button comes immediatly with LV_STATE_FOCUS_KEY state and i can not get rid of it on the default state. Which means that the button is always focused even if i have a second object inside.

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:

typedef enum {
	START,
	STOP,
	SAVE,
	LOAD
}ButtonTask;

typedef struct {
	int32_t width;
	int32_t height;
	ButtonTask cbTask;
	char* text;
	bool checkable;
}button_t;

lv_obj_t* create_button(button_t btnConf){


	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_outline_width(&style_btn, 0);
	lv_style_set_radius(&style_btn, btnConf.height/2);
	lv_style_set_bg_grad_color(&style_btn, lv_palette_darken(LV_PALETTE_BLUE, 4));
	lv_style_set_bg_grad_dir(&style_btn, LV_GRAD_DIR_VER);


	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(lv_scr_act());
	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, button_event_handler, LV_EVENT_VALUE_CHANGED, (void *) &btnConf.cbTask);

	lv_obj_add_style(btn, &style_btn, LV_PART_MAIN | LV_STATE_DEFAULT | LV_STATE_FOCUS_KEY);
	lv_obj_add_style(btn, &style_btn_pr, LV_PART_MAIN | LV_STATE_PRESSED  );
	return btn;
}

void button_event_handler(lv_event_t* e){

	lv_event_code_t code=lv_event_get_code(e);
	lv_obj_t* button = lv_event_get_target(e);
	ButtonTask task= (ButtonTask)lv_event_get_user_data(e);

	switch(button->state){
	case LV_STATE_DEFAULT:
		LV_LOG_USER("LV_STATE_DEFAULT");
		break;
	case LV_STATE_CHECKED:
		LV_LOG_USER("LV_STATE_CHECKED");
		break;
	case LV_STATE_FOCUSED:
		LV_LOG_USER("LV_STATE_FOCUSED");
		break;
	case LV_STATE_FOCUS_KEY:
		LV_LOG_USER("LV_STATE_FOCUS_KEY");
		break;
	case LV_STATE_EDITED :
		LV_LOG_USER("LV_STATE_EDITED");
		break;
	case LV_STATE_HOVERED:
		LV_LOG_USER("LV_STATE_HOVERED");
		break;
	case LV_STATE_PRESSED:
		LV_LOG_USER("LV_STATE_PRESSED");
		break;
	case LV_STATE_SCROLLED:
		LV_LOG_USER("LV_STATE_SCROLLED");
		break;
	case LV_STATE_DISABLED:
		LV_LOG_USER("LV_STATE_DISABLED");
		break;

	case LV_STATE_USER_1:
		LV_LOG_USER("LV_STATE_USER_1");
		break;
	case LV_STATE_USER_2:
		LV_LOG_USER("LV_STATE_USER_2");
				break;
	case LV_STATE_USER_3:
		LV_LOG_USER("LV_STATE_USER_3");
				break;
	case LV_STATE_USER_4:
		LV_LOG_USER("LV_STATE_USER_4");
				break;
	default:
		printf("%d\n",button->state);
		LV_LOG_USER( "non");
		break;
	}

	switch (code) {
		case LV_EVENT_PRESSED: 			   /**< The object has been pressed*/
			LV_LOG_USER("LV_EVENT_PRESSED");
			break;
		case LV_EVENT_PRESSING:            /**< The object is being pressed (called continuously while pressing)*/
			LV_LOG_USER("LV_EVENT_PRESSING");
			break;
		case LV_EVENT_PRESS_LOST:          /**< The object is still being pressed but slid cursor/finger off of the object */
			LV_LOG_USER("LV_EVENT_PRESS_LOST");
			break;
		case LV_EVENT_SHORT_CLICKED:       /**< The object was pressed for a short period of time, then released it. Not called if scrolled.*/
			LV_LOG_USER("LV_EVENT_SHORT_CLICKED");
			break;
		case LV_EVENT_LONG_PRESSED:        /**< Object has been pressed for at least `long_press_time`.  Not called if scrolled.*/
			LV_LOG_USER("LV_EVENT_LONG_PRESSED");
			break;
		case LV_EVENT_LONG_PRESSED_REPEAT: /**< Called after `long_press_time` in every `long_press_repeat_time` ms.  Not called if scrolled.*/
			LV_LOG_USER("LV_EVENT_LONG_PRESSED_REPEAT");
			break;
		case LV_EVENT_CLICKED:             /**< Called on release if not scrolled (regardless to long press)*/
			LV_LOG_USER("LV_EVENT_CLICKED");
			break;
		case LV_EVENT_RELEASED:            /**< Called in every cases when the object has been released*/
			LV_LOG_USER("LV_EVENT_RELEASED");
			break;
		case LV_EVENT_SCROLL_BEGIN:        /**< Scrolling begins. The event parameter is a pointer to the animation of the scroll. Can be modified*/
			LV_LOG_USER("LV_EVENT_SCROLL_BEGIN");
			break;
		case LV_EVENT_SCROLL_END:          /**< Scrolling ends*/
			LV_LOG_USER("LV_EVENT_SCROLL_END");
			break;
		case LV_EVENT_SCROLL:              /**< Scrolling*/
			LV_LOG_USER("LV_EVENT_SCROLL");
			break;
		case LV_EVENT_GESTURE:             /**< A gesture is detected. Get the gesture with `lv_indev_get_gesture_dir(lv_indev_get_act());` */
			LV_LOG_USER("LV_EVENT_GESTURE");
			break;
		case LV_EVENT_KEY:                 /**< A key is sent to the object. Get the key with `lv_indev_get_key(lv_indev_get_act());`*/
			LV_LOG_USER("LV_EVENT_KEY");
			break;
		case LV_EVENT_FOCUSED:             /**< The object is focused*/
			LV_LOG_USER("LV_EVENT_FOCUSED");
			break;
		case LV_EVENT_DEFOCUSED:          /**< The object is defocused*/
			LV_LOG_USER("LV_EVENT_DEFOCUSED");
			break;
		case LV_EVENT_LEAVE:               /**< The object is defocused but still selected*/
			LV_LOG_USER("LV_EVENT_LEAVE");
			break;
		case LV_EVENT_HIT_TEST:
			LV_LOG_USER("LV_EVENT_HIT_TEST");
			break;
		default:
			//LV_LOG_USER("NON");
			break;
	}


}

void main_view(){

	button_t btnConf={
			.width=144,
			.height=32,
			.cbTask = START,
			.text = "The Start",
			.checkable = true
	};
	lv_obj_t* button = create_button(btnConf);
	lv_obj_center(button);
}

Screenshot and/or video

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

After reading again the base object section, as i understood the problem is of not setting the styles properly not only the content itself cause of the ORed style setup. so i fixed it with a focus style that only takes out the border and placing the pressed style to check and pressed states but not together but separately

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

I was expecting at the beginning that OR condition will cover all 3 cases:

lv_obj_add_style(btn, &style_btn_pr, LV_PART_MAIN | LV_STATE_CHECKED | LV_STATE_PRESSED);

checked,pressed,check+pressed but it seems it covers only the last one. So it does not work. Please some confirmation to be sure that i have understood it well.

Hi,

I confirm that LV_STATE_CHECKED | LV_STATE_PRESSED means the checked and pressed state together. I admit that it’s counter intuitive as you write an OR operator. To add a style for the pressed state and the same style for checked state you really need two lines of code as you assumed.

The root of the gradient issue is that by default the check button is red:
forum

And here you haven’t specified the bg_color, so in default state it will be blue, in checked state it will be red:

	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_outline_width(&style_btn, 0);
	lv_style_set_radius(&style_btn, btnConf.height/2);
	lv_style_set_bg_grad_color(&style_btn, lv_palette_darken(LV_PALETTE_BLUE, 4));
	lv_style_set_bg_grad_dir(&style_btn, LV_GRAD_DIR_VER);

Just add this:

	lv_style_set_bg_color(&style_btn, lv_palette_darken(LV_PALETTE_BLUE, 1));

Thank you i have only one more question:
Why the button on creation is immediately on focus_key state?

I know this cause i was trying to remove the outline and i found out that i was not setting the LV_STATE_FOCUS_KEY state.
So every button is always at focus key state or this is an issue on simulator? Or something else that i did not understood yet?

Probably you have set default group with lv_group_set_default. If so LVGL adds the focusable objects that group and also focuses it.

No sorry no group used just to textareas and a button to test behaviours and learn the lvgl.
As slow start.

I tried also group on the textareas to try to switch between them so you can guess what hapend button is still on focus immediatly and also between textareas you can not change with tab (also i am trying to make it from lvgl keyboard this but for later).

My problem is that even if the last item created is the group and it is focusing as being the last item created also the button keeps the outline that is is by default assigned to the focus state which means that for some reason i have 2 items focused?

Some other thing are not ok also like opening the number keyboard on the text but i will find them later. It is about the order probably