HASPmota and LVGL stripes widget (#24907)

This commit is contained in:
s-hadinger
2026-07-18 17:34:55 +02:00
committed by GitHub
parent 2bceaa0431
commit 5614aaec2a
13 changed files with 712 additions and 1 deletions
@@ -1811,3 +1811,20 @@ set_mode|int||[lv_colorwheel_set_mode](https://docs.lvgl.io/9.0/search.html?q=lv
set_mode_fixed|bool||[lv_colorwheel_set_mode_fixed](https://docs.lvgl.io/9.0/search.html?q=lv_colorwheel_set_mode_fixed)
set_rgb|lv.color|bool|[lv_colorwheel_set_rgb](https://docs.lvgl.io/9.0/search.html?q=lv_colorwheel_set_rgb)
### widget `lv.stripes`
Method|Arguments|Return type|LVGL equivalent
:---|:---|:---|:---
get_angle||int|[lv_stripes_get_angle](https://docs.lvgl.io/9.0/search.html?q=lv_stripes_get_angle)
get_gap_width||int|[lv_stripes_get_gap_width](https://docs.lvgl.io/9.0/search.html?q=lv_stripes_get_gap_width)
get_offset||int|[lv_stripes_get_offset](https://docs.lvgl.io/9.0/search.html?q=lv_stripes_get_offset)
get_stripe_color||lv.color|[lv_stripes_get_stripe_color](https://docs.lvgl.io/9.0/search.html?q=lv_stripes_get_stripe_color)
get_stripe_opa||int|[lv_stripes_get_stripe_opa](https://docs.lvgl.io/9.0/search.html?q=lv_stripes_get_stripe_opa)
get_stripe_width||int|[lv_stripes_get_stripe_width](https://docs.lvgl.io/9.0/search.html?q=lv_stripes_get_stripe_width)
set_angle|int||[lv_stripes_set_angle](https://docs.lvgl.io/9.0/search.html?q=lv_stripes_set_angle)
set_gap_width|int||[lv_stripes_set_gap_width](https://docs.lvgl.io/9.0/search.html?q=lv_stripes_set_gap_width)
set_offset|int||[lv_stripes_set_offset](https://docs.lvgl.io/9.0/search.html?q=lv_stripes_set_offset)
set_stripe_color|lv.color||[lv_stripes_set_stripe_color](https://docs.lvgl.io/9.0/search.html?q=lv_stripes_set_stripe_color)
set_stripe_opa|int||[lv_stripes_set_stripe_opa](https://docs.lvgl.io/9.0/search.html?q=lv_stripes_set_stripe_opa)
set_stripe_width|int||[lv_stripes_set_stripe_width](https://docs.lvgl.io/9.0/search.html?q=lv_stripes_set_stripe_width)
@@ -10,6 +10,7 @@ extern "C" {
#include "be_ctypes.h"
#include "be_mapping.h"
#include "../src/lv_colorwheel.h"
#include "../src/lv_stripes.h"
/* `lv_style` methods */
const be_ntv_func_def_t lv_style_func[] = {
@@ -1646,6 +1647,24 @@ const be_ntv_func_def_t lv_colorwheel_func[] = {
};
#endif // BE_LV_WIDGET_COLORWHEEL
/* `lv_stripes` methods */
#ifdef BE_LV_WIDGET_STRIPES
const be_ntv_func_def_t lv_stripes_func[] = {
{ "get_angle", { (const void*) &lv_stripes_get_angle, "i", "(lv.obj)" } },
{ "get_gap_width", { (const void*) &lv_stripes_get_gap_width, "i", "(lv.obj)" } },
{ "get_offset", { (const void*) &lv_stripes_get_offset, "i", "(lv.obj)" } },
{ "get_stripe_color", { (const void*) &lv_stripes_get_stripe_color, "lv.color", "(lv.obj)" } },
{ "get_stripe_opa", { (const void*) &lv_stripes_get_stripe_opa, "i", "(lv.obj)" } },
{ "get_stripe_width", { (const void*) &lv_stripes_get_stripe_width, "i", "(lv.obj)" } },
{ "set_angle", { (const void*) &lv_stripes_set_angle, "", "(lv.obj)i" } },
{ "set_gap_width", { (const void*) &lv_stripes_set_gap_width, "", "(lv.obj)i" } },
{ "set_offset", { (const void*) &lv_stripes_set_offset, "", "(lv.obj)i" } },
{ "set_stripe_color", { (const void*) &lv_stripes_set_stripe_color, "", "(lv.obj)(lv.color)" } },
{ "set_stripe_opa", { (const void*) &lv_stripes_set_stripe_opa, "", "(lv.obj)i" } },
{ "set_stripe_width", { (const void*) &lv_stripes_set_stripe_width, "", "(lv.obj)i" } },
};
#endif // BE_LV_WIDGET_STRIPES
extern const bclass be_class_lv_anim;
extern const bclass be_class_lv_animimg;
extern const bclass be_class_lv_arc;
@@ -1688,6 +1707,7 @@ extern const bclass be_class_lv_span;
extern const bclass be_class_lv_spangroup;
extern const bclass be_class_lv_spinbox;
extern const bclass be_class_lv_spinner;
extern const bclass be_class_lv_stripes;
extern const bclass be_class_lv_style;
extern const bclass be_class_lv_switch;
extern const bclass be_class_lv_table;
@@ -1812,6 +1832,9 @@ const be_ntv_class_def_t lv_classes[] = {
#ifdef BE_LV_WIDGET_SPINNER
{ "lv_spinner", &be_class_lv_spinner, lv_spinner_func, sizeof(lv_spinner_func) / sizeof(lv_spinner_func[0]) },
#endif // BE_LV_WIDGET_SPINNER
#ifdef BE_LV_WIDGET_STRIPES
{ "lv_stripes", &be_class_lv_stripes, lv_stripes_func, sizeof(lv_stripes_func) / sizeof(lv_stripes_func[0]) },
#endif // BE_LV_WIDGET_STRIPES
{ "lv_style", &be_class_lv_style, lv_style_func, sizeof(lv_style_func) / sizeof(lv_style_func[0]) },
#ifdef BE_LV_WIDGET_SWITCH
{ "lv_switch", &be_class_lv_switch, lv_switch_func, sizeof(lv_switch_func) / sizeof(lv_switch_func[0]) },
@@ -2004,6 +2027,10 @@ const size_t lv_classes_size = sizeof(lv_classes) / sizeof(lv_classes[0]);
#ifdef BE_LV_WIDGET_COLORWHEEL
int be_ntv_lv_colorwheel_init(bvm *vm) { return be_call_c_func(vm, (void*) &lv_colorwheel_create, "+_p", "(lv.obj)b"); }
#endif // BE_LV_WIDGET_COLORWHEEL
/* `lv_stripes` methods */
#ifdef BE_LV_WIDGET_STRIPES
int be_ntv_lv_stripes_init(bvm *vm) { return be_call_c_func(vm, (void*) &lv_stripes_create, "+_p", "(lv.obj)"); }
#endif // BE_LV_WIDGET_STRIPES
// create font either empty or from parameter on stack
int lvbe_font_create(bvm *vm) { return be_call_c_func(vm, NULL, "+_p", ""); }
@@ -32,6 +32,9 @@ extern int lvbe_theme_create(bvm *vm);
// adding ad-hoc colorwheel from LVGL8 to LVGL9
extern const lv_obj_class_t lv_colorwheel_class;
// custom Tasmota widget: rectangle filled with an angled stripe pattern
extern const lv_obj_class_t lv_stripes_class;
extern int be_ntv_lv_style_init(bvm *vm);
extern int be_ntv_lv_font_init(bvm *vm);
@@ -83,6 +86,7 @@ extern int be_ntv_lv_tabview_init(bvm *vm);
extern int be_ntv_lv_textarea_init(bvm *vm);
extern int be_ntv_lv_tileview_init(bvm *vm);
extern int be_ntv_lv_colorwheel_init(bvm *vm);
extern int be_ntv_lv_stripes_init(bvm *vm);
extern const bclass be_class_lv_anim;
extern const bclass be_class_lv_animimg;
@@ -126,6 +130,7 @@ extern const bclass be_class_lv_span;
extern const bclass be_class_lv_spangroup;
extern const bclass be_class_lv_spinbox;
extern const bclass be_class_lv_spinner;
extern const bclass be_class_lv_stripes;
extern const bclass be_class_lv_style;
extern const bclass be_class_lv_switch;
extern const bclass be_class_lv_table;
@@ -714,3 +719,14 @@ class be_class_lv_colorwheel (scope: global, name: lv_colorwheel, super: be_clas
}
@const_object_info_end */
/********************************************************************
** Solidified class: lv_stripes
********************************************************************/
#include "be_fixed_be_class_lv_stripes.h"
/* @const_object_info_begin
class be_class_lv_stripes (scope: global, name: lv_stripes, super: be_class_lv_obj, strings: weak) {
init, func(be_ntv_lv_stripes_init)
_class, comptr(&lv_stripes_class)
}
@const_object_info_end */
@@ -1744,3 +1744,18 @@ lv_color_t lv_colorwheel_get_rgb(lv_obj_t * obj)
lv_colorwheel_mode_t lv_colorwheel_get_color_mode(lv_obj_t * obj)
bool lv_colorwheel_get_color_mode_fixed(lv_obj_t * obj)
// ../src/lv_stripes.h
lv_obj_t * lv_stripes_create(lv_obj_t * parent)
void lv_stripes_set_stripe_color(lv_obj_t * obj, lv_color_t color)
void lv_stripes_set_stripe_opa(lv_obj_t * obj, lv_opa_t opa)
void lv_stripes_set_stripe_width(lv_obj_t * obj, int32_t width)
void lv_stripes_set_gap_width(lv_obj_t * obj, int32_t width)
void lv_stripes_set_angle(lv_obj_t * obj, int32_t angle)
void lv_stripes_set_offset(lv_obj_t * obj, int32_t offset)
lv_color_t lv_stripes_get_stripe_color(lv_obj_t * obj)
lv_opa_t lv_stripes_get_stripe_opa(lv_obj_t * obj)
int32_t lv_stripes_get_stripe_width(lv_obj_t * obj)
int32_t lv_stripes_get_gap_width(lv_obj_t * obj)
int32_t lv_stripes_get_angle(lv_obj_t * obj)
int32_t lv_stripes_get_offset(lv_obj_t * obj)
@@ -0,0 +1,418 @@
/**
* @file lv_stripes.c
*
* A rectangle widget decorated with a repeating pattern of diagonal
* (or any angle) stripes, drawn as thick lines on top of the normal
* `lv_obj` background.
*
* Part of the Tasmota project (https://github.com/arendst/Tasmota).
* Author: Stephan Hadinger
*/
/*********************
* INCLUDES
*********************/
#include "lv_stripes.h"
#include "../../lvgl/src/core/lv_obj_class_private.h"
#include "../../lvgl/src/core/lv_obj_private.h"
#include "../../lvgl/src/misc/lv_area_private.h"
#include "../../lvgl/src/misc/lv_math.h"
#include "../../lvgl/src/core/lv_obj_scroll.h"
#include "../../lvgl/src/draw/lv_draw.h"
#include "../../lvgl/src/draw/lv_draw_mask.h"
#include "../../lvgl/src/draw/lv_draw_image.h"
#include "../../lvgl/src/draw/lv_draw_line.h"
/*********************
* DEFINES
*********************/
#define MY_CLASS &lv_stripes_class
/**********************
* TYPEDEFS
**********************/
/*Data of the stripes widget*/
typedef struct {
lv_obj_t obj;
lv_color_t stripe_color;
lv_opa_t stripe_opa;
int32_t stripe_width;
int32_t gap_width;
int32_t angle; /*in degrees, 0..359*/
int32_t offset; /*pattern shift in pixels along the normal*/
} lv_stripes_t;
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_stripes_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_stripes_event(const lv_obj_class_t * class_p, lv_event_t * e);
static void draw_stripes(lv_event_t * e);
static void draw_stripes_pattern(lv_stripes_t * stripes, lv_layer_t * target_layer, const lv_area_t * coords);
static void draw_stripes_masked_band(lv_stripes_t * stripes, lv_layer_t * layer, const lv_area_t * coords,
int32_t radius, const lv_area_t * band);
/**********************
* STATIC VARIABLES
**********************/
const lv_obj_class_t lv_stripes_class = {
.constructor_cb = lv_stripes_constructor,
.event_cb = lv_stripes_event,
.instance_size = sizeof(lv_stripes_t),
.base_class = &lv_obj_class,
.width_def = LV_DPI_DEF,
.height_def = LV_DPI_DEF,
};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_obj_t * lv_stripes_create(lv_obj_t * parent)
{
LV_LOG_INFO("begin");
lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
lv_obj_class_init_obj(obj);
return obj;
}
/*=====================
* Setter functions
*====================*/
void lv_stripes_set_stripe_color(lv_obj_t * obj, lv_color_t color)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_stripes_t * stripes = (lv_stripes_t *)obj;
if(lv_color_eq(stripes->stripe_color, color)) return;
stripes->stripe_color = color;
lv_obj_invalidate(obj);
}
void lv_stripes_set_stripe_opa(lv_obj_t * obj, lv_opa_t opa)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_stripes_t * stripes = (lv_stripes_t *)obj;
if(stripes->stripe_opa == opa) return;
stripes->stripe_opa = opa;
lv_obj_invalidate(obj);
}
void lv_stripes_set_stripe_width(lv_obj_t * obj, int32_t width)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_stripes_t * stripes = (lv_stripes_t *)obj;
if(width < 1) width = 1;
if(stripes->stripe_width == width) return;
stripes->stripe_width = width;
lv_obj_invalidate(obj);
}
void lv_stripes_set_gap_width(lv_obj_t * obj, int32_t width)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_stripes_t * stripes = (lv_stripes_t *)obj;
if(width < 0) width = 0;
if(stripes->gap_width == width) return;
stripes->gap_width = width;
lv_obj_invalidate(obj);
}
void lv_stripes_set_angle(lv_obj_t * obj, int32_t angle)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_stripes_t * stripes = (lv_stripes_t *)obj;
angle %= 360;
if(angle < 0) angle += 360;
if(stripes->angle == angle) return;
stripes->angle = angle;
lv_obj_invalidate(obj);
}
void lv_stripes_set_offset(lv_obj_t * obj, int32_t offset)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_stripes_t * stripes = (lv_stripes_t *)obj;
if(stripes->offset == offset) return;
stripes->offset = offset;
lv_obj_invalidate(obj);
}
/*=====================
* Getter functions
*====================*/
lv_color_t lv_stripes_get_stripe_color(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_stripes_t * stripes = (lv_stripes_t *)obj;
return stripes->stripe_color;
}
lv_opa_t lv_stripes_get_stripe_opa(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_stripes_t * stripes = (lv_stripes_t *)obj;
return stripes->stripe_opa;
}
int32_t lv_stripes_get_stripe_width(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_stripes_t * stripes = (lv_stripes_t *)obj;
return stripes->stripe_width;
}
int32_t lv_stripes_get_gap_width(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_stripes_t * stripes = (lv_stripes_t *)obj;
return stripes->gap_width;
}
int32_t lv_stripes_get_angle(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_stripes_t * stripes = (lv_stripes_t *)obj;
return stripes->angle;
}
int32_t lv_stripes_get_offset(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_stripes_t * stripes = (lv_stripes_t *)obj;
return stripes->offset;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_stripes_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
lv_stripes_t * stripes = (lv_stripes_t *)obj;
stripes->stripe_color = lv_color_black();
stripes->stripe_opa = LV_OPA_COVER;
stripes->stripe_width = 10;
stripes->gap_width = 10;
stripes->angle = 45;
stripes->offset = 0;
/*Sensible default background so the widget is visible out of the box*/
lv_obj_set_style_bg_color(obj, lv_color_white(), LV_PART_MAIN);
lv_obj_set_style_bg_opa(obj, LV_OPA_COVER, LV_PART_MAIN);
/*Draw the border after the main content (our stripes), so it stays on
*top of them. This is LVGL's built-in mechanism for "border above
*content" and avoids drawing the border twice.*/
lv_obj_set_style_border_post(obj, true, LV_PART_MAIN);
/*Clip children (e.g. an oversized animated stripe child) to the
*rounded-corner shape, hide scrollbars that would otherwise appear when a
*child is larger than the container.*/
lv_obj_set_style_clip_corner(obj, true, LV_PART_MAIN);
lv_obj_set_scrollbar_mode(obj, LV_SCROLLBAR_MODE_OFF);
}
/**
* Draw the stripe pattern onto `target_layer`.
*
* The pattern is a family of parallel stripes, each drawn as a single thick
* line `stripe_width` pixels wide, spaced every `stripe_width + gap_width`
* pixels along the direction perpendicular to the stripe orientation. Each
* line runs the full diagonal of the widget (extended past the edges), so
* rotation never leaves gaps at the corners. Drawing one line per stripe -
* rather than a pair of triangles - avoids any internal anti-aliasing seam
* along the stripe. The renderer clips everything to `target_layer->_clip_area`.
*/
static void draw_stripes_pattern(lv_stripes_t * stripes, lv_layer_t * target_layer, const lv_area_t * coords)
{
int32_t w = lv_area_get_width(coords);
int32_t h = lv_area_get_height(coords);
int32_t cx = coords->x1 + w / 2;
int32_t cy = coords->y1 + h / 2;
int32_t period = stripes->stripe_width + stripes->gap_width;
if(period < 1) period = 1;
/*Half-diagonal: long enough so stripe lines always cover the full widget
*once rotated to any angle, plus a small margin.*/
int32_t half_diag = lv_sqrt32((uint32_t)((int64_t)w * w + (int64_t)h * h)) / 2 + period;
/*Direction vector along the stripe length, and its normal (the direction
*along which stripes are spaced).*/
int32_t dx = lv_trigo_sin(stripes->angle + 90);
int32_t dy = lv_trigo_sin(stripes->angle);
int32_t nx = lv_trigo_sin(stripes->angle);
int32_t ny = lv_trigo_sin(stripes->angle - 90);
lv_draw_line_dsc_t line_dsc;
lv_draw_line_dsc_init(&line_dsc);
line_dsc.color = stripes->stripe_color;
line_dsc.opa = stripes->stripe_opa;
line_dsc.width = stripes->stripe_width;
/*Offset from a stripe center to its two ends (half diagonal along the
*stripe direction). Same for every stripe.*/
int32_t lx = ((int64_t)half_diag * dx) / LV_TRIGO_SIN_MAX;
int32_t ly = ((int64_t)half_diag * dy) / LV_TRIGO_SIN_MAX;
/*Pattern shift along the normal, wrapped to one period so any offset
*value (e.g. an ever-increasing animation counter) stays in range.*/
int32_t phase = stripes->offset % period;
int32_t n_stripes = half_diag / period + 1;
for(int32_t i = -n_stripes; i <= n_stripes; i++) {
/*Center of this stripe, offset from widget center along the normal,
*plus the animation phase.*/
int32_t d = i * period + phase;
int32_t bcx = cx + ((int64_t)d * nx) / LV_TRIGO_SIN_MAX;
int32_t bcy = cy + ((int64_t)d * ny) / LV_TRIGO_SIN_MAX;
line_dsc.p1.x = bcx - lx;
line_dsc.p1.y = bcy - ly;
line_dsc.p2.x = bcx + lx;
line_dsc.p2.y = bcy + ly;
lv_draw_line(target_layer, &line_dsc);
}
}
/**
* Draw the stripe pattern into an offscreen ARGB layer covering only
* `band`, mask it to the object's rounded rectangle, then composite it
* back. Used only for the two thin corner strips (top and bottom) where
* rounded corners actually exist.
*/
static void draw_stripes_masked_band(lv_stripes_t * stripes, lv_layer_t * layer, const lv_area_t * coords,
int32_t radius, const lv_area_t * band)
{
lv_layer_t * layer_band = lv_draw_layer_create(layer, LV_COLOR_FORMAT_ARGB8888, band);
if(layer_band == NULL) {
/*Out of memory: draw directly, corners of this strip may overflow*/
const lv_area_t clip_ori = layer->_clip_area;
layer->_clip_area = *band;
draw_stripes_pattern(stripes, layer, coords);
layer->_clip_area = clip_ori;
return;
}
draw_stripes_pattern(stripes, layer_band, coords);
lv_draw_mask_rect_dsc_t mask_dsc;
lv_draw_mask_rect_dsc_init(&mask_dsc);
mask_dsc.area = *coords;
mask_dsc.radius = radius;
lv_draw_mask_rect(layer_band, &mask_dsc);
lv_draw_image_dsc_t img_dsc;
lv_draw_image_dsc_init(&img_dsc);
img_dsc.src = layer_band;
lv_draw_layer(layer, &img_dsc, band);
}
/**
* Draw the stripes for LV_EVENT_DRAW_MAIN.
*
* Fast path (no rounded corners): the object is a plain rectangle, so the
* stripes are drawn directly onto the main layer. LVGL already restricted
* `layer->_clip_area` to the object's rectangular bounds, which is all the
* clipping we need, so this costs nothing more than the line draws.
*
* Rounded path (radius > 0): rounded corners only exist in the top and
* bottom `radius`-high strips of the widget. So, following LVGL's own
* `clip_corner` implementation in `lv_refr.c`, the widget is split into
* three horizontal bands:
* - the middle band (the bulk of the widget) is drawn directly with a
* plain rectangular clip - no offscreen buffer, full speed;
* - the top and bottom strips (each only `radius` pixels high) are each
* rendered into a small ARGB layer, masked to the rounded rectangle,
* and composited back.
* This keeps the expensive offscreen path limited to the small corner
* regions instead of the whole widget.
*/
static void draw_stripes(lv_event_t * e)
{
lv_obj_t * obj = lv_event_get_current_target(e);
lv_layer_t * layer = lv_event_get_layer(e);
lv_stripes_t * stripes = (lv_stripes_t *)obj;
lv_area_t coords;
lv_area_copy(&coords, &obj->coords);
int32_t w = lv_area_get_width(&coords);
int32_t h = lv_area_get_height(&coords);
int32_t radius = lv_obj_get_style_radius(obj, LV_PART_MAIN);
int32_t short_side = LV_MIN(w, h);
int32_t rout = radius;
if(rout > short_side >> 1) rout = short_side >> 1;
/*Fast path: plain rectangle, draw straight onto the main layer.*/
if(rout <= 0) {
draw_stripes_pattern(stripes, layer, &coords);
return;
}
const lv_area_t clip_area_ori = layer->_clip_area;
/*Middle band: no rounded corners here, plain rectangular clip, drawn
*directly onto the main layer (this is the bulk of the widget).*/
lv_area_t mid = coords;
mid.y1 += rout;
mid.y2 -= rout;
lv_area_t mid_clip;
if(lv_area_intersect(&mid_clip, &mid, &clip_area_ori)) {
layer->_clip_area = mid_clip;
draw_stripes_pattern(stripes, layer, &coords);
layer->_clip_area = clip_area_ori;
}
/*Top strip: contains the two top rounded corners.*/
lv_area_t top = coords;
top.y2 = top.y1 + rout - 1;
lv_area_t top_clip;
if(lv_area_intersect(&top_clip, &top, &clip_area_ori)) {
draw_stripes_masked_band(stripes, layer, &coords, radius, &top_clip);
}
/*Bottom strip: contains the two bottom rounded corners.*/
lv_area_t bottom = coords;
bottom.y1 = bottom.y2 - rout + 1;
lv_area_t bottom_clip;
if(lv_area_intersect(&bottom_clip, &bottom, &clip_area_ori)) {
draw_stripes_masked_band(stripes, layer, &coords, radius, &bottom_clip);
}
layer->_clip_area = clip_area_ori;
}
static void lv_stripes_event(const lv_obj_class_t * class_p, lv_event_t * e)
{
LV_UNUSED(class_p);
/*Call the ancestor's event handler. For LV_EVENT_DRAW_MAIN it draws the
*standard lv_obj background and shadow (the border is deferred to
*LV_EVENT_DRAW_POST because we set `border_post`), and it handles
*LV_EVENT_COVER_CHECK, so the stripes' coverage matches the background.*/
lv_result_t res = lv_obj_event_base(MY_CLASS, e);
if(res != LV_RESULT_OK) return;
if(lv_event_get_code(e) == LV_EVENT_DRAW_MAIN) {
draw_stripes(e);
}
}
@@ -0,0 +1,156 @@
/**
* @file lv_stripes.h
*
* A rectangle widget filled with a repeating pattern of straight
* stripes drawn at an arbitrary angle, on top of the normal `lv_obj`
* background. Stripe color, opacity, width and inter-stripe gap width
* are all configurable, as is the pattern angle.
*
* The background (color/opacity/radius/border/...) is the regular
* `LV_PART_MAIN` style of `lv_obj`, so it composes with the normal
* LVGL style system. Stripes are drawn as a set of thick lines,
* clipped to the object's bounds.
*
* Part of the Tasmota project (https://github.com/arendst/Tasmota).
* Author: Stephan Hadinger
*/
#ifndef LV_STRIPES_H
#define LV_STRIPES_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "lvgl.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
extern const lv_obj_class_t lv_stripes_class;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a stripes object.
* @param parent pointer to an object, it will be the parent of the new stripes object
* @return pointer to the created stripes object
*/
lv_obj_t * lv_stripes_create(lv_obj_t * parent);
/*=====================
* Setter functions
*====================*/
/**
* Set the color of the stripes.
* @param obj pointer to a stripes object
* @param color color of the stripes
*/
void lv_stripes_set_stripe_color(lv_obj_t * obj, lv_color_t color);
/**
* Set the opacity of the stripes.
* @param obj pointer to a stripes object
* @param opa opacity 0 (transparent) .. 255 (opaque). `LV_OPA_*` constants can be used.
*/
void lv_stripes_set_stripe_opa(lv_obj_t * obj, lv_opa_t opa);
/**
* Set the width (thickness) of each stripe, in pixels.
* @param obj pointer to a stripes object
* @param width width of a stripe in pixels, minimum 1
*/
void lv_stripes_set_stripe_width(lv_obj_t * obj, int32_t width);
/**
* Set the width of the gap between two consecutive stripes, in pixels.
* @param obj pointer to a stripes object
* @param width width of the gap in pixels, 0 means stripes are contiguous
*/
void lv_stripes_set_gap_width(lv_obj_t * obj, int32_t width);
/**
* Set the angle of the stripes.
* @param obj pointer to a stripes object
* @param angle angle in degrees, 0 is horizontal stripes, 90 is vertical
* stripes. Any value is accepted and normalized to 0..359.
*/
void lv_stripes_set_angle(lv_obj_t * obj, int32_t angle);
/**
* Set the offset of the pattern.
* The pattern is shifted by `offset` pixels along the direction perpendicular
* to the stripes (the direction along which stripes are spaced). Because the
* pattern repeats every `stripe_width + gap_width` pixels, the offset wraps
* seamlessly, so it can be freely incremented to animate a scrolling motion.
* @param obj pointer to a stripes object
* @param offset offset in pixels, any value is accepted (wrapped internally)
*/
void lv_stripes_set_offset(lv_obj_t * obj, int32_t offset);
/*=====================
* Getter functions
*====================*/
/**
* Get the color of the stripes.
* @param obj pointer to a stripes object
* @return color of the stripes
*/
lv_color_t lv_stripes_get_stripe_color(lv_obj_t * obj);
/**
* Get the opacity of the stripes.
* @param obj pointer to a stripes object
* @return opacity 0..255
*/
lv_opa_t lv_stripes_get_stripe_opa(lv_obj_t * obj);
/**
* Get the width of each stripe.
* @param obj pointer to a stripes object
* @return width of a stripe in pixels
*/
int32_t lv_stripes_get_stripe_width(lv_obj_t * obj);
/**
* Get the width of the gap between two consecutive stripes.
* @param obj pointer to a stripes object
* @return width of the gap in pixels
*/
int32_t lv_stripes_get_gap_width(lv_obj_t * obj);
/**
* Get the angle of the stripes.
* @param obj pointer to a stripes object
* @return angle in degrees (0..359)
*/
int32_t lv_stripes_get_angle(lv_obj_t * obj);
/**
* Get the offset of the pattern.
* @param obj pointer to a stripes object
* @return offset in pixels (as last set, not wrapped)
*/
int32_t lv_stripes_get_offset(lv_obj_t * obj);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_STRIPES_H*/
@@ -588,6 +588,7 @@ extern "C" {
#include "be_ctypes.h"
#include "be_mapping.h"
#include "../src/lv_colorwheel.h"
#include "../src/lv_stripes.h"
"""
C_CLASSES_MAP_OPEN = """
@@ -712,6 +713,9 @@ extern int lvbe_theme_create(bvm *vm);
// adding ad-hoc colorwheel from LVGL8 to LVGL9
extern const lv_obj_class_t lv_colorwheel_class;
// custom Tasmota widget: rectangle filled with an angled stripe pattern
extern const lv_obj_class_t lv_stripes_class;
"""
WIDGETS_LIB_CORE_CLASSES = """
@@ -49,6 +49,8 @@ LV_WIDGETS = [
'qrcode',
# ad-hoc colorwheel back-ported from LVGL8 to LVGL9
'colorwheel',
# custom Tasmota widget: rectangle filled with an angled stripe pattern
'stripes',
]
# Widgets that do not have a backing ``lv_<name>_class`` C object.
@@ -578,6 +580,7 @@ FUNCTION_HEADER_EXTRA_PATHS = [
"../../LVGL_assets/src/lv_theme_haspmota.h",
"../src/lv_berry.h",
"../src/lv_colorwheel.h",
"../src/lv_stripes.h",
]
# Glob patterns (relative to LV_SRC_PREFIX) for headers scanned for enums.
@@ -3648,7 +3648,17 @@ class HASPmota
end
end
# Step 3.d. if not found, try to load a module with the name of the class
# Step 3.d. if not found, try `lv.<name>` as direct mapping of LVGL class
if obj_class == nil
# if not found, check if a LVGL class with name `lv.<name>` exists
var lv_cl = introspect.get(lv, obj_type)
if (lv_cl != nil) && (type(lv_cl) == 'class')
lv_instance = lv_cl(parent_lvgl)
obj_class = self.lvh_obj # use the basic lvh_obj component to encapsulate
end
end
# Step 3.e. if not found, try to load a module with the name of the class
if obj_class == nil
var lv_cl = introspect.module(obj_type)
if lv_cl != nil && type(lv_cl) == 'class'