The effect about button object move to triangle object

Hi,
I want to know what the problem is and what methods can be optimized.

This effect is shown in the figure

The issue is that when the button is placed above the triangle in the Z-order the button’s corner isn’t drawn properly?

1.Both button object and triangle object setup lv_obj_set_drag(object, true)
2.The triangle object is always on top
3.When I drag the button object move to the triangle object until overlap, the triangle object effect be changed, I expect that the triangle object effect don’t be changed

So I wonder if it’s possible to achieve this or feasible advice.

In the second effect comparison, the button’s corner shouldn’t be visible inside the triangle. It looks like a bug with LittlevGL to me.

When overlapping, this is generated when the triangle object is redrawn.
In fact, the triangle object does not need to be mixed with the button object.

That’s strange!
@Anda please send a code snippet which can be copied (no screenshot).

Hi, The code like this

typedef struct {
	lv_point_t points[3];
	bool       auto_size;
} lv_triangle_ext_t;

lv_obj_t* lv_triangle_create(lv_obj_t* par, const lv_obj_t* copy);
void lv_triangle_set_points(lv_obj_t* obj, const lv_point_t* points);
void lv_triangle_set_auto_size(lv_obj_t* obj, bool en);
bool lv_triangle_get_auto_size(const lv_obj_t* obj);

static bool lv_triangle_design(lv_obj_t* obj, const lv_area_t* mask, lv_design_mode_t mode);

lv_obj_t* lv_triangle_create(lv_obj_t* par, const lv_obj_t* copy)
{
	lv_obj_t* obj = lv_obj_create(par, copy);
	LV_ASSERT_MEM(obj);
	if (obj == NULL) return NULL;

	lv_triangle_ext_t* ext = (lv_triangle_ext_t*)lv_obj_allocate_ext_attr(obj, sizeof(lv_triangle_ext_t));
	LV_ASSERT_MEM(ext);
	if (ext == NULL){
        lv_obj_del(obj);
        return NULL;
    }
 
	ext->auto_size = 1;
	lv_obj_set_design_cb(obj, lv_triangle_design);
	lv_obj_set_click(obj, false);

	if (copy == NULL) {
		lv_obj_set_size(obj, LV_DPI, LV_DPI);  /*Auto size is enables, but set default size until no points are added*/
		lv_obj_set_style(obj, NULL); /*Inherit parent's style*/
	}
	else {
		lv_triangle_ext_t* copy_ext = (lv_triangle_ext_t*)lv_obj_get_ext_attr(copy);
		lv_triangle_set_points(obj, copy_ext->points);
		lv_obj_set_style(obj, lv_obj_get_style(copy));
		lv_obj_refresh_style(obj);
	}
	return obj;
}

void lv_triangle_set_points(lv_obj_t* obj, const lv_point_t* points)
{
	lv_triangle_ext_t* ext = (lv_triangle_ext_t*)lv_obj_get_ext_attr(obj);
	ext->points[0] = points[0];
	ext->points[1] = points[1];
	ext->points[2] = points[2];

	if (ext->auto_size != 0) {
		uint16_t i;
		lv_coord_t xmax = LV_COORD_MIN;
		lv_coord_t ymax = LV_COORD_MIN;
		for (i = 0; i < 3; i++) {
			xmax = LV_MATH_MAX(points[i].x, xmax);
			ymax = LV_MATH_MAX(points[i].y, ymax);
		}

		const lv_style_t* style = lv_obj_get_style(obj);
		lv_obj_set_size(obj, xmax + style->body.border.width, ymax + style->body.border.width);
	}

	lv_obj_invalidate(obj);
}

void lv_triangle_set_auto_size(lv_obj_t* obj, bool en)
{
	lv_triangle_ext_t* ext = (lv_triangle_ext_t*)lv_obj_get_ext_attr(obj);
	if (ext->auto_size == en) return;
	ext->auto_size = en == false ? 0 : 1;
	if (en) lv_triangle_set_points(obj, ext->points);
}

bool lv_triangle_get_auto_size(const lv_obj_t* obj) {
	lv_triangle_ext_t* ext = (lv_triangle_ext_t*)lv_obj_get_ext_attr(obj);
	return ext->auto_size;
}

