mirror of
https://github.com/arendst/Tasmota.git
synced 2026-07-27 20:05:46 +00:00
HASPmota and LVGL stripes widget (#24907)
This commit is contained in:
@@ -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'
|
||||
|
||||
Reference in New Issue
Block a user