static bool lv_triangle_design(lv_obj_t* obj, const lv_area_t* mask, lv_design_mode_t mode)
{
	if (mode == LV_DESIGN_COVER_CHK) {
	    return false;
	}
	else if (mode == LV_DESIGN_DRAW_MAIN) {
		lv_triangle_ext_t* ext = (lv_triangle_ext_t*)lv_obj_get_ext_attr(obj);

		if ((ext->points[0].x == ext->points[1].x && ext->points[0].y == ext->points[1].y) ||
			(ext->points[0].x == ext->points[2].x && ext->points[0].y == ext->points[2].y) ||
			(ext->points[1].x == ext->points[2].x && ext->points[1].y == ext->points[2].y))
			return true;

		const lv_style_t* style = lv_obj_get_style(obj);
		lv_opa_t          opa_scale = lv_obj_get_opa_scale(obj);
		lv_area_t         offset; 

        lv_obj_get_coords(obj, &offset);

		lv_point_t points[3] = { {ext->points[0].x + offset.x1, ext->points[0].y + offset.y1},
								 {ext->points[1].x + offset.x1, ext->points[1].y + offset.y1},
								 {ext->points[2].x + offset.x1, ext->points[2].y + offset.y1} };

		if (style->body.opa != 0){
			lv_draw_triangle(points, mask, style, opa_scale);            
        }

		if (style->line.width) {
			lv_style_t style_line; lv_style_copy(&style_line, style);
			style_line.line.width = style->body.border.width;
			style_line.line.color = style->body.border.color;

			lv_draw_line(&points[0], &points[1], mask, &style_line, opa_scale);
			lv_draw_line(&points[1], &points[2], mask, &style_line, opa_scale);
			lv_draw_line(&points[2], &points[0], mask, &style_line, opa_scale);
		}
	}
	return true;
}

void ui_test(void)
{
	lv_obj_t *btn_obj = lv_btn_create(lv_scr_act(), NULL);
	lv_obj_set_size(btn_obj, 60, 60);
	lv_obj_set_drag(btn_obj, true);

	lv_obj_t *triangle_obj = lv_triangle_create(lv_scr_act(), NULL);
	static lv_style_t triangle_style;
	lv_style_copy(&triangle_style, &lv_style_plain);
	triangle_style.body.main_color   = LV_COLOR_BLUE;
	triangle_style.body.grad_color   = LV_COLOR_PURPLE;
	triangle_style.body.opa          = LV_OPA_COVER;
	triangle_style.body.border.color = LV_COLOR_RED;
	triangle_style.body.border.width = 2;
	triangle_style.body.border.opa   = LV_OPA_COVER;
	lv_obj_set_style(triangle_obj, &triangle_style);
	static lv_point_t triangle_pts[] = {
		{ 0,  50 },
		{ 30, 0  },
		{ 60, 60 },
	};
	lv_triangle_set_points(triangle_obj, triangle_pts);
	lv_obj_set_drag(triangle_obj, true);
}

Thanks. I’ve just fixed it in dev-7.0.

I’ve also slightly modified you design function:

  • in dev-7.0 it has lv_design_res_t return value
  • disabled border and shadow drawing because they would be applied on the rectangle from where the triangle is masked out
static lv_design_res_t lv_triangle_design(lv_obj_t* obj, const lv_area_t* mask, lv_design_mode_t mode)
{
    if (mode == LV_DESIGN_COVER_CHK) {
        return LV_DESIGN_RES_NOT_COVER;
    }
    else if (mode == LV_DESIGN_DRAW_MAIN) {
        lv_triangle_ext_t* ext = (lv_triangle_ext_t*)lv_obj_get_ext_attr(obj);

        if ((ext->points[0].x == ext->points[1].x && ext->points[0].y == ext->points[1].y) ||
            (ext->points[0].x == ext->points[2].x && ext->points[0].y == ext->points[2].y) ||
            (ext->points[1].x == ext->points[2].x && ext->points[1].y == ext->points[2].y))
            return true;

        const lv_style_t* style = lv_obj_get_style(obj);
        lv_opa_t          opa_scale = lv_obj_get_opa_scale(obj);
        lv_area_t         offset;

        lv_obj_get_coords(obj, &offset);

        lv_point_t points[3] = { {ext->points[0].x + offset.x1, ext->points[0].y + offset.y1},
                                 {ext->points[1].x + offset.x1, ext->points[1].y + offset.y1},
                                 {ext->points[2].x + offset.x1, ext->points[2].y + offset.y1} };


        if (style->body.opa != 0){
            /*Polygons are drawn from rectangles.
             * Disable border an shadow because the border would be applied on the masked rectangle not the polygon */
            lv_style_t style_tmp;
            lv_style_copy(&style_tmp, style);
            style_tmp.body.border.width = 0;
            style_tmp.body.shadow.width = 0;
            lv_draw_triangle(points, mask, &style_tmp, opa_scale);
        }

        if (style->line.width) {
            lv_style_t style_line; lv_style_copy(&style_line, style);
            style_line.line.width = style->body.border.width;
            style_line.line.color = style->body.border.color;

            lv_draw_line(&points[0], &points[1], mask, &style_line, opa_scale);
            lv_draw_line(&points[1], &points[2], mask, &style_line, opa_scale);
            lv_draw_line(&points[2], &points[0], mask, &style_line, opa_scale);
        }
    }
    return LV_DESIGN_RES_OK;
}

Thank you very much. I’ve just tested it and it works.

Great! Thanks for the feedback!