doc: renamed project

This commit is contained in:
SylvanArnold
2025-04-29 13:52:54 +02:00
committed by Sylvan Arnold
parent 244e516bd8
commit 32618389d1
985 changed files with 1 additions and 1 deletions

View File

@@ -0,0 +1,844 @@
/**
* @file lv_arc.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_arc.h"
#if LV_USE_ARC != 0
#include "../core/lv_group.h"
#include "../core/lv_indev.h"
#include "../misc/lv_assert.h"
#include "../misc/lv_math.h"
#include "../draw/lv_draw_arc.h"
/*********************
* DEFINES
*********************/
#define MY_CLASS &lv_arc_class
#define VALUE_UNSET INT16_MIN
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_arc_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_arc_draw(lv_event_t * e);
static void lv_arc_event(const lv_obj_class_t * class_p, lv_event_t * e);
static void inv_arc_area(lv_obj_t * arc, uint16_t start_angle, uint16_t end_angle, lv_part_t part);
static void inv_knob_area(lv_obj_t * obj);
static void get_center(const lv_obj_t * obj, lv_point_t * center, lv_coord_t * arc_r);
static lv_coord_t get_angle(const lv_obj_t * obj);
static void get_knob_area(lv_obj_t * arc, const lv_point_t * center, lv_coord_t r, lv_area_t * knob_area);
static void value_update(lv_obj_t * arc);
/**********************
* STATIC VARIABLES
**********************/
const lv_obj_class_t lv_arc_class = {
.constructor_cb = lv_arc_constructor,
.event_cb = lv_arc_event,
.instance_size = sizeof(lv_arc_t),
.editable = LV_OBJ_CLASS_EDITABLE_TRUE,
.base_class = &lv_obj_class
};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_obj_t * lv_arc_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;
}
/*======================
* Add/remove functions
*=====================*/
/*
* New object specific "add" or "remove" functions come here
*/
/*=====================
* Setter functions
*====================*/
void lv_arc_set_start_angle(lv_obj_t * obj, uint16_t start)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_arc_t * arc = (lv_arc_t *)obj;
if(start > 360) start -= 360;
int16_t old_delta = arc->indic_angle_end - arc->indic_angle_start;
int16_t new_delta = arc->indic_angle_end - start;
if(old_delta < 0) old_delta = 360 + old_delta;
if(new_delta < 0) new_delta = 360 + new_delta;
if(LV_ABS(new_delta - old_delta) > 180) lv_obj_invalidate(obj);
else if(new_delta < old_delta) inv_arc_area(obj, arc->indic_angle_start, start, LV_PART_INDICATOR);
else if(old_delta < new_delta) inv_arc_area(obj, start, arc->indic_angle_start, LV_PART_INDICATOR);
inv_knob_area(obj);
arc->indic_angle_start = start;
inv_knob_area(obj);
}
void lv_arc_set_end_angle(lv_obj_t * obj, uint16_t end)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_arc_t * arc = (lv_arc_t *)obj;
if(end > 360) end -= 360;
int16_t old_delta = arc->indic_angle_end - arc->indic_angle_start;
int16_t new_delta = end - arc->indic_angle_start;
if(old_delta < 0) old_delta = 360 + old_delta;
if(new_delta < 0) new_delta = 360 + new_delta;
if(LV_ABS(new_delta - old_delta) > 180) lv_obj_invalidate(obj);
else if(new_delta < old_delta) inv_arc_area(obj, end, arc->indic_angle_end, LV_PART_INDICATOR);
else if(old_delta < new_delta) inv_arc_area(obj, arc->indic_angle_end, end, LV_PART_INDICATOR);
inv_knob_area(obj);
arc->indic_angle_end = end;
inv_knob_area(obj);
}
void lv_arc_set_angles(lv_obj_t * obj, uint16_t start, uint16_t end)
{
lv_arc_set_end_angle(obj, end);
lv_arc_set_start_angle(obj, start);
}
void lv_arc_set_bg_start_angle(lv_obj_t * obj, uint16_t start)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_arc_t * arc = (lv_arc_t *)obj;
if(start > 360) start -= 360;
int16_t old_delta = arc->bg_angle_end - arc->bg_angle_start;
int16_t new_delta = arc->bg_angle_end - start;
if(old_delta < 0) old_delta = 360 + old_delta;
if(new_delta < 0) new_delta = 360 + new_delta;
if(LV_ABS(new_delta - old_delta) > 180) lv_obj_invalidate(obj);
else if(new_delta < old_delta) inv_arc_area(obj, arc->bg_angle_start, start, LV_PART_MAIN);
else if(old_delta < new_delta) inv_arc_area(obj, start, arc->bg_angle_start, LV_PART_MAIN);
arc->bg_angle_start = start;
value_update(obj);
}
void lv_arc_set_bg_end_angle(lv_obj_t * obj, uint16_t end)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_arc_t * arc = (lv_arc_t *)obj;
if(end > 360) end -= 360;
int16_t old_delta = arc->bg_angle_end - arc->bg_angle_start;
int16_t new_delta = end - arc->bg_angle_start;
if(old_delta < 0) old_delta = 360 + old_delta;
if(new_delta < 0) new_delta = 360 + new_delta;
if(LV_ABS(new_delta - old_delta) > 180) lv_obj_invalidate(obj);
else if(new_delta < old_delta) inv_arc_area(obj, end, arc->bg_angle_end, LV_PART_MAIN);
else if(old_delta < new_delta) inv_arc_area(obj, arc->bg_angle_end, end, LV_PART_MAIN);
arc->bg_angle_end = end;
value_update(obj);
}
void lv_arc_set_bg_angles(lv_obj_t * obj, uint16_t start, uint16_t end)
{
lv_arc_set_bg_end_angle(obj, end);
lv_arc_set_bg_start_angle(obj, start);
}
void lv_arc_set_rotation(lv_obj_t * obj, uint16_t rotation)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_arc_t * arc = (lv_arc_t *)obj;
arc->rotation = rotation;
lv_obj_invalidate(obj);
}
void lv_arc_set_mode(lv_obj_t * obj, lv_arc_mode_t type)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_arc_t * arc = (lv_arc_t *)obj;
int16_t val = arc->value;
arc->type = type;
arc->value = -1; /** Force set_value handling*/
int16_t bg_midpoint, bg_end = arc->bg_angle_end;
if(arc->bg_angle_end < arc->bg_angle_start) bg_end = arc->bg_angle_end + 360;
switch(arc->type) {
case LV_ARC_MODE_SYMMETRICAL:
bg_midpoint = (arc->bg_angle_start + bg_end) / 2;
lv_arc_set_start_angle(obj, bg_midpoint);
lv_arc_set_end_angle(obj, bg_midpoint);
break;
case LV_ARC_MODE_REVERSE:
lv_arc_set_end_angle(obj, arc->bg_angle_end);
break;
default: /** LV_ARC_TYPE_NORMAL*/
lv_arc_set_start_angle(obj, arc->bg_angle_start);
}
lv_arc_set_value(obj, val);
}
void lv_arc_set_value(lv_obj_t * obj, int16_t value)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_arc_t * arc = (lv_arc_t *)obj;
if(arc->value == value) return;
int16_t new_value;
new_value = value > arc->max_value ? arc->max_value : value;
new_value = new_value < arc->min_value ? arc->min_value : new_value;
if(arc->value == new_value) return;
arc->value = new_value;
value_update(obj);
}
void lv_arc_set_range(lv_obj_t * obj, int16_t min, int16_t max)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_arc_t * arc = (lv_arc_t *)obj;
if(arc->min_value == min && arc->max_value == max) return;
arc->min_value = min;
arc->max_value = max;
if(arc->value < min) {
arc->value = min;
}
if(arc->value > max) {
arc->value = max;
}
value_update(obj); /*value has changed relative to the new range*/
}
void lv_arc_set_change_rate(lv_obj_t * obj, uint16_t rate)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_arc_t * arc = (lv_arc_t *)obj;
arc->chg_rate = rate;
}
/*=====================
* Getter functions
*====================*/
uint16_t lv_arc_get_angle_start(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
return ((lv_arc_t *) obj)->indic_angle_start;
}
uint16_t lv_arc_get_angle_end(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
return ((lv_arc_t *) obj)->indic_angle_end;
}
uint16_t lv_arc_get_bg_angle_start(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
return ((lv_arc_t *) obj)->bg_angle_start;
}
uint16_t lv_arc_get_bg_angle_end(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
return ((lv_arc_t *) obj)->bg_angle_end;
}
int16_t lv_arc_get_value(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
return ((lv_arc_t *) obj)->value;
}
int16_t lv_arc_get_min_value(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
return ((lv_arc_t *) obj)->min_value;
}
int16_t lv_arc_get_max_value(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
return ((lv_arc_t *) obj)->max_value;
}
lv_arc_mode_t lv_arc_get_mode(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
return ((lv_arc_t *) obj)->type;
}
/*=====================
* Other functions
*====================*/
void lv_arc_align_obj_to_angle(const lv_obj_t * obj, lv_obj_t * obj_to_align, lv_coord_t r_offset)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
LV_ASSERT_NULL(obj_to_align);
lv_obj_update_layout(obj);
lv_point_t center;
lv_coord_t arc_r;
get_center(obj, &center, &arc_r);
lv_coord_t indic_width = lv_obj_get_style_arc_width(obj, LV_PART_INDICATOR);
lv_coord_t indic_width_half = indic_width / 2;
arc_r -= indic_width_half;
arc_r += r_offset;
uint16_t angle = get_angle(obj);
lv_coord_t knob_x = (arc_r * lv_trigo_sin(angle + 90)) >> LV_TRIGO_SHIFT;
lv_coord_t knob_y = (arc_r * lv_trigo_sin(angle)) >> LV_TRIGO_SHIFT;
lv_obj_align_to(obj_to_align, obj, LV_ALIGN_CENTER, knob_x, knob_y);
}
void lv_arc_rotate_obj_to_angle(const lv_obj_t * obj, lv_obj_t * obj_to_rotate, lv_coord_t r_offset)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
LV_ASSERT_NULL(obj_to_rotate);
lv_obj_update_layout(obj);
lv_point_t center;
lv_coord_t arc_r;
get_center(obj, &center, &arc_r);
lv_coord_t indic_width = lv_obj_get_style_arc_width(obj, LV_PART_INDICATOR);
lv_coord_t indic_width_half = indic_width / 2;
arc_r -= indic_width_half;
arc_r += r_offset;
lv_obj_align_to(obj_to_rotate, obj, LV_ALIGN_CENTER, 0, -arc_r);
lv_obj_update_layout(obj);
uint16_t angle = get_angle(obj);
lv_coord_t pivot_x = obj_to_rotate->coords.x1 - center.x;
lv_coord_t pivot_y = obj_to_rotate->coords.y1 - center.y;
lv_obj_set_style_transform_pivot_x(obj_to_rotate, -pivot_x, 0);
lv_obj_set_style_transform_pivot_y(obj_to_rotate, -pivot_y, 0);
lv_obj_set_style_transform_angle(obj_to_rotate, angle * 10 + 900, 0);
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_arc_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
LV_TRACE_OBJ_CREATE("begin");
lv_arc_t * arc = (lv_arc_t *)obj;
/*Initialize the allocated 'ext'*/
arc->rotation = 0;
arc->bg_angle_start = 135;
arc->bg_angle_end = 45;
arc->indic_angle_start = 135;
arc->indic_angle_end = 270;
arc->type = LV_ARC_MODE_NORMAL;
arc->value = VALUE_UNSET;
arc->min_close = 1;
arc->min_value = 0;
arc->max_value = 100;
arc->dragging = false;
arc->chg_rate = 720;
arc->last_tick = lv_tick_get();
arc->last_angle = arc->indic_angle_end;
lv_obj_add_flag(obj, LV_OBJ_FLAG_CLICKABLE);
lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLL_CHAIN | LV_OBJ_FLAG_SCROLLABLE);
lv_obj_set_ext_click_area(obj, LV_DPI_DEF / 10);
LV_TRACE_OBJ_CREATE("finished");
}
static void lv_arc_event(const lv_obj_class_t * class_p, lv_event_t * e)
{
LV_UNUSED(class_p);
lv_res_t res;
/*Call the ancestor's event handler*/
res = lv_obj_event_base(MY_CLASS, e);
if(res != LV_RES_OK) return;
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * obj = lv_event_get_target(e);
lv_arc_t * arc = (lv_arc_t *)lv_event_get_target(e);
if(code == LV_EVENT_PRESSING) {
lv_indev_t * indev = lv_indev_get_act();
if(indev == NULL) return;
/*Handle only pointers here*/
lv_indev_type_t indev_type = lv_indev_get_type(indev);
if(indev_type != LV_INDEV_TYPE_POINTER) return;
lv_point_t p;
lv_indev_get_point(indev, &p);
/*Make point relative to the arc's center*/
lv_point_t center;
lv_coord_t r;
get_center(obj, &center, &r);
p.x -= center.x;
p.y -= center.y;
/*Enter dragging mode if pressed out of the knob*/
if(arc->dragging == false) {
lv_coord_t indic_width = lv_obj_get_style_arc_width(obj, LV_PART_INDICATOR);
r -= indic_width;
/*Add some more sensitive area if there is no advanced git testing.
* (Advanced hit testing is more precise)*/
if(lv_obj_has_flag(obj, LV_OBJ_FLAG_ADV_HITTEST)) {
r -= indic_width;
}
else {
r -= LV_MAX(r / 4, indic_width);
}
if(r < 1) r = 1;
if(p.x * p.x + p.y * p.y > r * r) {
arc->dragging = true;
arc->last_tick = lv_tick_get(); /*Capture timestamp at dragging start*/
}
}
/*It must be in "dragging" mode to turn the arc*/
if(arc->dragging == false) return;
/*No angle can be determined if exactly the middle of the arc is being pressed*/
if(p.x == 0 && p.y == 0) return;
/*Calculate the angle of the pressed point*/
int16_t angle;
int16_t bg_end = arc->bg_angle_end;
if(arc->bg_angle_end < arc->bg_angle_start) {
bg_end = arc->bg_angle_end + 360;
}
angle = lv_atan2(p.y, p.x);
angle -= arc->rotation;
angle -= arc->bg_angle_start; /*Make the angle relative to the start angle*/
if(angle < 0) angle += 360;
int16_t deg_range = bg_end - arc->bg_angle_start;
int16_t last_angle_rel = arc->last_angle - arc->bg_angle_start;
int16_t delta_angle = angle - last_angle_rel;
/*Do not allow big jumps.
*It's mainly to avoid jumping to the opposite end if the "dead" range between min. and max. is crossed.
*Check which end was closer on the last valid press (arc->min_close) and prefer that end*/
if(LV_ABS(delta_angle) > 280) {
if(arc->min_close) angle = 0;
else angle = deg_range;
}
else {
if(angle < deg_range / 2)arc->min_close = 1;
else arc->min_close = 0;
}
/*Calculate the slew rate limited angle based on change rate (degrees/sec)*/
delta_angle = angle - last_angle_rel;
uint32_t delta_tick = lv_tick_elaps(arc->last_tick);
int16_t delta_angle_max = (arc->chg_rate * delta_tick) / 1000;
if(delta_angle > delta_angle_max) {
delta_angle = delta_angle_max;
}
else if(delta_angle < -delta_angle_max) {
delta_angle = -delta_angle_max;
}
angle = last_angle_rel + delta_angle; /*Apply the limited angle change*/
/*Rounding for symmetry*/
int32_t round = ((bg_end - arc->bg_angle_start) * 8) / (arc->max_value - arc->min_value);
round = (round + 4) >> 4;
angle += round;
angle += arc->bg_angle_start; /*Make the angle absolute again*/
/*Set the new value*/
int16_t old_value = arc->value;
int16_t new_value = lv_map(angle, arc->bg_angle_start, bg_end, arc->min_value, arc->max_value);
if(new_value != lv_arc_get_value(obj)) {
arc->last_tick = lv_tick_get(); /*Cache timestamp for the next iteration*/
lv_arc_set_value(obj, new_value); /*set_value caches the last_angle for the next iteration*/
if(new_value != old_value) {
res = lv_event_send(obj, LV_EVENT_VALUE_CHANGED, NULL);
if(res != LV_RES_OK) return;
}
}
/*Don't let the elapsed time become too big while sitting on an end point*/
if(new_value == arc->min_value || new_value == arc->max_value) {
arc->last_tick = lv_tick_get(); /*Cache timestamp for the next iteration*/
}
}
else if(code == LV_EVENT_RELEASED || code == LV_EVENT_PRESS_LOST) {
arc->dragging = false;
/*Leave edit mode if released. (No need to wait for LONG_PRESS)*/
lv_group_t * g = lv_obj_get_group(obj);
bool editing = lv_group_get_editing(g);
lv_indev_type_t indev_type = lv_indev_get_type(lv_indev_get_act());
if(indev_type == LV_INDEV_TYPE_ENCODER) {
if(editing) lv_group_set_editing(g, false);
}
}
else if(code == LV_EVENT_KEY) {
char c = *((char *)lv_event_get_param(e));
int16_t old_value = arc->value;
if(c == LV_KEY_RIGHT || c == LV_KEY_UP) {
lv_arc_set_value(obj, lv_arc_get_value(obj) + 1);
}
else if(c == LV_KEY_LEFT || c == LV_KEY_DOWN) {
lv_arc_set_value(obj, lv_arc_get_value(obj) - 1);
}
if(old_value != arc->value) {
res = lv_event_send(obj, LV_EVENT_VALUE_CHANGED, NULL);
if(res != LV_RES_OK) return;
}
}
else if(code == LV_EVENT_HIT_TEST) {
lv_hit_test_info_t * info = lv_event_get_param(e);;
lv_point_t p;
lv_coord_t r;
get_center(obj, &p, &r);
lv_coord_t ext_click_area = 0;
if(obj->spec_attr) ext_click_area = obj->spec_attr->ext_click_pad;
lv_coord_t w = lv_obj_get_style_arc_width(obj, LV_PART_MAIN);
r -= w + ext_click_area;
lv_area_t a;
/*Invalid if clicked inside*/
lv_area_set(&a, p.x - r, p.y - r, p.x + r, p.y + r);
if(_lv_area_is_point_on(&a, info->point, LV_RADIUS_CIRCLE)) {
info->res = false;
return;
}
/*Valid if no clicked outside*/
lv_area_increase(&a, w + ext_click_area * 2, w + ext_click_area * 2);
info->res = _lv_area_is_point_on(&a, info->point, LV_RADIUS_CIRCLE);
}
else if(code == LV_EVENT_REFR_EXT_DRAW_SIZE) {
lv_coord_t bg_left = lv_obj_get_style_pad_left(obj, LV_PART_MAIN);
lv_coord_t bg_right = lv_obj_get_style_pad_right(obj, LV_PART_MAIN);
lv_coord_t bg_top = lv_obj_get_style_pad_top(obj, LV_PART_MAIN);
lv_coord_t bg_bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_MAIN);
lv_coord_t bg_pad = LV_MAX4(bg_left, bg_right, bg_top, bg_bottom);
lv_coord_t knob_left = lv_obj_get_style_pad_left(obj, LV_PART_KNOB);
lv_coord_t knob_right = lv_obj_get_style_pad_right(obj, LV_PART_KNOB);
lv_coord_t knob_top = lv_obj_get_style_pad_top(obj, LV_PART_KNOB);
lv_coord_t knob_bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_KNOB);
lv_coord_t knob_pad = LV_MAX4(knob_left, knob_right, knob_top, knob_bottom) + 2;
lv_coord_t * s = lv_event_get_param(e);
*s = LV_MAX(*s, knob_pad - bg_pad);
}
else if(code == LV_EVENT_DRAW_MAIN) {
lv_arc_draw(e);
}
}
static void lv_arc_draw(lv_event_t * e)
{
lv_obj_t * obj = lv_event_get_target(e);
lv_arc_t * arc = (lv_arc_t *)obj;
lv_draw_ctx_t * draw_ctx = lv_event_get_draw_ctx(e);
lv_point_t center;
lv_coord_t arc_r;
get_center(obj, &center, &arc_r);
lv_obj_draw_part_dsc_t part_draw_dsc;
lv_obj_draw_dsc_init(&part_draw_dsc, draw_ctx);
/*Draw the background arc*/
lv_draw_arc_dsc_t arc_dsc;
if(arc_r > 0) {
lv_draw_arc_dsc_init(&arc_dsc);
lv_obj_init_draw_arc_dsc(obj, LV_PART_MAIN, &arc_dsc);
part_draw_dsc.part = LV_PART_MAIN;
part_draw_dsc.class_p = MY_CLASS;
part_draw_dsc.type = LV_ARC_DRAW_PART_BACKGROUND;
part_draw_dsc.p1 = &center;
part_draw_dsc.radius = arc_r;
part_draw_dsc.arc_dsc = &arc_dsc;
lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &part_draw_dsc);
lv_draw_arc(draw_ctx, &arc_dsc, &center, part_draw_dsc.radius, arc->bg_angle_start + arc->rotation,
arc->bg_angle_end + arc->rotation);
lv_event_send(obj, LV_EVENT_DRAW_PART_END, &part_draw_dsc);
}
/*Make the indicator arc smaller or larger according to its greatest padding value*/
lv_coord_t left_indic = lv_obj_get_style_pad_left(obj, LV_PART_INDICATOR);
lv_coord_t right_indic = lv_obj_get_style_pad_right(obj, LV_PART_INDICATOR);
lv_coord_t top_indic = lv_obj_get_style_pad_top(obj, LV_PART_INDICATOR);
lv_coord_t bottom_indic = lv_obj_get_style_pad_bottom(obj, LV_PART_INDICATOR);
lv_coord_t indic_r = arc_r - LV_MAX4(left_indic, right_indic, top_indic, bottom_indic);
if(indic_r > 0) {
lv_draw_arc_dsc_init(&arc_dsc);
lv_obj_init_draw_arc_dsc(obj, LV_PART_INDICATOR, &arc_dsc);
part_draw_dsc.part = LV_PART_INDICATOR;
part_draw_dsc.class_p = MY_CLASS;
part_draw_dsc.type = LV_ARC_DRAW_PART_FOREGROUND;
part_draw_dsc.p1 = &center;
part_draw_dsc.radius = indic_r;
part_draw_dsc.arc_dsc = &arc_dsc;
lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &part_draw_dsc);
if(arc_dsc.width > part_draw_dsc.radius) arc_dsc.width = part_draw_dsc.radius;
lv_draw_arc(draw_ctx, &arc_dsc, &center, part_draw_dsc.radius, arc->indic_angle_start + arc->rotation,
arc->indic_angle_end + arc->rotation);
lv_event_send(obj, LV_EVENT_DRAW_PART_END, &part_draw_dsc);
}
lv_area_t knob_area;
get_knob_area(obj, &center, arc_r, &knob_area);
lv_draw_rect_dsc_t knob_rect_dsc;
lv_draw_rect_dsc_init(&knob_rect_dsc);
lv_obj_init_draw_rect_dsc(obj, LV_PART_KNOB, &knob_rect_dsc);
part_draw_dsc.part = LV_PART_KNOB;
part_draw_dsc.class_p = MY_CLASS;
part_draw_dsc.type = LV_ARC_DRAW_PART_KNOB;
part_draw_dsc.draw_area = &knob_area;
part_draw_dsc.rect_dsc = &knob_rect_dsc;
lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &part_draw_dsc);
lv_draw_rect(draw_ctx, &knob_rect_dsc, &knob_area);
lv_event_send(obj, LV_EVENT_DRAW_PART_END, &part_draw_dsc);
}
static void inv_arc_area(lv_obj_t * obj, uint16_t start_angle, uint16_t end_angle, lv_part_t part)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
/*Skip this complicated invalidation if the arc is not visible*/
if(lv_obj_is_visible(obj) == false) return;
lv_arc_t * arc = (lv_arc_t *)obj;
if(start_angle == end_angle) return;
if(start_angle > 360) start_angle -= 360;
if(end_angle > 360) end_angle -= 360;
start_angle += arc->rotation;
end_angle += arc->rotation;
if(start_angle > 360) start_angle -= 360;
if(end_angle > 360) end_angle -= 360;
lv_coord_t r;
lv_point_t c;
get_center(obj, &c, &r);
lv_coord_t w = lv_obj_get_style_arc_width(obj, part);
lv_coord_t rounded = lv_obj_get_style_arc_rounded(obj, part);
lv_area_t inv_area;
lv_draw_arc_get_area(c.x, c.y, r, start_angle, end_angle, w, rounded, &inv_area);
lv_obj_invalidate_area(obj, &inv_area);
}
static void inv_knob_area(lv_obj_t * obj)
{
lv_point_t c;
lv_coord_t r;
get_center(obj, &c, &r);
lv_area_t a;
get_knob_area(obj, &c, r, &a);
lv_obj_invalidate_area(obj, &a);
}
static void get_center(const lv_obj_t * obj, lv_point_t * center, lv_coord_t * arc_r)
{
lv_coord_t left_bg = lv_obj_get_style_pad_left(obj, LV_PART_MAIN);
lv_coord_t right_bg = lv_obj_get_style_pad_right(obj, LV_PART_MAIN);
lv_coord_t top_bg = lv_obj_get_style_pad_top(obj, LV_PART_MAIN);
lv_coord_t bottom_bg = lv_obj_get_style_pad_bottom(obj, LV_PART_MAIN);
lv_coord_t r = (LV_MIN(lv_obj_get_width(obj) - left_bg - right_bg,
lv_obj_get_height(obj) - top_bg - bottom_bg)) / 2;
center->x = obj->coords.x1 + r + left_bg;
center->y = obj->coords.y1 + r + top_bg;
if(arc_r) *arc_r = r;
}
static lv_coord_t get_angle(const lv_obj_t * obj)
{
lv_arc_t * arc = (lv_arc_t *)obj;
uint16_t angle = arc->rotation;
if(arc->type == LV_ARC_MODE_NORMAL) {
angle += arc->indic_angle_end;
}
else if(arc->type == LV_ARC_MODE_REVERSE) {
angle += arc->indic_angle_start;
}
else if(arc->type == LV_ARC_MODE_SYMMETRICAL) {
int16_t bg_end = arc->bg_angle_end;
if(arc->bg_angle_end < arc->bg_angle_start) bg_end = arc->bg_angle_end + 360;
int16_t indic_end = arc->indic_angle_end;
if(arc->indic_angle_end < arc->indic_angle_start) indic_end = arc->indic_angle_end + 360;
int32_t angle_midpoint = (int32_t)(arc->bg_angle_start + bg_end) / 2;
if(arc->indic_angle_start < angle_midpoint) angle += arc->indic_angle_start;
else if(indic_end > angle_midpoint) angle += arc->indic_angle_end;
else angle += angle_midpoint;
}
return angle;
}
static void get_knob_area(lv_obj_t * obj, const lv_point_t * center, lv_coord_t r, lv_area_t * knob_area)
{
lv_coord_t indic_width = lv_obj_get_style_arc_width(obj, LV_PART_INDICATOR);
lv_coord_t indic_width_half = indic_width / 2;
r -= indic_width_half;
lv_coord_t angle = get_angle(obj);
lv_coord_t knob_x = (r * lv_trigo_sin(angle + 90)) >> LV_TRIGO_SHIFT;
lv_coord_t knob_y = (r * lv_trigo_sin(angle)) >> LV_TRIGO_SHIFT;
lv_coord_t left_knob = lv_obj_get_style_pad_left(obj, LV_PART_KNOB);
lv_coord_t right_knob = lv_obj_get_style_pad_right(obj, LV_PART_KNOB);
lv_coord_t top_knob = lv_obj_get_style_pad_top(obj, LV_PART_KNOB);
lv_coord_t bottom_knob = lv_obj_get_style_pad_bottom(obj, LV_PART_KNOB);
knob_area->x1 = center->x + knob_x - left_knob - indic_width_half;
knob_area->x2 = center->x + knob_x + right_knob + indic_width_half;
knob_area->y1 = center->y + knob_y - top_knob - indic_width_half;
knob_area->y2 = center->y + knob_y + bottom_knob + indic_width_half;
}
/**
* Used internally to update arc angles after a value change
* @param arc pointer to an arc object
*/
static void value_update(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_arc_t * arc = (lv_arc_t *)obj;
/*If the value is still not set to any value do not update*/
if(arc->value == VALUE_UNSET) return;
int16_t bg_midpoint, range_midpoint, bg_end = arc->bg_angle_end;
if(arc->bg_angle_end < arc->bg_angle_start) bg_end = arc->bg_angle_end + 360;
int16_t angle;
switch(arc->type) {
case LV_ARC_MODE_SYMMETRICAL:
bg_midpoint = (arc->bg_angle_start + bg_end) / 2;
range_midpoint = (int32_t)(arc->min_value + arc->max_value) / 2;
if(arc->value < range_midpoint) {
angle = lv_map(arc->value, arc->min_value, range_midpoint, arc->bg_angle_start, bg_midpoint);
lv_arc_set_start_angle(obj, angle);
lv_arc_set_end_angle(obj, bg_midpoint);
}
else {
angle = lv_map(arc->value, range_midpoint, arc->max_value, bg_midpoint, bg_end);
lv_arc_set_start_angle(obj, bg_midpoint);
lv_arc_set_end_angle(obj, angle);
}
break;
case LV_ARC_MODE_REVERSE:
angle = lv_map(arc->value, arc->min_value, arc->max_value, bg_end, arc->bg_angle_start);
lv_arc_set_angles(obj, angle, arc->bg_angle_end);
break;
case LV_ARC_MODE_NORMAL:
angle = lv_map(arc->value, arc->min_value, arc->max_value, arc->bg_angle_start, bg_end);
lv_arc_set_angles(obj, arc->bg_angle_start, angle);
break;
default:
LV_LOG_WARN("Invalid mode: %d", arc->type);
return;
}
arc->last_angle = angle; /*Cache angle for slew rate limiting*/
}
#endif

View File

@@ -0,0 +1,256 @@
/**
* @file lv_arc.h
*
*/
#ifndef LV_ARC_H
#define LV_ARC_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../lv_conf_internal.h"
#if LV_USE_ARC != 0
#include "../core/lv_obj.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
enum {
LV_ARC_MODE_NORMAL,
LV_ARC_MODE_SYMMETRICAL,
LV_ARC_MODE_REVERSE
};
typedef uint8_t lv_arc_mode_t;
typedef struct {
lv_obj_t obj;
uint16_t rotation;
uint16_t indic_angle_start;
uint16_t indic_angle_end;
uint16_t bg_angle_start;
uint16_t bg_angle_end;
int16_t value; /*Current value of the arc*/
int16_t min_value; /*Minimum value of the arc*/
int16_t max_value; /*Maximum value of the arc*/
uint16_t dragging : 1;
uint16_t type : 2;
uint16_t min_close : 1; /*1: the last pressed angle was closer to minimum end*/
uint16_t chg_rate; /*Drag angle rate of change of the arc (degrees/sec)*/
uint32_t last_tick; /*Last dragging event timestamp of the arc*/
int16_t last_angle; /*Last dragging angle of the arc*/
} lv_arc_t;
extern const lv_obj_class_t lv_arc_class;
/**
* `type` field in `lv_obj_draw_part_dsc_t` if `class_p = lv_arc_class`
* Used in `LV_EVENT_DRAW_PART_BEGIN` and `LV_EVENT_DRAW_PART_END`
*/
typedef enum {
LV_ARC_DRAW_PART_BACKGROUND, /**< The background arc*/
LV_ARC_DRAW_PART_FOREGROUND, /**< The foreground arc*/
LV_ARC_DRAW_PART_KNOB, /**< The knob*/
} lv_arc_draw_part_type_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create an arc object
* @param parent pointer to an object, it will be the parent of the new arc
* @return pointer to the created arc
*/
lv_obj_t * lv_arc_create(lv_obj_t * parent);
/*======================
* Add/remove functions
*=====================*/
/*=====================
* Setter functions
*====================*/
/**
* Set the start angle of an arc. 0 deg: right, 90 bottom, etc.
* @param obj pointer to an arc object
* @param start the start angle
*/
void lv_arc_set_start_angle(lv_obj_t * obj, uint16_t start);
/**
* Set the end angle of an arc. 0 deg: right, 90 bottom, etc.
* @param obj pointer to an arc object
* @param end the end angle
*/
void lv_arc_set_end_angle(lv_obj_t * obj, uint16_t end);
/**
* Set the start and end angles
* @param obj pointer to an arc object
* @param start the start angle
* @param end the end angle
*/
void lv_arc_set_angles(lv_obj_t * obj, uint16_t start, uint16_t end);
/**
* Set the start angle of an arc background. 0 deg: right, 90 bottom, etc.
* @param obj pointer to an arc object
* @param start the start angle
*/
void lv_arc_set_bg_start_angle(lv_obj_t * obj, uint16_t start);
/**
* Set the start angle of an arc background. 0 deg: right, 90 bottom etc.
* @param obj pointer to an arc object
* @param end the end angle
*/
void lv_arc_set_bg_end_angle(lv_obj_t * obj, uint16_t end);
/**
* Set the start and end angles of the arc background
* @param obj pointer to an arc object
* @param start the start angle
* @param end the end angle
*/
void lv_arc_set_bg_angles(lv_obj_t * obj, uint16_t start, uint16_t end);
/**
* Set the rotation for the whole arc
* @param obj pointer to an arc object
* @param rotation rotation angle
*/
void lv_arc_set_rotation(lv_obj_t * obj, uint16_t rotation);
/**
* Set the type of arc.
* @param obj pointer to arc object
* @param mode arc's mode
*/
void lv_arc_set_mode(lv_obj_t * obj, lv_arc_mode_t type);
/**
* Set a new value on the arc
* @param obj pointer to an arc object
* @param value new value
*/
void lv_arc_set_value(lv_obj_t * obj, int16_t value);
/**
* Set minimum and the maximum values of an arc
* @param obj pointer to the arc object
* @param min minimum value
* @param max maximum value
*/
void lv_arc_set_range(lv_obj_t * obj, int16_t min, int16_t max);
/**
* Set a change rate to limit the speed how fast the arc should reach the pressed point.
* @param obj pointer to an arc object
* @param rate the change rate
*/
void lv_arc_set_change_rate(lv_obj_t * obj, uint16_t rate);
/*=====================
* Getter functions
*====================*/
/**
* Get the start angle of an arc.
* @param obj pointer to an arc object
* @return the start angle [0..360]
*/
uint16_t lv_arc_get_angle_start(lv_obj_t * obj);
/**
* Get the end angle of an arc.
* @param obj pointer to an arc object
* @return the end angle [0..360]
*/
uint16_t lv_arc_get_angle_end(lv_obj_t * obj);
/**
* Get the start angle of an arc background.
* @param obj pointer to an arc object
* @return the start angle [0..360]
*/
uint16_t lv_arc_get_bg_angle_start(lv_obj_t * obj);
/**
* Get the end angle of an arc background.
* @param obj pointer to an arc object
* @return the end angle [0..360]
*/
uint16_t lv_arc_get_bg_angle_end(lv_obj_t * obj);
/**
* Get the value of an arc
* @param obj pointer to an arc object
* @return the value of the arc
*/
int16_t lv_arc_get_value(const lv_obj_t * obj);
/**
* Get the minimum value of an arc
* @param obj pointer to an arc object
* @return the minimum value of the arc
*/
int16_t lv_arc_get_min_value(const lv_obj_t * obj);
/**
* Get the maximum value of an arc
* @param obj pointer to an arc object
* @return the maximum value of the arc
*/
int16_t lv_arc_get_max_value(const lv_obj_t * obj);
/**
* Get whether the arc is type or not.
* @param obj pointer to an arc object
* @return arc's mode
*/
lv_arc_mode_t lv_arc_get_mode(const lv_obj_t * obj);
/*=====================
* Other functions
*====================*/
/**
* Align an object to the current position of the arc (knob)
* @param obj pointer to an arc object
* @param obj_to_align pointer to an object to align
* @param r_offset consider the radius larger with this value (< 0: for smaller radius)
*/
void lv_arc_align_obj_to_angle(const lv_obj_t * obj, lv_obj_t * obj_to_align, lv_coord_t r_offset);
/**
* Rotate an object to the current position of the arc (knob)
* @param obj pointer to an arc object
* @param obj_to_align pointer to an object to rotate
* @param r_offset consider the radius larger with this value (< 0: for smaller radius)
*/
void lv_arc_rotate_obj_to_angle(const lv_obj_t * obj, lv_obj_t * obj_to_rotate, lv_coord_t r_offset);
/**********************
* MACROS
**********************/
#endif /*LV_USE_ARC*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_ARC_H*/

View File

@@ -0,0 +1,611 @@
/**
* @file lv_bar.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_bar.h"
#if LV_USE_BAR != 0
#include "../misc/lv_assert.h"
#include "../draw/lv_draw.h"
#include "../misc/lv_anim.h"
#include "../misc/lv_math.h"
/*********************
* DEFINES
*********************/
#define MY_CLASS &lv_bar_class
/** hor. pad and ver. pad cannot make the indicator smaller than this [px]*/
#define LV_BAR_SIZE_MIN 4
#define LV_BAR_IS_ANIMATING(anim_struct) (((anim_struct).anim_state) != LV_BAR_ANIM_STATE_INV)
#define LV_BAR_GET_ANIM_VALUE(orig_value, anim_struct) (LV_BAR_IS_ANIMATING(anim_struct) ? ((anim_struct).anim_end) : (orig_value))
/** Bar animation start value. (Not the real value of the Bar just indicates process animation)*/
#define LV_BAR_ANIM_STATE_START 0
/** Bar animation end value. (Not the real value of the Bar just indicates process animation)*/
#define LV_BAR_ANIM_STATE_END 256
/** Mark no animation is in progress*/
#define LV_BAR_ANIM_STATE_INV -1
/** log2(LV_BAR_ANIM_STATE_END) used to normalize data*/
#define LV_BAR_ANIM_STATE_NORM 8
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_bar_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_bar_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_bar_event(const lv_obj_class_t * class_p, lv_event_t * e);
static void draw_indic(lv_event_t * e);
static void lv_bar_set_value_with_anim(lv_obj_t * obj, int32_t new_value, int32_t * value_ptr,
_lv_bar_anim_t * anim_info, lv_anim_enable_t en);
static void lv_bar_init_anim(lv_obj_t * bar, _lv_bar_anim_t * bar_anim);
static void lv_bar_anim(void * bar, int32_t value);
static void lv_bar_anim_ready(lv_anim_t * a);
/**********************
* STATIC VARIABLES
**********************/
const lv_obj_class_t lv_bar_class = {
.constructor_cb = lv_bar_constructor,
.destructor_cb = lv_bar_destructor,
.event_cb = lv_bar_event,
.width_def = LV_DPI_DEF * 2,
.height_def = LV_DPI_DEF / 10,
.instance_size = sizeof(lv_bar_t),
.base_class = &lv_obj_class
};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_obj_t * lv_bar_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_bar_set_value(lv_obj_t * obj, int32_t value, lv_anim_enable_t anim)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_bar_t * bar = (lv_bar_t *)obj;
if(bar->cur_value == value) return;
value = LV_CLAMP(bar->min_value, value, bar->max_value);
value = value < bar->start_value ? bar->start_value : value; /*Can't be smaller than the left value*/
if(bar->cur_value == value) return;
lv_bar_set_value_with_anim(obj, value, &bar->cur_value, &bar->cur_value_anim, anim);
}
void lv_bar_set_start_value(lv_obj_t * obj, int32_t value, lv_anim_enable_t anim)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_bar_t * bar = (lv_bar_t *)obj;
if(bar->mode != LV_BAR_MODE_RANGE) {
return;
}
value = LV_CLAMP(bar->min_value, value, bar->max_value);
value = value > bar->cur_value ? bar->cur_value : value; /*Can't be greater than the right value*/
if(bar->start_value == value) return;
lv_bar_set_value_with_anim(obj, value, &bar->start_value, &bar->start_value_anim, anim);
}
void lv_bar_set_range(lv_obj_t * obj, int32_t min, int32_t max)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_bar_t * bar = (lv_bar_t *)obj;
if(bar->min_value == min && bar->max_value == max) return;
bar->max_value = max;
bar->min_value = min;
if(lv_bar_get_mode(obj) != LV_BAR_MODE_RANGE)
bar->start_value = min;
if(bar->cur_value > max) {
bar->cur_value = max;
lv_bar_set_value(obj, bar->cur_value, LV_ANIM_OFF);
}
if(bar->cur_value < min) {
bar->cur_value = min;
lv_bar_set_value(obj, bar->cur_value, LV_ANIM_OFF);
}
lv_obj_invalidate(obj);
}
void lv_bar_set_mode(lv_obj_t * obj, lv_bar_mode_t mode)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_bar_t * bar = (lv_bar_t *)obj;
bar->mode = mode;
if(bar->mode != LV_BAR_MODE_RANGE) {
bar->start_value = bar->min_value;
}
lv_obj_invalidate(obj);
}
/*=====================
* Getter functions
*====================*/
int32_t lv_bar_get_value(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_bar_t * bar = (lv_bar_t *)obj;
return LV_BAR_GET_ANIM_VALUE(bar->cur_value, bar->cur_value_anim);
}
int32_t lv_bar_get_start_value(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_bar_t * bar = (lv_bar_t *)obj;
if(bar->mode != LV_BAR_MODE_RANGE) return bar->min_value;
return LV_BAR_GET_ANIM_VALUE(bar->start_value, bar->start_value_anim);
}
int32_t lv_bar_get_min_value(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_bar_t * bar = (lv_bar_t *)obj;
return bar->min_value;
}
int32_t lv_bar_get_max_value(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_bar_t * bar = (lv_bar_t *)obj;
return bar->max_value;
}
lv_bar_mode_t lv_bar_get_mode(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_bar_t * bar = (lv_bar_t *)obj;
return bar->mode;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_bar_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
LV_TRACE_OBJ_CREATE("begin");
lv_bar_t * bar = (lv_bar_t *)obj;
bar->min_value = 0;
bar->max_value = 100;
bar->start_value = 0;
bar->cur_value = 0;
bar->indic_area.x1 = 0;
bar->indic_area.x2 = 0;
bar->indic_area.y1 = 0;
bar->indic_area.y2 = 0;
bar->mode = LV_BAR_MODE_NORMAL;
lv_bar_init_anim(obj, &bar->cur_value_anim);
lv_bar_init_anim(obj, &bar->start_value_anim);
lv_obj_clear_flag(obj, LV_OBJ_FLAG_CHECKABLE);
lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE);
lv_bar_set_value(obj, 0, LV_ANIM_OFF);
LV_TRACE_OBJ_CREATE("finished");
}
static void lv_bar_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
lv_bar_t * bar = (lv_bar_t *)obj;
lv_anim_del(&bar->cur_value_anim, NULL);
lv_anim_del(&bar->start_value_anim, NULL);
}
static void draw_indic(lv_event_t * e)
{
lv_obj_t * obj = lv_event_get_target(e);
lv_bar_t * bar = (lv_bar_t *)obj;
lv_draw_ctx_t * draw_ctx = lv_event_get_draw_ctx(e);
lv_area_t bar_coords;
lv_obj_get_coords(obj, &bar_coords);
lv_coord_t transf_w = lv_obj_get_style_transform_width(obj, LV_PART_MAIN);
lv_coord_t transf_h = lv_obj_get_style_transform_height(obj, LV_PART_MAIN);
bar_coords.x1 -= transf_w;
bar_coords.x2 += transf_w;
bar_coords.y1 -= transf_h;
bar_coords.y2 += transf_h;
lv_coord_t barw = lv_area_get_width(&bar_coords);
lv_coord_t barh = lv_area_get_height(&bar_coords);
int32_t range = bar->max_value - bar->min_value;
bool hor = barw >= barh ? true : false;
bool sym = false;
if(bar->mode == LV_BAR_MODE_SYMMETRICAL && bar->min_value < 0 && bar->max_value > 0 &&
bar->start_value == bar->min_value) sym = true;
/*Calculate the indicator area*/
lv_coord_t bg_left = lv_obj_get_style_pad_left(obj, LV_PART_MAIN);
lv_coord_t bg_right = lv_obj_get_style_pad_right(obj, LV_PART_MAIN);
lv_coord_t bg_top = lv_obj_get_style_pad_top(obj, LV_PART_MAIN);
lv_coord_t bg_bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_MAIN);
/*Respect padding and minimum width/height too*/
lv_area_copy(&bar->indic_area, &bar_coords);
bar->indic_area.x1 += bg_left;
bar->indic_area.x2 -= bg_right;
bar->indic_area.y1 += bg_top;
bar->indic_area.y2 -= bg_bottom;
if(hor && lv_area_get_height(&bar->indic_area) < LV_BAR_SIZE_MIN) {
bar->indic_area.y1 = obj->coords.y1 + (barh / 2) - (LV_BAR_SIZE_MIN / 2);
bar->indic_area.y2 = bar->indic_area.y1 + LV_BAR_SIZE_MIN;
}
else if(!hor && lv_area_get_width(&bar->indic_area) < LV_BAR_SIZE_MIN) {
bar->indic_area.x1 = obj->coords.x1 + (barw / 2) - (LV_BAR_SIZE_MIN / 2);
bar->indic_area.x2 = bar->indic_area.x1 + LV_BAR_SIZE_MIN;
}
lv_coord_t indicw = lv_area_get_width(&bar->indic_area);
lv_coord_t indich = lv_area_get_height(&bar->indic_area);
/*Calculate the indicator length*/
lv_coord_t anim_length = hor ? indicw : indich;
lv_coord_t anim_cur_value_x, anim_start_value_x;
lv_coord_t * axis1, * axis2;
lv_coord_t (*indic_length_calc)(const lv_area_t * area);
if(hor) {
axis1 = &bar->indic_area.x1;
axis2 = &bar->indic_area.x2;
indic_length_calc = lv_area_get_width;
}
else {
axis1 = &bar->indic_area.y1;
axis2 = &bar->indic_area.y2;
indic_length_calc = lv_area_get_height;
}
if(LV_BAR_IS_ANIMATING(bar->start_value_anim)) {
lv_coord_t anim_start_value_start_x =
(int32_t)((int32_t)anim_length * (bar->start_value_anim.anim_start - bar->min_value)) / range;
lv_coord_t anim_start_value_end_x =
(int32_t)((int32_t)anim_length * (bar->start_value_anim.anim_end - bar->min_value)) / range;
anim_start_value_x = (((anim_start_value_end_x - anim_start_value_start_x) * bar->start_value_anim.anim_state) /
LV_BAR_ANIM_STATE_END);
anim_start_value_x += anim_start_value_start_x;
}
else {
anim_start_value_x = (int32_t)((int32_t)anim_length * (bar->start_value - bar->min_value)) / range;
}
if(LV_BAR_IS_ANIMATING(bar->cur_value_anim)) {
lv_coord_t anim_cur_value_start_x =
(int32_t)((int32_t)anim_length * (bar->cur_value_anim.anim_start - bar->min_value)) / range;
lv_coord_t anim_cur_value_end_x =
(int32_t)((int32_t)anim_length * (bar->cur_value_anim.anim_end - bar->min_value)) / range;
anim_cur_value_x = anim_cur_value_start_x + (((anim_cur_value_end_x - anim_cur_value_start_x) *
bar->cur_value_anim.anim_state) /
LV_BAR_ANIM_STATE_END);
}
else {
anim_cur_value_x = (int32_t)((int32_t)anim_length * (bar->cur_value - bar->min_value)) / range;
}
lv_base_dir_t base_dir = lv_obj_get_style_base_dir(obj, LV_PART_MAIN);
if(hor && base_dir == LV_BASE_DIR_RTL) {
/*Swap axes*/
lv_coord_t * tmp;
tmp = axis1;
axis1 = axis2;
axis2 = tmp;
anim_cur_value_x = -anim_cur_value_x;
anim_start_value_x = -anim_start_value_x;
}
/*Set the indicator length*/
if(hor) {
*axis2 = *axis1 + anim_cur_value_x;
*axis1 += anim_start_value_x;
}
else {
*axis1 = *axis2 - anim_cur_value_x + 1;
*axis2 -= anim_start_value_x;
}
if(sym) {
lv_coord_t zero, shift;
shift = (-bar->min_value * anim_length) / range;
if(hor) {
zero = *axis1 + shift;
if(*axis2 > zero)
*axis1 = zero;
else {
*axis1 = *axis2;
*axis2 = zero;
}
}
else {
zero = *axis2 - shift + 1;
if(*axis1 > zero)
*axis2 = zero;
else {
*axis2 = *axis1;
*axis1 = zero;
}
if(*axis2 < *axis1) {
/*swap*/
zero = *axis1;
*axis1 = *axis2;
*axis2 = zero;
}
}
}
/*Do not draw a zero length indicator but at least call the draw part events*/
if(!sym && indic_length_calc(&bar->indic_area) <= 1) {
lv_obj_draw_part_dsc_t part_draw_dsc;
lv_obj_draw_dsc_init(&part_draw_dsc, draw_ctx);
part_draw_dsc.part = LV_PART_INDICATOR;
part_draw_dsc.class_p = MY_CLASS;
part_draw_dsc.type = LV_BAR_DRAW_PART_INDICATOR;
part_draw_dsc.draw_area = &bar->indic_area;
lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &part_draw_dsc);
lv_event_send(obj, LV_EVENT_DRAW_PART_END, &part_draw_dsc);
return;
}
lv_area_t indic_area;
lv_area_copy(&indic_area, &bar->indic_area);
lv_draw_rect_dsc_t draw_rect_dsc;
lv_draw_rect_dsc_init(&draw_rect_dsc);
lv_obj_init_draw_rect_dsc(obj, LV_PART_INDICATOR, &draw_rect_dsc);
lv_obj_draw_part_dsc_t part_draw_dsc;
lv_obj_draw_dsc_init(&part_draw_dsc, draw_ctx);
part_draw_dsc.part = LV_PART_INDICATOR;
part_draw_dsc.class_p = MY_CLASS;
part_draw_dsc.type = LV_BAR_DRAW_PART_INDICATOR;
part_draw_dsc.rect_dsc = &draw_rect_dsc;
part_draw_dsc.draw_area = &bar->indic_area;
lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &part_draw_dsc);
lv_coord_t bg_radius = lv_obj_get_style_radius(obj, LV_PART_MAIN);
lv_coord_t short_side = LV_MIN(barw, barh);
if(bg_radius > short_side >> 1) bg_radius = short_side >> 1;
lv_coord_t indic_radius = draw_rect_dsc.radius;
short_side = LV_MIN(indicw, indich);
if(indic_radius > short_side >> 1) indic_radius = short_side >> 1;
/*Draw only the shadow and outline only if the indicator is long enough.
*The radius of the bg and the indicator can make a strange shape where
*it'd be very difficult to draw shadow.*/
if((hor && lv_area_get_width(&bar->indic_area) > indic_radius * 2) ||
(!hor && lv_area_get_height(&bar->indic_area) > indic_radius * 2)) {
lv_opa_t bg_opa = draw_rect_dsc.bg_opa;
lv_opa_t bg_img_opa = draw_rect_dsc.bg_img_opa;
lv_opa_t border_opa = draw_rect_dsc.border_opa;
draw_rect_dsc.bg_opa = LV_OPA_TRANSP;
draw_rect_dsc.bg_img_opa = LV_OPA_TRANSP;
draw_rect_dsc.border_opa = LV_OPA_TRANSP;
lv_draw_rect(draw_ctx, &draw_rect_dsc, &bar->indic_area);
draw_rect_dsc.bg_opa = bg_opa;
draw_rect_dsc.bg_img_opa = bg_img_opa;
draw_rect_dsc.border_opa = border_opa;
}
#if LV_DRAW_COMPLEX
lv_draw_mask_radius_param_t mask_bg_param;
lv_area_t bg_mask_area;
bg_mask_area.x1 = obj->coords.x1 + bg_left;
bg_mask_area.x2 = obj->coords.x2 - bg_right;
bg_mask_area.y1 = obj->coords.y1 + bg_top;
bg_mask_area.y2 = obj->coords.y2 - bg_bottom;
lv_draw_mask_radius_init(&mask_bg_param, &bg_mask_area, bg_radius, false);
lv_coord_t mask_bg_id = lv_draw_mask_add(&mask_bg_param, NULL);
#endif
/*Draw_only the background and background image*/
lv_opa_t shadow_opa = draw_rect_dsc.shadow_opa;
lv_opa_t border_opa = draw_rect_dsc.border_opa;
draw_rect_dsc.border_opa = LV_OPA_TRANSP;
draw_rect_dsc.shadow_opa = LV_OPA_TRANSP;
/*Get the max possible indicator area. The gradient should be applied on this*/
lv_area_t mask_indic_max_area;
lv_area_copy(&mask_indic_max_area, &bar_coords);
mask_indic_max_area.x1 += bg_left;
mask_indic_max_area.y1 += bg_top;
mask_indic_max_area.x2 -= bg_right;
mask_indic_max_area.y2 -= bg_bottom;
if(hor && lv_area_get_height(&mask_indic_max_area) < LV_BAR_SIZE_MIN) {
mask_indic_max_area.y1 = obj->coords.y1 + (barh / 2) - (LV_BAR_SIZE_MIN / 2);
mask_indic_max_area.y2 = mask_indic_max_area.y1 + LV_BAR_SIZE_MIN;
}
else if(!hor && lv_area_get_width(&mask_indic_max_area) < LV_BAR_SIZE_MIN) {
mask_indic_max_area.x1 = obj->coords.x1 + (barw / 2) - (LV_BAR_SIZE_MIN / 2);
mask_indic_max_area.x2 = mask_indic_max_area.x1 + LV_BAR_SIZE_MIN;
}
#if LV_DRAW_COMPLEX
/*Create a mask to the current indicator area to see only this part from the whole gradient.*/
lv_draw_mask_radius_param_t mask_indic_param;
lv_draw_mask_radius_init(&mask_indic_param, &bar->indic_area, draw_rect_dsc.radius, false);
int16_t mask_indic_id = lv_draw_mask_add(&mask_indic_param, NULL);
#endif
lv_draw_rect(draw_ctx, &draw_rect_dsc, &mask_indic_max_area);
draw_rect_dsc.border_opa = border_opa;
draw_rect_dsc.shadow_opa = shadow_opa;
/*Draw the border*/
draw_rect_dsc.bg_opa = LV_OPA_TRANSP;
draw_rect_dsc.bg_img_opa = LV_OPA_TRANSP;
draw_rect_dsc.shadow_opa = LV_OPA_TRANSP;
lv_draw_rect(draw_ctx, &draw_rect_dsc, &bar->indic_area);
#if LV_DRAW_COMPLEX
lv_draw_mask_free_param(&mask_indic_param);
lv_draw_mask_free_param(&mask_bg_param);
lv_draw_mask_remove_id(mask_indic_id);
lv_draw_mask_remove_id(mask_bg_id);
#endif
lv_event_send(obj, LV_EVENT_DRAW_PART_END, &part_draw_dsc);
}
static void lv_bar_event(const lv_obj_class_t * class_p, lv_event_t * e)
{
LV_UNUSED(class_p);
lv_res_t res;
/*Call the ancestor's event handler*/
res = lv_obj_event_base(MY_CLASS, e);
if(res != LV_RES_OK) return;
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * obj = lv_event_get_target(e);
if(code == LV_EVENT_REFR_EXT_DRAW_SIZE) {
lv_coord_t indic_size;
indic_size = lv_obj_calculate_ext_draw_size(obj, LV_PART_INDICATOR);
/*Bg size is handled by lv_obj*/
lv_coord_t * s = lv_event_get_param(e);
*s = LV_MAX(*s, indic_size);
/*Calculate the indicator area*/
lv_coord_t bg_left = lv_obj_get_style_pad_left(obj, LV_PART_MAIN);
lv_coord_t bg_right = lv_obj_get_style_pad_right(obj, LV_PART_MAIN);
lv_coord_t bg_top = lv_obj_get_style_pad_top(obj, LV_PART_MAIN);
lv_coord_t bg_bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_MAIN);
lv_coord_t pad = LV_MIN4(bg_left, bg_right, bg_top, bg_bottom);
if(pad < 0) {
*s = LV_MAX(*s, -pad);
}
}
else if(code == LV_EVENT_PRESSED || code == LV_EVENT_RELEASED) {
lv_bar_t * bar = (lv_bar_t *)obj;
lv_obj_invalidate_area(obj, &bar->indic_area);
}
else if(code == LV_EVENT_DRAW_MAIN) {
draw_indic(e);
}
}
static void lv_bar_anim(void * var, int32_t value)
{
_lv_bar_anim_t * bar_anim = var;
bar_anim->anim_state = value;
lv_obj_invalidate(bar_anim->bar);
}
static void lv_bar_anim_ready(lv_anim_t * a)
{
_lv_bar_anim_t * var = a->var;
lv_obj_t * obj = (lv_obj_t *)var->bar;
lv_bar_t * bar = (lv_bar_t *)obj;
var->anim_state = LV_BAR_ANIM_STATE_INV;
if(var == &bar->cur_value_anim)
bar->cur_value = var->anim_end;
else if(var == &bar->start_value_anim)
bar->start_value = var->anim_end;
lv_obj_invalidate(var->bar);
}
static void lv_bar_set_value_with_anim(lv_obj_t * obj, int32_t new_value, int32_t * value_ptr,
_lv_bar_anim_t * anim_info, lv_anim_enable_t en)
{
if(en == LV_ANIM_OFF) {
*value_ptr = new_value;
lv_obj_invalidate((lv_obj_t *)obj);
}
else {
/*No animation in progress -> simply set the values*/
if(anim_info->anim_state == LV_BAR_ANIM_STATE_INV) {
anim_info->anim_start = *value_ptr;
anim_info->anim_end = new_value;
}
/*Animation in progress. Start from the animation end value*/
else {
anim_info->anim_start = anim_info->anim_end;
anim_info->anim_end = new_value;
}
*value_ptr = new_value;
/*Stop the previous animation if it exists*/
lv_anim_del(anim_info, NULL);
lv_anim_t a;
lv_anim_init(&a);
lv_anim_set_var(&a, anim_info);
lv_anim_set_exec_cb(&a, lv_bar_anim);
lv_anim_set_values(&a, LV_BAR_ANIM_STATE_START, LV_BAR_ANIM_STATE_END);
lv_anim_set_ready_cb(&a, lv_bar_anim_ready);
lv_anim_set_time(&a, lv_obj_get_style_anim_time(obj, LV_PART_MAIN));
lv_anim_start(&a);
}
}
static void lv_bar_init_anim(lv_obj_t * obj, _lv_bar_anim_t * bar_anim)
{
bar_anim->bar = obj;
bar_anim->anim_start = 0;
bar_anim->anim_end = 0;
bar_anim->anim_state = LV_BAR_ANIM_STATE_INV;
}
#endif

View File

@@ -0,0 +1,164 @@
/**
* @file lv_bar.h
*
*/
#ifndef LV_BAR_H
#define LV_BAR_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../lv_conf_internal.h"
#if LV_USE_BAR != 0
#include "../core/lv_obj.h"
#include "../misc/lv_anim.h"
#include "lv_btn.h"
#include "lv_label.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
enum {
LV_BAR_MODE_NORMAL,
LV_BAR_MODE_SYMMETRICAL,
LV_BAR_MODE_RANGE
};
typedef uint8_t lv_bar_mode_t;
typedef struct {
lv_obj_t * bar;
int32_t anim_start;
int32_t anim_end;
int32_t anim_state;
} _lv_bar_anim_t;
typedef struct {
lv_obj_t obj;
int32_t cur_value; /**< Current value of the bar*/
int32_t min_value; /**< Minimum value of the bar*/
int32_t max_value; /**< Maximum value of the bar*/
int32_t start_value; /**< Start value of the bar*/
lv_area_t indic_area; /**< Save the indicator area. Might be used by derived types*/
_lv_bar_anim_t cur_value_anim;
_lv_bar_anim_t start_value_anim;
lv_bar_mode_t mode : 2; /**< Type of bar*/
} lv_bar_t;
extern const lv_obj_class_t lv_bar_class;
/**
* `type` field in `lv_obj_draw_part_dsc_t` if `class_p = lv_bar_class`
* Used in `LV_EVENT_DRAW_PART_BEGIN` and `LV_EVENT_DRAW_PART_END`
*/
typedef enum {
LV_BAR_DRAW_PART_INDICATOR, /**< The indicator*/
} lv_bar_draw_part_type_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a bar object
* @param parent pointer to an object, it will be the parent of the new bar
* @return pointer to the created bar
*/
lv_obj_t * lv_bar_create(lv_obj_t * parent);
/*=====================
* Setter functions
*====================*/
/**
* Set a new value on the bar
* @param bar pointer to a bar object
* @param value new value
* @param anim LV_ANIM_ON: set the value with an animation; LV_ANIM_OFF: change the value immediately
*/
void lv_bar_set_value(lv_obj_t * obj, int32_t value, lv_anim_enable_t anim);
/**
* Set a new start value on the bar
* @param obj pointer to a bar object
* @param value new start value
* @param anim LV_ANIM_ON: set the value with an animation; LV_ANIM_OFF: change the value immediately
*/
void lv_bar_set_start_value(lv_obj_t * obj, int32_t start_value, lv_anim_enable_t anim);
/**
* Set minimum and the maximum values of a bar
* @param obj pointer to the bar object
* @param min minimum value
* @param max maximum value
*/
void lv_bar_set_range(lv_obj_t * obj, int32_t min, int32_t max);
/**
* Set the type of bar.
* @param obj pointer to bar object
* @param mode bar type from ::lv_bar_mode_t
*/
void lv_bar_set_mode(lv_obj_t * obj, lv_bar_mode_t mode);
/*=====================
* Getter functions
*====================*/
/**
* Get the value of a bar
* @param obj pointer to a bar object
* @return the value of the bar
*/
int32_t lv_bar_get_value(const lv_obj_t * obj);
/**
* Get the start value of a bar
* @param obj pointer to a bar object
* @return the start value of the bar
*/
int32_t lv_bar_get_start_value(const lv_obj_t * obj);
/**
* Get the minimum value of a bar
* @param obj pointer to a bar object
* @return the minimum value of the bar
*/
int32_t lv_bar_get_min_value(const lv_obj_t * obj);
/**
* Get the maximum value of a bar
* @param obj pointer to a bar object
* @return the maximum value of the bar
*/
int32_t lv_bar_get_max_value(const lv_obj_t * obj);
/**
* Get the type of bar.
* @param obj pointer to bar object
* @return bar type from ::lv_bar_mode_t
*/
lv_bar_mode_t lv_bar_get_mode(lv_obj_t * obj);
/**********************
* MACROS
**********************/
#endif /*LV_USE_BAR*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_BAR_H*/

View File

@@ -0,0 +1,72 @@
/**
* @file lv_btn.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_btn.h"
#if LV_USE_BTN != 0
#include "../extra/layouts/flex/lv_flex.h"
/*********************
* DEFINES
*********************/
#define MY_CLASS &lv_btn_class
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_btn_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
/**********************
* STATIC VARIABLES
**********************/
const lv_obj_class_t lv_btn_class = {
.constructor_cb = lv_btn_constructor,
.width_def = LV_SIZE_CONTENT,
.height_def = LV_SIZE_CONTENT,
.group_def = LV_OBJ_CLASS_GROUP_DEF_TRUE,
.instance_size = sizeof(lv_btn_t),
.base_class = &lv_obj_class
};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_obj_t * lv_btn_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;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_btn_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
LV_TRACE_OBJ_CREATE("begin");
lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_add_flag(obj, LV_OBJ_FLAG_SCROLL_ON_FOCUS);
LV_TRACE_OBJ_CREATE("finished");
}
#endif

View File

@@ -0,0 +1,56 @@
/**
* @file lv_btn.h
*
*/
#ifndef LV_BTN_H
#define LV_BTN_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../lv_conf_internal.h"
#if LV_USE_BTN != 0
#include "../core/lv_obj.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_obj_t obj;
} lv_btn_t;
extern const lv_obj_class_t lv_btn_class;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a button object
* @param parent pointer to an object, it will be the parent of the new button
* @return pointer to the created button
*/
lv_obj_t * lv_btn_create(lv_obj_t * parent);
/**********************
* MACROS
**********************/
#endif /*LV_USE_BTN*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_BTN_H*/

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,225 @@
/**
* @file lv_btnmatrix.h
*
*/
#ifndef LV_BTNMATRIX_H
#define LV_BTNMATRIX_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../lv_conf_internal.h"
#if LV_USE_BTNMATRIX != 0
#include "../core/lv_obj.h"
/*********************
* DEFINES
*********************/
#define LV_BTNMATRIX_BTN_NONE 0xFFFF
LV_EXPORT_CONST_INT(LV_BTNMATRIX_BTN_NONE);
/**********************
* TYPEDEFS
**********************/
/** Type to store button control bits (disabled, hidden etc.)
* The first 3 bits are used to store the width*/
enum {
_LV_BTNMATRIX_WIDTH = 0x0007, /**< Reserved to stire the size units*/
LV_BTNMATRIX_CTRL_HIDDEN = 0x0008, /**< Button hidden*/
LV_BTNMATRIX_CTRL_NO_REPEAT = 0x0010, /**< Do not repeat press this button.*/
LV_BTNMATRIX_CTRL_DISABLED = 0x0020, /**< Disable this button.*/
LV_BTNMATRIX_CTRL_CHECKABLE = 0x0040, /**< The button can be toggled.*/
LV_BTNMATRIX_CTRL_CHECKED = 0x0080, /**< Button is currently toggled (e.g. checked).*/
LV_BTNMATRIX_CTRL_CLICK_TRIG = 0x0100, /**< 1: Send LV_EVENT_VALUE_CHANGE on CLICK, 0: Send LV_EVENT_VALUE_CHANGE on PRESS*/
LV_BTNMATRIX_CTRL_POPOVER = 0x0200, /**< Show a popover when pressing this key*/
LV_BTNMATRIX_CTRL_RECOLOR = 0x1000, /**< Enable text recoloring with `#color`*/
_LV_BTNMATRIX_CTRL_RESERVED = 0x2000, /**< Reserved for later use*/
LV_BTNMATRIX_CTRL_CUSTOM_1 = 0x4000, /**< Custom free to use flag*/
LV_BTNMATRIX_CTRL_CUSTOM_2 = 0x8000, /**< Custom free to use flag*/
};
typedef uint16_t lv_btnmatrix_ctrl_t;
typedef bool (*lv_btnmatrix_btn_draw_cb_t)(lv_obj_t * btnm, uint32_t btn_id, const lv_area_t * draw_area,
const lv_area_t * clip_area);
/*Data of button matrix*/
typedef struct {
lv_obj_t obj;
const char ** map_p; /*Pointer to the current map*/
lv_area_t * button_areas; /*Array of areas of buttons*/
lv_btnmatrix_ctrl_t * ctrl_bits; /*Array of control bytes*/
uint16_t btn_cnt; /*Number of button in 'map_p'(Handled by the library)*/
uint16_t row_cnt; /*Number of rows in 'map_p'(Handled by the library)*/
uint16_t btn_id_sel; /*Index of the active button (being pressed/released etc) or LV_BTNMATRIX_BTN_NONE*/
uint8_t one_check : 1; /*Single button toggled at once*/
} lv_btnmatrix_t;
extern const lv_obj_class_t lv_btnmatrix_class;
/**
* `type` field in `lv_obj_draw_part_dsc_t` if `class_p = lv_btnmatrix_class`
* Used in `LV_EVENT_DRAW_PART_BEGIN` and `LV_EVENT_DRAW_PART_END`
*/
typedef enum {
LV_BTNMATRIX_DRAW_PART_BTN, /**< The rectangle and label of buttons*/
} lv_btnmatrix_draw_part_type_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a button matrix object
* @param parent pointer to an object, it will be the parent of the new button matrix
* @return pointer to the created button matrix
*/
lv_obj_t * lv_btnmatrix_create(lv_obj_t * parent);
/*=====================
* Setter functions
*====================*/
/**
* Set a new map. Buttons will be created/deleted according to the map. The
* button matrix keeps a reference to the map and so the string array must not
* be deallocated during the life of the matrix.
* @param obj pointer to a button matrix object
* @param map pointer a string array. The last string has to be: "". Use "\n" to make a line break.
*/
void lv_btnmatrix_set_map(lv_obj_t * obj, const char * map[]);
/**
* Set the button control map (hidden, disabled etc.) for a button matrix.
* The control map array will be copied and so may be deallocated after this
* function returns.
* @param obj pointer to a button matrix object
* @param ctrl_map pointer to an array of `lv_btn_ctrl_t` control bytes. The
* length of the array and position of the elements must match
* the number and order of the individual buttons (i.e. excludes
* newline entries).
* An element of the map should look like e.g.:
* `ctrl_map[0] = width | LV_BTNMATRIX_CTRL_NO_REPEAT | LV_BTNMATRIX_CTRL_TGL_ENABLE`
*/
void lv_btnmatrix_set_ctrl_map(lv_obj_t * obj, const lv_btnmatrix_ctrl_t ctrl_map[]);
/**
* Set the selected buttons
* @param obj pointer to button matrix object
* @param btn_id 0 based index of the button to modify. (Not counting new lines)
*/
void lv_btnmatrix_set_selected_btn(lv_obj_t * obj, uint16_t btn_id);
/**
* Set the attributes of a button of the button matrix
* @param obj pointer to button matrix object
* @param btn_id 0 based index of the button to modify. (Not counting new lines)
* @param ctrl OR-ed attributs. E.g. `LV_BTNMATRIX_CTRL_NO_REPEAT | LV_BTNMATRIX_CTRL_CHECKABLE`
*/
void lv_btnmatrix_set_btn_ctrl(lv_obj_t * obj, uint16_t btn_id, lv_btnmatrix_ctrl_t ctrl);
/**
* Clear the attributes of a button of the button matrix
* @param obj pointer to button matrix object
* @param btn_id 0 based index of the button to modify. (Not counting new lines)
* @param ctrl OR-ed attributs. E.g. `LV_BTNMATRIX_CTRL_NO_REPEAT | LV_BTNMATRIX_CTRL_CHECKABLE`
*/
void lv_btnmatrix_clear_btn_ctrl(lv_obj_t * obj, uint16_t btn_id, lv_btnmatrix_ctrl_t ctrl);
/**
* Set attributes of all buttons of a button matrix
* @param obj pointer to a button matrix object
* @param ctrl attribute(s) to set from `lv_btnmatrix_ctrl_t`. Values can be ORed.
*/
void lv_btnmatrix_set_btn_ctrl_all(lv_obj_t * obj, lv_btnmatrix_ctrl_t ctrl);
/**
* Clear the attributes of all buttons of a button matrix
* @param obj pointer to a button matrix object
* @param ctrl attribute(s) to set from `lv_btnmatrix_ctrl_t`. Values can be ORed.
* @param en true: set the attributes; false: clear the attributes
*/
void lv_btnmatrix_clear_btn_ctrl_all(lv_obj_t * obj, lv_btnmatrix_ctrl_t ctrl);
/**
* Set a single button's relative width.
* This method will cause the matrix be regenerated and is a relatively
* expensive operation. It is recommended that initial width be specified using
* `lv_btnmatrix_set_ctrl_map` and this method only be used for dynamic changes.
* @param obj pointer to button matrix object
* @param btn_id 0 based index of the button to modify.
* @param width relative width compared to the buttons in the same row. [1..7]
*/
void lv_btnmatrix_set_btn_width(lv_obj_t * obj, uint16_t btn_id, uint8_t width);
/**
* Make the button matrix like a selector widget (only one button may be checked at a time).
* `LV_BTNMATRIX_CTRL_CHECKABLE` must be enabled on the buttons to be selected using
* `lv_btnmatrix_set_ctrl()` or `lv_btnmatrix_set_btn_ctrl_all()`.
* @param obj pointer to a button matrix object
* @param en whether "one check" mode is enabled
*/
void lv_btnmatrix_set_one_checked(lv_obj_t * obj, bool en);
/*=====================
* Getter functions
*====================*/
/**
* Get the current map of a button matrix
* @param obj pointer to a button matrix object
* @return the current map
*/
const char ** lv_btnmatrix_get_map(const lv_obj_t * obj);
/**
* Get the index of the lastly "activated" button by the user (pressed, released, focused etc)
* Useful in the `event_cb` to get the text of the button, check if hidden etc.
* @param obj pointer to button matrix object
* @return index of the last released button (LV_BTNMATRIX_BTN_NONE: if unset)
*/
uint16_t lv_btnmatrix_get_selected_btn(const lv_obj_t * obj);
/**
* Get the button's text
* @param obj pointer to button matrix object
* @param btn_id the index a button not counting new line characters.
* @return text of btn_index` button
*/
const char * lv_btnmatrix_get_btn_text(const lv_obj_t * obj, uint16_t btn_id);
/**
* Get the whether a control value is enabled or disabled for button of a button matrix
* @param obj pointer to a button matrix object
* @param btn_id the index of a button not counting new line characters.
* @param ctrl control values to check (ORed value can be used)
* @return true: the control attribute is enabled false: disabled
*/
bool lv_btnmatrix_has_btn_ctrl(lv_obj_t * obj, uint16_t btn_id, lv_btnmatrix_ctrl_t ctrl);
/**
* Tell whether "one check" mode is enabled or not.
* @param obj Button matrix object
* @return true: "one check" mode is enabled; false: disabled
*/
bool lv_btnmatrix_get_one_checked(const lv_obj_t * obj);
/**********************
* MACROS
**********************/
#endif /*LV_USE_BTNMATRIX*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_BTNMATRIX_H*/

View File

@@ -0,0 +1,836 @@
/**
* @file lv_canvas.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_canvas.h"
#include "../misc/lv_assert.h"
#include "../misc/lv_math.h"
#include "../draw/lv_draw.h"
#include "../core/lv_refr.h"
#if LV_USE_CANVAS != 0
#include "../draw/sw/lv_draw_sw.h"
/*********************
* DEFINES
*********************/
#define MY_CLASS &lv_canvas_class
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_canvas_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_canvas_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void init_fake_disp(lv_obj_t * canvas, lv_disp_t * disp, lv_disp_drv_t * drv, lv_area_t * clip_area);
static void deinit_fake_disp(lv_obj_t * canvas, lv_disp_t * disp);
/**********************
* STATIC VARIABLES
**********************/
const lv_obj_class_t lv_canvas_class = {
.constructor_cb = lv_canvas_constructor,
.destructor_cb = lv_canvas_destructor,
.instance_size = sizeof(lv_canvas_t),
.base_class = &lv_img_class
};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_obj_t * lv_canvas_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_canvas_set_buffer(lv_obj_t * obj, void * buf, lv_coord_t w, lv_coord_t h, lv_img_cf_t cf)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
LV_ASSERT_NULL(buf);
lv_canvas_t * canvas = (lv_canvas_t *)obj;
canvas->dsc.header.cf = cf;
canvas->dsc.header.w = w;
canvas->dsc.header.h = h;
canvas->dsc.data = buf;
lv_img_set_src(obj, &canvas->dsc);
lv_img_cache_invalidate_src(&canvas->dsc);
}
void lv_canvas_set_px_color(lv_obj_t * obj, lv_coord_t x, lv_coord_t y, lv_color_t c)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_canvas_t * canvas = (lv_canvas_t *)obj;
lv_img_buf_set_px_color(&canvas->dsc, x, y, c);
lv_obj_invalidate(obj);
}
void lv_canvas_set_px_opa(lv_obj_t * obj, lv_coord_t x, lv_coord_t y, lv_opa_t opa)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_canvas_t * canvas = (lv_canvas_t *)obj;
lv_img_buf_set_px_alpha(&canvas->dsc, x, y, opa);
lv_obj_invalidate(obj);
}
void lv_canvas_set_palette(lv_obj_t * obj, uint8_t id, lv_color_t c)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_canvas_t * canvas = (lv_canvas_t *)obj;
lv_img_buf_set_palette(&canvas->dsc, id, c);
lv_obj_invalidate(obj);
}
/*=====================
* Getter functions
*====================*/
lv_color_t lv_canvas_get_px(lv_obj_t * obj, lv_coord_t x, lv_coord_t y)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_canvas_t * canvas = (lv_canvas_t *)obj;
lv_color_t color = lv_obj_get_style_img_recolor(obj, LV_PART_MAIN);
return lv_img_buf_get_px_color(&canvas->dsc, x, y, color);
}
lv_img_dsc_t * lv_canvas_get_img(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_canvas_t * canvas = (lv_canvas_t *)obj;
return &canvas->dsc;
}
/*=====================
* Other functions
*====================*/
void lv_canvas_copy_buf(lv_obj_t * obj, const void * to_copy, lv_coord_t x, lv_coord_t y, lv_coord_t w, lv_coord_t h)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
LV_ASSERT_NULL(to_copy);
lv_canvas_t * canvas = (lv_canvas_t *)obj;
if(x + w - 1 >= (lv_coord_t)canvas->dsc.header.w || y + h - 1 >= (lv_coord_t)canvas->dsc.header.h) {
LV_LOG_WARN("lv_canvas_copy_buf: x or y out of the canvas");
return;
}
uint32_t px_size = lv_img_cf_get_px_size(canvas->dsc.header.cf) >> 3;
uint32_t px = canvas->dsc.header.w * y * px_size + x * px_size;
uint8_t * to_copy8 = (uint8_t *)to_copy;
lv_coord_t i;
for(i = 0; i < h; i++) {
lv_memcpy((void *)&canvas->dsc.data[px], to_copy8, w * px_size);
px += canvas->dsc.header.w * px_size;
to_copy8 += w * px_size;
}
}
void lv_canvas_transform(lv_obj_t * obj, lv_img_dsc_t * src_img, int16_t angle, uint16_t zoom, lv_coord_t offset_x,
lv_coord_t offset_y,
int32_t pivot_x, int32_t pivot_y, bool antialias)
{
#if LV_DRAW_COMPLEX
LV_ASSERT_OBJ(obj, MY_CLASS);
LV_ASSERT_NULL(src_img);
lv_canvas_t * canvas = (lv_canvas_t *)obj;
lv_img_dsc_t * dest_img = &canvas->dsc;
int32_t x;
int32_t y;
lv_draw_img_dsc_t draw_dsc;
lv_draw_img_dsc_init(&draw_dsc);
draw_dsc.angle = angle;
draw_dsc.zoom = zoom;
draw_dsc.pivot.x = pivot_x;
draw_dsc.pivot.y = pivot_y;
draw_dsc.antialias = antialias;
lv_area_t dest_area;
dest_area.x1 = -offset_x;
dest_area.x2 = dest_area.x1 + dest_img->header.w - 1;
dest_area.y1 = -offset_y;
dest_area.y2 = -offset_y;
lv_color_t * cbuf = lv_mem_alloc(dest_img->header.w * sizeof(lv_color_t));
lv_opa_t * abuf = lv_mem_alloc(dest_img->header.w * sizeof(lv_opa_t));
for(y = 0; y < dest_img->header.h; y++) {
if(y + offset_y >= 0) {
lv_draw_sw_transform(NULL, &dest_area, src_img->data, src_img->header.w, src_img->header.h, src_img->header.w,
&draw_dsc, canvas->dsc.header.cf, cbuf, abuf);
for(x = 0; x < dest_img->header.w; x++) {
if(abuf[x]) {
lv_img_buf_set_px_color(dest_img, x, y, cbuf[x]);
lv_img_buf_set_px_alpha(dest_img, x, y, abuf[x]);
}
}
dest_area.y1++;
dest_area.y2++;
}
}
lv_mem_free(cbuf);
lv_mem_free(abuf);
lv_obj_invalidate(obj);
#else
LV_UNUSED(obj);
LV_UNUSED(src_img);
LV_UNUSED(angle);
LV_UNUSED(zoom);
LV_UNUSED(offset_x);
LV_UNUSED(offset_y);
LV_UNUSED(pivot_x);
LV_UNUSED(pivot_y);
LV_UNUSED(antialias);
LV_LOG_WARN("Can't transform canvas with LV_DRAW_COMPLEX == 0");
#endif
}
void lv_canvas_blur_hor(lv_obj_t * obj, const lv_area_t * area, uint16_t r)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
if(r == 0) return;
lv_canvas_t * canvas = (lv_canvas_t *)obj;
lv_area_t a;
if(area) {
lv_area_copy(&a, area);
if(a.x1 < 0) a.x1 = 0;
if(a.y1 < 0) a.y1 = 0;
if(a.x2 > canvas->dsc.header.w - 1) a.x2 = canvas->dsc.header.w - 1;
if(a.y2 > canvas->dsc.header.h - 1) a.y2 = canvas->dsc.header.h - 1;
}
else {
a.x1 = 0;
a.y1 = 0;
a.x2 = canvas->dsc.header.w - 1;
a.y2 = canvas->dsc.header.h - 1;
}
lv_color_t color = lv_obj_get_style_img_recolor(obj, LV_PART_MAIN);
uint16_t r_back = r / 2;
uint16_t r_front = r / 2;
if((r & 0x1) == 0) r_back--;
bool has_alpha = lv_img_cf_has_alpha(canvas->dsc.header.cf);
lv_coord_t line_w = lv_img_buf_get_img_size(canvas->dsc.header.w, 1, canvas->dsc.header.cf);
uint8_t * line_buf = lv_mem_buf_get(line_w);
lv_img_dsc_t line_img;
line_img.data = line_buf;
line_img.header.always_zero = 0;
line_img.header.w = canvas->dsc.header.w;
line_img.header.h = 1;
line_img.header.cf = canvas->dsc.header.cf;
lv_coord_t x;
lv_coord_t y;
lv_coord_t x_safe;
for(y = a.y1; y <= a.y2; y++) {
uint32_t asum = 0;
uint32_t rsum = 0;
uint32_t gsum = 0;
uint32_t bsum = 0;
lv_color_t c;
lv_opa_t opa = LV_OPA_TRANSP;
lv_memcpy(line_buf, &canvas->dsc.data[y * line_w], line_w);
for(x = a.x1 - r_back; x <= a.x1 + r_front; x++) {
x_safe = x < 0 ? 0 : x;
x_safe = x_safe > canvas->dsc.header.w - 1 ? canvas->dsc.header.w - 1 : x_safe;
c = lv_img_buf_get_px_color(&line_img, x_safe, 0, color);
if(has_alpha) opa = lv_img_buf_get_px_alpha(&line_img, x_safe, 0);
rsum += c.ch.red;
#if LV_COLOR_DEPTH == 16 && LV_COLOR_16_SWAP
gsum += (c.ch.green_h << 3) + c.ch.green_l;
#else
gsum += c.ch.green;
#endif
bsum += c.ch.blue;
if(has_alpha) asum += opa;
}
/*Just to indicate that the px is visible*/
if(has_alpha == false) asum = LV_OPA_COVER;
for(x = a.x1; x <= a.x2; x++) {
if(asum) {
c.ch.red = rsum / r;
#if LV_COLOR_DEPTH == 16 && LV_COLOR_16_SWAP
uint8_t gtmp = gsum / r;
c.ch.green_h = gtmp >> 3;
c.ch.green_l = gtmp & 0x7;
#else
c.ch.green = gsum / r;
#endif
c.ch.blue = bsum / r;
if(has_alpha) opa = asum / r;
lv_img_buf_set_px_color(&canvas->dsc, x, y, c);
}
if(has_alpha) lv_img_buf_set_px_alpha(&canvas->dsc, x, y, opa);
x_safe = x - r_back;
x_safe = x_safe < 0 ? 0 : x_safe;
c = lv_img_buf_get_px_color(&line_img, x_safe, 0, color);
if(has_alpha) opa = lv_img_buf_get_px_alpha(&line_img, x_safe, 0);
rsum -= c.ch.red;
#if LV_COLOR_DEPTH == 16 && LV_COLOR_16_SWAP
gsum -= (c.ch.green_h << 3) + c.ch.green_l;
#else
gsum -= c.ch.green;
#endif
bsum -= c.ch.blue;
if(has_alpha) asum -= opa;
x_safe = x + 1 + r_front;
x_safe = x_safe > canvas->dsc.header.w - 1 ? canvas->dsc.header.w - 1 : x_safe;
c = lv_img_buf_get_px_color(&line_img, x_safe, 0, lv_color_white());
if(has_alpha) opa = lv_img_buf_get_px_alpha(&line_img, x_safe, 0);
rsum += c.ch.red;
#if LV_COLOR_DEPTH == 16 && LV_COLOR_16_SWAP
gsum += (c.ch.green_h << 3) + c.ch.green_l;
#else
gsum += c.ch.green;
#endif
bsum += c.ch.blue;
if(has_alpha) asum += opa;
}
}
lv_obj_invalidate(obj);
lv_mem_buf_release(line_buf);
}
void lv_canvas_blur_ver(lv_obj_t * obj, const lv_area_t * area, uint16_t r)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
if(r == 0) return;
lv_canvas_t * canvas = (lv_canvas_t *)obj;
lv_area_t a;
if(area) {
lv_area_copy(&a, area);
if(a.x1 < 0) a.x1 = 0;
if(a.y1 < 0) a.y1 = 0;
if(a.x2 > canvas->dsc.header.w - 1) a.x2 = canvas->dsc.header.w - 1;
if(a.y2 > canvas->dsc.header.h - 1) a.y2 = canvas->dsc.header.h - 1;
}
else {
a.x1 = 0;
a.y1 = 0;
a.x2 = canvas->dsc.header.w - 1;
a.y2 = canvas->dsc.header.h - 1;
}
lv_color_t color = lv_obj_get_style_img_recolor(obj, LV_PART_MAIN);
uint16_t r_back = r / 2;
uint16_t r_front = r / 2;
if((r & 0x1) == 0) r_back--;
bool has_alpha = lv_img_cf_has_alpha(canvas->dsc.header.cf);
lv_coord_t col_w = lv_img_buf_get_img_size(1, canvas->dsc.header.h, canvas->dsc.header.cf);
uint8_t * col_buf = lv_mem_buf_get(col_w);
lv_img_dsc_t line_img;
line_img.data = col_buf;
line_img.header.always_zero = 0;
line_img.header.w = 1;
line_img.header.h = canvas->dsc.header.h;
line_img.header.cf = canvas->dsc.header.cf;
lv_coord_t x;
lv_coord_t y;
lv_coord_t y_safe;
for(x = a.x1; x <= a.x2; x++) {
uint32_t asum = 0;
uint32_t rsum = 0;
uint32_t gsum = 0;
uint32_t bsum = 0;
lv_color_t c;
lv_opa_t opa = LV_OPA_COVER;
for(y = a.y1 - r_back; y <= a.y1 + r_front; y++) {
y_safe = y < 0 ? 0 : y;
y_safe = y_safe > canvas->dsc.header.h - 1 ? canvas->dsc.header.h - 1 : y_safe;
c = lv_img_buf_get_px_color(&canvas->dsc, x, y_safe, color);
if(has_alpha) opa = lv_img_buf_get_px_alpha(&canvas->dsc, x, y_safe);
lv_img_buf_set_px_color(&line_img, 0, y_safe, c);
if(has_alpha) lv_img_buf_set_px_alpha(&line_img, 0, y_safe, opa);
rsum += c.ch.red;
#if LV_COLOR_DEPTH == 16 && LV_COLOR_16_SWAP
gsum += (c.ch.green_h << 3) + c.ch.green_l;
#else
gsum += c.ch.green;
#endif
bsum += c.ch.blue;
if(has_alpha) asum += opa;
}
/*Just to indicate that the px is visible*/
if(has_alpha == false) asum = LV_OPA_COVER;
for(y = a.y1; y <= a.y2; y++) {
if(asum) {
c.ch.red = rsum / r;
#if LV_COLOR_DEPTH == 16 && LV_COLOR_16_SWAP
uint8_t gtmp = gsum / r;
c.ch.green_h = gtmp >> 3;
c.ch.green_l = gtmp & 0x7;
#else
c.ch.green = gsum / r;
#endif
c.ch.blue = bsum / r;
if(has_alpha) opa = asum / r;
lv_img_buf_set_px_color(&canvas->dsc, x, y, c);
}
if(has_alpha) lv_img_buf_set_px_alpha(&canvas->dsc, x, y, opa);
y_safe = y - r_back;
y_safe = y_safe < 0 ? 0 : y_safe;
c = lv_img_buf_get_px_color(&line_img, 0, y_safe, color);
if(has_alpha) opa = lv_img_buf_get_px_alpha(&line_img, 0, y_safe);
rsum -= c.ch.red;
#if LV_COLOR_DEPTH == 16 && LV_COLOR_16_SWAP
gsum -= (c.ch.green_h << 3) + c.ch.green_l;
#else
gsum -= c.ch.green;
#endif
bsum -= c.ch.blue;
if(has_alpha) asum -= opa;
y_safe = y + 1 + r_front;
y_safe = y_safe > canvas->dsc.header.h - 1 ? canvas->dsc.header.h - 1 : y_safe;
c = lv_img_buf_get_px_color(&canvas->dsc, x, y_safe, color);
if(has_alpha) opa = lv_img_buf_get_px_alpha(&canvas->dsc, x, y_safe);
lv_img_buf_set_px_color(&line_img, 0, y_safe, c);
if(has_alpha) lv_img_buf_set_px_alpha(&line_img, 0, y_safe, opa);
rsum += c.ch.red;
#if LV_COLOR_DEPTH == 16 && LV_COLOR_16_SWAP
gsum += (c.ch.green_h << 3) + c.ch.green_l;
#else
gsum += c.ch.green;
#endif
bsum += c.ch.blue;
if(has_alpha) asum += opa;
}
}
lv_obj_invalidate(obj);
lv_mem_buf_release(col_buf);
}
void lv_canvas_fill_bg(lv_obj_t * canvas, lv_color_t color, lv_opa_t opa)
{
LV_ASSERT_OBJ(canvas, MY_CLASS);
lv_img_dsc_t * dsc = lv_canvas_get_img(canvas);
if(dsc->header.cf == LV_IMG_CF_INDEXED_1BIT) {
uint32_t row_byte_cnt = (dsc->header.w + 7) >> 3;
/*+8 skip the palette*/
lv_memset((uint8_t *)dsc->data + 8, color.full ? 0xff : 0x00, row_byte_cnt * dsc->header.h);
}
else if(dsc->header.cf == LV_IMG_CF_ALPHA_1BIT) {
uint32_t row_byte_cnt = (dsc->header.w + 7) >> 3;
lv_memset((uint8_t *)dsc->data, opa > LV_OPA_50 ? 0xff : 0x00, row_byte_cnt * dsc->header.h);
}
else {
uint32_t x;
uint32_t y;
for(y = 0; y < dsc->header.h; y++) {
for(x = 0; x < dsc->header.w; x++) {
lv_img_buf_set_px_color(dsc, x, y, color);
lv_img_buf_set_px_alpha(dsc, x, y, opa);
}
}
}
lv_obj_invalidate(canvas);
}
void lv_canvas_draw_rect(lv_obj_t * canvas, lv_coord_t x, lv_coord_t y, lv_coord_t w, lv_coord_t h,
const lv_draw_rect_dsc_t * draw_dsc)
{
LV_ASSERT_OBJ(canvas, MY_CLASS);
lv_img_dsc_t * dsc = lv_canvas_get_img(canvas);
if(dsc->header.cf >= LV_IMG_CF_INDEXED_1BIT && dsc->header.cf <= LV_IMG_CF_INDEXED_8BIT) {
LV_LOG_WARN("lv_canvas_draw_rect: can't draw to LV_IMG_CF_INDEXED canvas");
return;
}
/*Create a dummy display to fool the lv_draw function.
*It will think it draws to real screen.*/
lv_disp_t fake_disp;
lv_disp_drv_t driver;
lv_area_t clip_area;
init_fake_disp(canvas, &fake_disp, &driver, &clip_area);
lv_disp_t * refr_ori = _lv_refr_get_disp_refreshing();
_lv_refr_set_disp_refreshing(&fake_disp);
/*Disable anti-aliasing if drawing with transparent color to chroma keyed canvas*/
lv_color_t ctransp = LV_COLOR_CHROMA_KEY;
if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED &&
draw_dsc->bg_color.full == ctransp.full) {
fake_disp.driver->antialiasing = 0;
}
lv_area_t coords;
coords.x1 = x;
coords.y1 = y;
coords.x2 = x + w - 1;
coords.y2 = y + h - 1;
lv_draw_rect(driver.draw_ctx, draw_dsc, &coords);
_lv_refr_set_disp_refreshing(refr_ori);
deinit_fake_disp(canvas, &fake_disp);
lv_obj_invalidate(canvas);
}
void lv_canvas_draw_text(lv_obj_t * canvas, lv_coord_t x, lv_coord_t y, lv_coord_t max_w,
lv_draw_label_dsc_t * draw_dsc, const char * txt)
{
LV_ASSERT_OBJ(canvas, MY_CLASS);
lv_img_dsc_t * dsc = lv_canvas_get_img(canvas);
if(dsc->header.cf >= LV_IMG_CF_INDEXED_1BIT && dsc->header.cf <= LV_IMG_CF_INDEXED_8BIT) {
LV_LOG_WARN("lv_canvas_draw_text: can't draw to LV_IMG_CF_INDEXED canvas");
return;
}
/*Create a dummy display to fool the lv_draw function.
*It will think it draws to real screen.*/
lv_disp_t fake_disp;
lv_disp_drv_t driver;
lv_area_t clip_area;
init_fake_disp(canvas, &fake_disp, &driver, &clip_area);
lv_disp_t * refr_ori = _lv_refr_get_disp_refreshing();
_lv_refr_set_disp_refreshing(&fake_disp);
lv_area_t coords;
coords.x1 = x;
coords.y1 = y;
coords.x2 = x + max_w - 1;
coords.y2 = dsc->header.h - 1;
lv_draw_label(driver.draw_ctx, draw_dsc, &coords, txt, NULL);
_lv_refr_set_disp_refreshing(refr_ori);
deinit_fake_disp(canvas, &fake_disp);
lv_obj_invalidate(canvas);
}
void lv_canvas_draw_img(lv_obj_t * canvas, lv_coord_t x, lv_coord_t y, const void * src,
const lv_draw_img_dsc_t * draw_dsc)
{
LV_ASSERT_OBJ(canvas, MY_CLASS);
lv_img_dsc_t * dsc = lv_canvas_get_img(canvas);
if(dsc->header.cf >= LV_IMG_CF_INDEXED_1BIT && dsc->header.cf <= LV_IMG_CF_INDEXED_8BIT) {
LV_LOG_WARN("lv_canvas_draw_img: can't draw to LV_IMG_CF_INDEXED canvas");
return;
}
lv_img_header_t header;
lv_res_t res = lv_img_decoder_get_info(src, &header);
if(res != LV_RES_OK) {
LV_LOG_WARN("lv_canvas_draw_img: Couldn't get the image data.");
return;
}
/*Create a dummy display to fool the lv_draw function.
*It will think it draws to real screen.*/
lv_disp_t fake_disp;
lv_disp_drv_t driver;
lv_area_t clip_area;
init_fake_disp(canvas, &fake_disp, &driver, &clip_area);
lv_disp_t * refr_ori = _lv_refr_get_disp_refreshing();
_lv_refr_set_disp_refreshing(&fake_disp);
lv_area_t coords;
coords.x1 = x;
coords.y1 = y;
coords.x2 = x + header.w - 1;
coords.y2 = y + header.h - 1;
lv_draw_img(driver.draw_ctx, draw_dsc, &coords, src);
_lv_refr_set_disp_refreshing(refr_ori);
deinit_fake_disp(canvas, &fake_disp);
lv_obj_invalidate(canvas);
}
void lv_canvas_draw_line(lv_obj_t * canvas, const lv_point_t points[], uint32_t point_cnt,
const lv_draw_line_dsc_t * draw_dsc)
{
LV_ASSERT_OBJ(canvas, MY_CLASS);
lv_img_dsc_t * dsc = lv_canvas_get_img(canvas);
if(dsc->header.cf >= LV_IMG_CF_INDEXED_1BIT && dsc->header.cf <= LV_IMG_CF_INDEXED_8BIT) {
LV_LOG_WARN("lv_canvas_draw_line: can't draw to LV_IMG_CF_INDEXED canvas");
return;
}
/*Create a dummy display to fool the lv_draw function.
*It will think it draws to real screen.*/
lv_disp_t fake_disp;
lv_disp_drv_t driver;
lv_area_t clip_area;
init_fake_disp(canvas, &fake_disp, &driver, &clip_area);
lv_disp_t * refr_ori = _lv_refr_get_disp_refreshing();
_lv_refr_set_disp_refreshing(&fake_disp);
/*Disable anti-aliasing if drawing with transparent color to chroma keyed canvas*/
lv_color_t ctransp = LV_COLOR_CHROMA_KEY;
if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED &&
draw_dsc->color.full == ctransp.full) {
fake_disp.driver->antialiasing = 0;
}
uint32_t i;
for(i = 0; i < point_cnt - 1; i++) {
lv_draw_line(driver.draw_ctx, draw_dsc, &points[i], &points[i + 1]);
}
_lv_refr_set_disp_refreshing(refr_ori);
deinit_fake_disp(canvas, &fake_disp);
lv_obj_invalidate(canvas);
}
void lv_canvas_draw_polygon(lv_obj_t * canvas, const lv_point_t points[], uint32_t point_cnt,
const lv_draw_rect_dsc_t * draw_dsc)
{
LV_ASSERT_OBJ(canvas, MY_CLASS);
lv_img_dsc_t * dsc = lv_canvas_get_img(canvas);
if(dsc->header.cf >= LV_IMG_CF_INDEXED_1BIT && dsc->header.cf <= LV_IMG_CF_INDEXED_8BIT) {
LV_LOG_WARN("lv_canvas_draw_polygon: can't draw to LV_IMG_CF_INDEXED canvas");
return;
}
/*Create a dummy display to fool the lv_draw function.
*It will think it draws to real screen.*/
lv_disp_t fake_disp;
lv_disp_drv_t driver;
lv_area_t clip_area;
init_fake_disp(canvas, &fake_disp, &driver, &clip_area);
lv_disp_t * refr_ori = _lv_refr_get_disp_refreshing();
_lv_refr_set_disp_refreshing(&fake_disp);
/*Disable anti-aliasing if drawing with transparent color to chroma keyed canvas*/
lv_color_t ctransp = LV_COLOR_CHROMA_KEY;
if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED &&
draw_dsc->bg_color.full == ctransp.full) {
fake_disp.driver->antialiasing = 0;
}
lv_draw_polygon(driver.draw_ctx, draw_dsc, points, point_cnt);
_lv_refr_set_disp_refreshing(refr_ori);
deinit_fake_disp(canvas, &fake_disp);
lv_obj_invalidate(canvas);
}
void lv_canvas_draw_arc(lv_obj_t * canvas, lv_coord_t x, lv_coord_t y, lv_coord_t r, int32_t start_angle,
int32_t end_angle, const lv_draw_arc_dsc_t * draw_dsc)
{
#if LV_DRAW_COMPLEX
LV_ASSERT_OBJ(canvas, MY_CLASS);
lv_img_dsc_t * dsc = lv_canvas_get_img(canvas);
if(dsc->header.cf >= LV_IMG_CF_INDEXED_1BIT && dsc->header.cf <= LV_IMG_CF_INDEXED_8BIT) {
LV_LOG_WARN("lv_canvas_draw_arc: can't draw to LV_IMG_CF_INDEXED canvas");
return;
}
/*Create a dummy display to fool the lv_draw function.
*It will think it draws to real screen.*/
lv_disp_t fake_disp;
lv_disp_drv_t driver;
lv_area_t clip_area;
init_fake_disp(canvas, &fake_disp, &driver, &clip_area);
lv_disp_t * refr_ori = _lv_refr_get_disp_refreshing();
_lv_refr_set_disp_refreshing(&fake_disp);
lv_point_t p = {x, y};
lv_draw_arc(driver.draw_ctx, draw_dsc, &p, r, start_angle, end_angle);
_lv_refr_set_disp_refreshing(refr_ori);
deinit_fake_disp(canvas, &fake_disp);
lv_obj_invalidate(canvas);
#else
LV_UNUSED(canvas);
LV_UNUSED(x);
LV_UNUSED(y);
LV_UNUSED(r);
LV_UNUSED(start_angle);
LV_UNUSED(end_angle);
LV_UNUSED(draw_dsc);
LV_LOG_WARN("Can't draw arc with LV_DRAW_COMPLEX == 0");
#endif
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_canvas_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
LV_TRACE_OBJ_CREATE("begin");
lv_canvas_t * canvas = (lv_canvas_t *)obj;
canvas->dsc.header.always_zero = 0;
canvas->dsc.header.cf = LV_IMG_CF_TRUE_COLOR;
canvas->dsc.header.h = 0;
canvas->dsc.header.w = 0;
canvas->dsc.data_size = 0;
canvas->dsc.data = NULL;
lv_img_set_src(obj, &canvas->dsc);
LV_TRACE_OBJ_CREATE("finished");
}
static void lv_canvas_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
LV_TRACE_OBJ_CREATE("begin");
lv_canvas_t * canvas = (lv_canvas_t *)obj;
lv_img_cache_invalidate_src(&canvas->dsc);
}
static void init_fake_disp(lv_obj_t * canvas, lv_disp_t * disp, lv_disp_drv_t * drv, lv_area_t * clip_area)
{
lv_img_dsc_t * dsc = lv_canvas_get_img(canvas);
clip_area->x1 = 0;
clip_area->x2 = dsc->header.w - 1;
clip_area->y1 = 0;
clip_area->y2 = dsc->header.h - 1;
/*Allocate the fake driver on the stack as the entire display doesn't outlive this function*/
lv_memset_00(disp, sizeof(lv_disp_t));
disp->driver = drv;
lv_disp_drv_init(disp->driver);
disp->driver->hor_res = dsc->header.w;
disp->driver->ver_res = dsc->header.h;
lv_draw_ctx_t * draw_ctx = lv_mem_alloc(sizeof(lv_draw_sw_ctx_t));
LV_ASSERT_MALLOC(draw_ctx);
if(draw_ctx == NULL) return;
lv_draw_sw_init_ctx(drv, draw_ctx);
disp->driver->draw_ctx = draw_ctx;
draw_ctx->clip_area = clip_area;
draw_ctx->buf_area = clip_area;
draw_ctx->buf = (void *)dsc->data;
lv_disp_drv_use_generic_set_px_cb(disp->driver, dsc->header.cf);
if(LV_COLOR_SCREEN_TRANSP && dsc->header.cf != LV_IMG_CF_TRUE_COLOR_ALPHA) {
drv->screen_transp = 0;
}
}
static void deinit_fake_disp(lv_obj_t * canvas, lv_disp_t * disp)
{
LV_UNUSED(canvas);
lv_draw_sw_deinit_ctx(disp->driver, disp->driver->draw_ctx);
lv_mem_free(disp->driver->draw_ctx);
}
#endif

View File

@@ -0,0 +1,280 @@
/**
* @file lv_canvas.h
*
*/
#ifndef LV_CANVAS_H
#define LV_CANVAS_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../lv_conf_internal.h"
#if LV_USE_CANVAS != 0
#include "../core/lv_obj.h"
#include "../widgets/lv_img.h"
#include "../draw/lv_draw_img.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
extern const lv_obj_class_t lv_canvas_class;
/*Data of canvas*/
typedef struct {
lv_img_t img;
lv_img_dsc_t dsc;
} lv_canvas_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a canvas object
* @param parent pointer to an object, it will be the parent of the new canvas
* @return pointer to the created canvas
*/
lv_obj_t * lv_canvas_create(lv_obj_t * parent);
/*=====================
* Setter functions
*====================*/
/**
* Set a buffer for the canvas.
* @param buf a buffer where the content of the canvas will be.
* The required size is (lv_img_color_format_get_px_size(cf) * w) / 8 * h)
* It can be allocated with `lv_mem_alloc()` or
* it can be statically allocated array (e.g. static lv_color_t buf[100*50]) or
* it can be an address in RAM or external SRAM
* @param canvas pointer to a canvas object
* @param w width of the canvas
* @param h height of the canvas
* @param cf color format. `LV_IMG_CF_...`
*/
void lv_canvas_set_buffer(lv_obj_t * canvas, void * buf, lv_coord_t w, lv_coord_t h, lv_img_cf_t cf);
/**
* Set the color of a pixel on the canvas
* @param canvas
* @param x x coordinate of the point to set
* @param y x coordinate of the point to set
* @param c color of the pixel
*/
void lv_canvas_set_px_color(lv_obj_t * canvas, lv_coord_t x, lv_coord_t y, lv_color_t c);
/**
* DEPRECATED: added only for backward compatibility
*/
static inline void lv_canvas_set_px(lv_obj_t * canvas, lv_coord_t x, lv_coord_t y, lv_color_t c)
{
lv_canvas_set_px_color(canvas, x, y, c);
}
/**
* Set the opacity of a pixel on the canvas
* @param canvas
* @param x x coordinate of the point to set
* @param y x coordinate of the point to set
* @param opa opacity of the pixel (0..255)
*/
void lv_canvas_set_px_opa(lv_obj_t * canvas, lv_coord_t x, lv_coord_t y, lv_opa_t opa);
/**
* Set the palette color of a canvas with index format. Valid only for `LV_IMG_CF_INDEXED1/2/4/8`
* @param canvas pointer to canvas object
* @param id the palette color to set:
* - for `LV_IMG_CF_INDEXED1`: 0..1
* - for `LV_IMG_CF_INDEXED2`: 0..3
* - for `LV_IMG_CF_INDEXED4`: 0..15
* - for `LV_IMG_CF_INDEXED8`: 0..255
* @param c the color to set
*/
void lv_canvas_set_palette(lv_obj_t * canvas, uint8_t id, lv_color_t c);
/*=====================
* Getter functions
*====================*/
/**
* Get the color of a pixel on the canvas
* @param canvas
* @param x x coordinate of the point to set
* @param y x coordinate of the point to set
* @return color of the point
*/
lv_color_t lv_canvas_get_px(lv_obj_t * canvas, lv_coord_t x, lv_coord_t y);
/**
* Get the image of the canvas as a pointer to an `lv_img_dsc_t` variable.
* @param canvas pointer to a canvas object
* @return pointer to the image descriptor.
*/
lv_img_dsc_t * lv_canvas_get_img(lv_obj_t * canvas);
/*=====================
* Other functions
*====================*/
/**
* Copy a buffer to the canvas
* @param canvas pointer to a canvas object
* @param to_copy buffer to copy. The color format has to match with the canvas's buffer color
* format
* @param x left side of the destination position
* @param y top side of the destination position
* @param w width of the buffer to copy
* @param h height of the buffer to copy
*/
void lv_canvas_copy_buf(lv_obj_t * canvas, const void * to_copy, lv_coord_t x, lv_coord_t y, lv_coord_t w,
lv_coord_t h);
/**
* Transform and image and store the result on a canvas.
* @param canvas pointer to a canvas object to store the result of the transformation.
* @param img pointer to an image descriptor to transform.
* Can be the image descriptor of an other canvas too (`lv_canvas_get_img()`).
* @param angle the angle of rotation (0..3600), 0.1 deg resolution
* @param zoom zoom factor (256 no zoom);
* @param offset_x offset X to tell where to put the result data on destination canvas
* @param offset_y offset X to tell where to put the result data on destination canvas
* @param pivot_x pivot X of rotation. Relative to the source canvas
* Set to `source width / 2` to rotate around the center
* @param pivot_y pivot Y of rotation. Relative to the source canvas
* Set to `source height / 2` to rotate around the center
* @param antialias apply anti-aliasing during the transformation. Looks better but slower.
*/
void lv_canvas_transform(lv_obj_t * canvas, lv_img_dsc_t * img, int16_t angle, uint16_t zoom, lv_coord_t offset_x,
lv_coord_t offset_y,
int32_t pivot_x, int32_t pivot_y, bool antialias);
/**
* Apply horizontal blur on the canvas
* @param canvas pointer to a canvas object
* @param area the area to blur. If `NULL` the whole canvas will be blurred.
* @param r radius of the blur
*/
void lv_canvas_blur_hor(lv_obj_t * canvas, const lv_area_t * area, uint16_t r);
/**
* Apply vertical blur on the canvas
* @param canvas pointer to a canvas object
* @param area the area to blur. If `NULL` the whole canvas will be blurred.
* @param r radius of the blur
*/
void lv_canvas_blur_ver(lv_obj_t * canvas, const lv_area_t * area, uint16_t r);
/**
* Fill the canvas with color
* @param canvas pointer to a canvas
* @param color the background color
* @param opa the desired opacity
*/
void lv_canvas_fill_bg(lv_obj_t * canvas, lv_color_t color, lv_opa_t opa);
/**
* Draw a rectangle on the canvas
* @param canvas pointer to a canvas object
* @param x left coordinate of the rectangle
* @param y top coordinate of the rectangle
* @param w width of the rectangle
* @param h height of the rectangle
* @param draw_dsc descriptor of the rectangle
*/
void lv_canvas_draw_rect(lv_obj_t * canvas, lv_coord_t x, lv_coord_t y, lv_coord_t w, lv_coord_t h,
const lv_draw_rect_dsc_t * draw_dsc);
/**
* Draw a text on the canvas.
* @param canvas pointer to a canvas object
* @param x left coordinate of the text
* @param y top coordinate of the text
* @param max_w max width of the text. The text will be wrapped to fit into this size
* @param draw_dsc pointer to a valid label descriptor `lv_draw_label_dsc_t`
* @param txt text to display
*/
void lv_canvas_draw_text(lv_obj_t * canvas, lv_coord_t x, lv_coord_t y, lv_coord_t max_w,
lv_draw_label_dsc_t * draw_dsc, const char * txt);
/**
* Draw an image on the canvas
* @param canvas pointer to a canvas object
* @param x left coordinate of the image
* @param y top coordinate of the image
* @param src image source. Can be a pointer an `lv_img_dsc_t` variable or a path an image.
* @param draw_dsc pointer to a valid label descriptor `lv_draw_img_dsc_t`
*/
void lv_canvas_draw_img(lv_obj_t * canvas, lv_coord_t x, lv_coord_t y, const void * src,
const lv_draw_img_dsc_t * draw_dsc);
/**
* Draw a line on the canvas
* @param canvas pointer to a canvas object
* @param points point of the line
* @param point_cnt number of points
* @param draw_dsc pointer to an initialized `lv_draw_line_dsc_t` variable
*/
void lv_canvas_draw_line(lv_obj_t * canvas, const lv_point_t points[], uint32_t point_cnt,
const lv_draw_line_dsc_t * draw_dsc);
/**
* Draw a polygon on the canvas
* @param canvas pointer to a canvas object
* @param points point of the polygon
* @param point_cnt number of points
* @param draw_dsc pointer to an initialized `lv_draw_rect_dsc_t` variable
*/
void lv_canvas_draw_polygon(lv_obj_t * canvas, const lv_point_t points[], uint32_t point_cnt,
const lv_draw_rect_dsc_t * draw_dsc);
/**
* Draw an arc on the canvas
* @param canvas pointer to a canvas object
* @param x origo x of the arc
* @param y origo y of the arc
* @param r radius of the arc
* @param start_angle start angle in degrees
* @param end_angle end angle in degrees
* @param draw_dsc pointer to an initialized `lv_draw_line_dsc_t` variable
*/
void lv_canvas_draw_arc(lv_obj_t * canvas, lv_coord_t x, lv_coord_t y, lv_coord_t r, int32_t start_angle,
int32_t end_angle, const lv_draw_arc_dsc_t * draw_dsc);
/**********************
* MACROS
**********************/
#define LV_CANVAS_BUF_SIZE_TRUE_COLOR(w, h) LV_IMG_BUF_SIZE_TRUE_COLOR(w, h)
#define LV_CANVAS_BUF_SIZE_TRUE_COLOR_CHROMA_KEYED(w, h) LV_IMG_BUF_SIZE_TRUE_COLOR_CHROMA_KEYED(w, h)
#define LV_CANVAS_BUF_SIZE_TRUE_COLOR_ALPHA(w, h) LV_IMG_BUF_SIZE_TRUE_COLOR_ALPHA(w, h)
/*+ 1: to be sure no fractional row*/
#define LV_CANVAS_BUF_SIZE_ALPHA_1BIT(w, h) LV_IMG_BUF_SIZE_ALPHA_1BIT(w, h)
#define LV_CANVAS_BUF_SIZE_ALPHA_2BIT(w, h) LV_IMG_BUF_SIZE_ALPHA_2BIT(w, h)
#define LV_CANVAS_BUF_SIZE_ALPHA_4BIT(w, h) LV_IMG_BUF_SIZE_ALPHA_4BIT(w, h)
#define LV_CANVAS_BUF_SIZE_ALPHA_8BIT(w, h) LV_IMG_BUF_SIZE_ALPHA_8BIT(w, h)
/*4 * X: for palette*/
#define LV_CANVAS_BUF_SIZE_INDEXED_1BIT(w, h) LV_IMG_BUF_SIZE_INDEXED_1BIT(w, h)
#define LV_CANVAS_BUF_SIZE_INDEXED_2BIT(w, h) LV_IMG_BUF_SIZE_INDEXED_2BIT(w, h)
#define LV_CANVAS_BUF_SIZE_INDEXED_4BIT(w, h) LV_IMG_BUF_SIZE_INDEXED_4BIT(w, h)
#define LV_CANVAS_BUF_SIZE_INDEXED_8BIT(w, h) LV_IMG_BUF_SIZE_INDEXED_8BIT(w, h)
#endif /*LV_USE_CANVAS*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_CANVAS_H*/

View File

@@ -0,0 +1,262 @@
/**
* @file lv_cb.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_checkbox.h"
#if LV_USE_CHECKBOX != 0
#include "../misc/lv_assert.h"
#include "../misc/lv_txt_ap.h"
#include "../core/lv_group.h"
#include "../draw/lv_draw.h"
/*********************
* DEFINES
*********************/
#define MY_CLASS &lv_checkbox_class
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_checkbox_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_checkbox_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_checkbox_event(const lv_obj_class_t * class_p, lv_event_t * e);
static void lv_checkbox_draw(lv_event_t * e);
/**********************
* STATIC VARIABLES
**********************/
const lv_obj_class_t lv_checkbox_class = {
.constructor_cb = lv_checkbox_constructor,
.destructor_cb = lv_checkbox_destructor,
.event_cb = lv_checkbox_event,
.width_def = LV_SIZE_CONTENT,
.height_def = LV_SIZE_CONTENT,
.group_def = LV_OBJ_CLASS_GROUP_DEF_TRUE,
.instance_size = sizeof(lv_checkbox_t),
.base_class = &lv_obj_class
};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_obj_t * lv_checkbox_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_checkbox_set_text(lv_obj_t * obj, const char * txt)
{
lv_checkbox_t * cb = (lv_checkbox_t *)obj;
#if LV_USE_ARABIC_PERSIAN_CHARS
size_t len = _lv_txt_ap_calc_bytes_cnt(txt);
#else
size_t len = strlen(txt);
#endif
if(!cb->static_txt) cb->txt = lv_mem_realloc(cb->txt, len + 1);
else cb->txt = lv_mem_alloc(len + 1);
#if LV_USE_ARABIC_PERSIAN_CHARS
_lv_txt_ap_proc(txt, cb->txt);
#else
strcpy(cb->txt, txt);
#endif
cb->static_txt = 0;
lv_obj_refresh_self_size(obj);
lv_obj_invalidate(obj);
}
void lv_checkbox_set_text_static(lv_obj_t * obj, const char * txt)
{
lv_checkbox_t * cb = (lv_checkbox_t *)obj;
if(!cb->static_txt) lv_mem_free(cb->txt);
cb->txt = (char *)txt;
cb->static_txt = 1;
lv_obj_refresh_self_size(obj);
lv_obj_invalidate(obj);
}
/*=====================
* Getter functions
*====================*/
const char * lv_checkbox_get_text(const lv_obj_t * obj)
{
lv_checkbox_t * cb = (lv_checkbox_t *)obj;
return cb->txt;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_checkbox_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
LV_TRACE_OBJ_CREATE("begin");
lv_checkbox_t * cb = (lv_checkbox_t *)obj;
cb->txt = "Check box";
cb->static_txt = 1;
lv_obj_add_flag(obj, LV_OBJ_FLAG_CLICKABLE);
lv_obj_add_flag(obj, LV_OBJ_FLAG_CHECKABLE);
lv_obj_add_flag(obj, LV_OBJ_FLAG_SCROLL_ON_FOCUS);
LV_TRACE_OBJ_CREATE("finished");
}
static void lv_checkbox_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
LV_TRACE_OBJ_CREATE("begin");
lv_checkbox_t * cb = (lv_checkbox_t *)obj;
if(!cb->static_txt) {
lv_mem_free(cb->txt);
cb->txt = NULL;
}
LV_TRACE_OBJ_CREATE("finished");
}
static void lv_checkbox_event(const lv_obj_class_t * class_p, lv_event_t * e)
{
LV_UNUSED(class_p);
lv_res_t res;
/*Call the ancestor's event handler*/
res = lv_obj_event_base(MY_CLASS, e);
if(res != LV_RES_OK) return;
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * obj = lv_event_get_target(e);
if(code == LV_EVENT_GET_SELF_SIZE) {
lv_point_t * p = lv_event_get_param(e);
lv_checkbox_t * cb = (lv_checkbox_t *)obj;
const lv_font_t * font = lv_obj_get_style_text_font(obj, LV_PART_MAIN);
lv_coord_t font_h = lv_font_get_line_height(font);
lv_coord_t line_space = lv_obj_get_style_text_line_space(obj, LV_PART_MAIN);
lv_coord_t letter_space = lv_obj_get_style_text_letter_space(obj, LV_PART_MAIN);
lv_point_t txt_size;
lv_txt_get_size(&txt_size, cb->txt, font, letter_space, line_space, LV_COORD_MAX, LV_TEXT_FLAG_NONE);
lv_coord_t bg_colp = lv_obj_get_style_pad_column(obj, LV_PART_MAIN);
lv_coord_t marker_leftp = lv_obj_get_style_pad_left(obj, LV_PART_INDICATOR);
lv_coord_t marker_rightp = lv_obj_get_style_pad_right(obj, LV_PART_INDICATOR);
lv_coord_t marker_topp = lv_obj_get_style_pad_top(obj, LV_PART_INDICATOR);
lv_coord_t marker_bottomp = lv_obj_get_style_pad_bottom(obj, LV_PART_INDICATOR);
lv_point_t marker_size;
marker_size.x = font_h + marker_leftp + marker_rightp;
marker_size.y = font_h + marker_topp + marker_bottomp;
p->x = marker_size.x + txt_size.x + bg_colp;
p->y = LV_MAX(marker_size.y, txt_size.y);
}
else if(code == LV_EVENT_REFR_EXT_DRAW_SIZE) {
lv_coord_t * s = lv_event_get_param(e);
lv_coord_t m = lv_obj_calculate_ext_draw_size(obj, LV_PART_INDICATOR);
*s = LV_MAX(*s, m);
}
else if(code == LV_EVENT_DRAW_MAIN) {
lv_checkbox_draw(e);
}
}
static void lv_checkbox_draw(lv_event_t * e)
{
lv_obj_t * obj = lv_event_get_target(e);
lv_checkbox_t * cb = (lv_checkbox_t *)obj;
lv_draw_ctx_t * draw_ctx = lv_event_get_draw_ctx(e);
const lv_font_t * font = lv_obj_get_style_text_font(obj, LV_PART_MAIN);
lv_coord_t font_h = lv_font_get_line_height(font);
lv_coord_t bg_border = lv_obj_get_style_border_width(obj, LV_PART_MAIN);
lv_coord_t bg_topp = lv_obj_get_style_pad_top(obj, LV_PART_MAIN) + bg_border;
lv_coord_t bg_leftp = lv_obj_get_style_pad_left(obj, LV_PART_MAIN) + bg_border;
lv_coord_t bg_colp = lv_obj_get_style_pad_column(obj, LV_PART_MAIN);
lv_coord_t marker_leftp = lv_obj_get_style_pad_left(obj, LV_PART_INDICATOR);
lv_coord_t marker_rightp = lv_obj_get_style_pad_right(obj, LV_PART_INDICATOR);
lv_coord_t marker_topp = lv_obj_get_style_pad_top(obj, LV_PART_INDICATOR);
lv_coord_t marker_bottomp = lv_obj_get_style_pad_bottom(obj, LV_PART_INDICATOR);
lv_coord_t transf_w = lv_obj_get_style_transform_width(obj, LV_PART_INDICATOR);
lv_coord_t transf_h = lv_obj_get_style_transform_height(obj, LV_PART_INDICATOR);
lv_draw_rect_dsc_t indic_dsc;
lv_draw_rect_dsc_init(&indic_dsc);
lv_obj_init_draw_rect_dsc(obj, LV_PART_INDICATOR, &indic_dsc);
lv_area_t marker_area;
marker_area.x1 = obj->coords.x1 + bg_leftp;
marker_area.x2 = marker_area.x1 + font_h + marker_leftp + marker_rightp - 1;
marker_area.y1 = obj->coords.y1 + bg_topp;
marker_area.y2 = marker_area.y1 + font_h + marker_topp + marker_bottomp - 1;
lv_area_t marker_area_transf;
lv_area_copy(&marker_area_transf, &marker_area);
marker_area_transf.x1 -= transf_w;
marker_area_transf.x2 += transf_w;
marker_area_transf.y1 -= transf_h;
marker_area_transf.y2 += transf_h;
lv_obj_draw_part_dsc_t part_draw_dsc;
lv_obj_draw_dsc_init(&part_draw_dsc, draw_ctx);
part_draw_dsc.rect_dsc = &indic_dsc;
part_draw_dsc.class_p = MY_CLASS;
part_draw_dsc.type = LV_CHECKBOX_DRAW_PART_BOX;
part_draw_dsc.draw_area = &marker_area_transf;
part_draw_dsc.part = LV_PART_INDICATOR;
lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &part_draw_dsc);
lv_draw_rect(draw_ctx, &indic_dsc, &marker_area_transf);
lv_event_send(obj, LV_EVENT_DRAW_PART_END, &part_draw_dsc);
lv_coord_t line_space = lv_obj_get_style_text_line_space(obj, LV_PART_MAIN);
lv_coord_t letter_space = lv_obj_get_style_text_letter_space(obj, LV_PART_MAIN);
lv_point_t txt_size;
lv_txt_get_size(&txt_size, cb->txt, font, letter_space, line_space, LV_COORD_MAX, LV_TEXT_FLAG_NONE);
lv_draw_label_dsc_t txt_dsc;
lv_draw_label_dsc_init(&txt_dsc);
lv_obj_init_draw_label_dsc(obj, LV_PART_MAIN, &txt_dsc);
lv_coord_t y_ofs = (lv_area_get_height(&marker_area) - font_h) / 2;
lv_area_t txt_area;
txt_area.x1 = marker_area.x2 + bg_colp;
txt_area.x2 = txt_area.x1 + txt_size.x;
txt_area.y1 = obj->coords.y1 + bg_topp + y_ofs;
txt_area.y2 = txt_area.y1 + txt_size.y;
lv_draw_label(draw_ctx, &txt_dsc, &txt_area, cb->txt, NULL);
}
#endif

View File

@@ -0,0 +1,97 @@
/**
* @file lv_cb.h
*
*/
#ifndef LV_CHECKBOX_H
#define LV_CHECKBOX_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../lv_conf_internal.h"
#include "../core/lv_obj.h"
#if LV_USE_CHECKBOX != 0
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_obj_t obj;
char * txt;
uint32_t static_txt : 1;
} lv_checkbox_t;
extern const lv_obj_class_t lv_checkbox_class;
/**
* `type` field in `lv_obj_draw_part_dsc_t` if `class_p = lv_checkbox_class`
* Used in `LV_EVENT_DRAW_PART_BEGIN` and `LV_EVENT_DRAW_PART_END`
*/
typedef enum {
LV_CHECKBOX_DRAW_PART_BOX, /**< The tick box*/
} lv_checkbox_draw_part_type_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a check box object
* @param parent pointer to an object, it will be the parent of the new button
* @return pointer to the created check box
*/
lv_obj_t * lv_checkbox_create(lv_obj_t * parent);
/*=====================
* Setter functions
*====================*/
/**
* Set the text of a check box. `txt` will be copied and may be deallocated
* after this function returns.
* @param cb pointer to a check box
* @param txt the text of the check box. NULL to refresh with the current text.
*/
void lv_checkbox_set_text(lv_obj_t * obj, const char * txt);
/**
* Set the text of a check box. `txt` must not be deallocated during the life
* of this checkbox.
* @param cb pointer to a check box
* @param txt the text of the check box.
*/
void lv_checkbox_set_text_static(lv_obj_t * obj, const char * txt);
/*=====================
* Getter functions
*====================*/
/**
* Get the text of a check box
* @param cb pointer to check box object
* @return pointer to the text of the check box
*/
const char * lv_checkbox_get_text(const lv_obj_t * obj);
/**********************
* MACROS
**********************/
#endif /*LV_USE_CHECKBOX*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_CHECKBOX_H*/

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,254 @@
/**
* @file lv_dropdown.h
*
*/
#ifndef LV_DROPDOWN_H
#define LV_DROPDOWN_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../lv_conf_internal.h"
#if LV_USE_DROPDOWN != 0
/*Testing of dependencies*/
#if LV_USE_LABEL == 0
#error "lv_dropdown: lv_label is required. Enable it in lv_conf.h (LV_USE_LABEL 1)"
#endif
#include "../widgets/lv_label.h"
/*********************
* DEFINES
*********************/
#define LV_DROPDOWN_POS_LAST 0xFFFF
LV_EXPORT_CONST_INT(LV_DROPDOWN_POS_LAST);
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_obj_t obj;
lv_obj_t * list; /**< The dropped down list*/
const char * text; /**< Text to display on the dropdown's button*/
const void * symbol; /**< Arrow or other icon when the drop-down list is closed*/
char * options; /**< Options in a '\n' separated list*/
uint16_t option_cnt; /**< Number of options*/
uint16_t sel_opt_id; /**< Index of the currently selected option*/
uint16_t sel_opt_id_orig; /**< Store the original index on focus*/
uint16_t pr_opt_id; /**< Index of the currently pressed option*/
lv_dir_t dir : 4; /**< Direction in which the list should open*/
uint8_t static_txt : 1; /**< 1: Only a pointer is saved in `options`*/
uint8_t selected_highlight: 1; /**< 1: Make the selected option highlighted in the list*/
} lv_dropdown_t;
typedef struct {
lv_obj_t obj;
lv_obj_t * dropdown;
} lv_dropdown_list_t;
extern const lv_obj_class_t lv_dropdown_class;
extern const lv_obj_class_t lv_dropdownlist_class;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a drop-down list object
* @param parent pointer to an object, it will be the parent of the new drop-down list
* @return pointer to the created drop-down list
*/
lv_obj_t * lv_dropdown_create(lv_obj_t * parent);
/*=====================
* Setter functions
*====================*/
/**
* Set text of the drop-down list's button.
* If set to `NULL` the selected option's text will be displayed on the button.
* If set to a specific text then that text will be shown regardless of the selected option.
* @param obj pointer to a drop-down list object
* @param txt the text as a string (Only its pointer is saved)
*/
void lv_dropdown_set_text(lv_obj_t * obj, const char * txt);
/**
* Set the options in a drop-down list from a string.
* The options will be copied and saved in the object so the `options` can be destroyed after calling this function
* @param obj pointer to drop-down list object
* @param options a string with '\n' separated options. E.g. "One\nTwo\nThree"
*/
void lv_dropdown_set_options(lv_obj_t * obj, const char * options);
/**
* Set the options in a drop-down list from a static string (global, static or dynamically allocated).
* Only the pointer of the option string will be saved.
* @param obj pointer to drop-down list object
* @param options a static string with '\n' separated options. E.g. "One\nTwo\nThree"
*/
void lv_dropdown_set_options_static(lv_obj_t * obj, const char * options);
/**
* Add an options to a drop-down list from a string. Only works for non-static options.
* @param obj pointer to drop-down list object
* @param option a string without '\n'. E.g. "Four"
* @param pos the insert position, indexed from 0, LV_DROPDOWN_POS_LAST = end of string
*/
void lv_dropdown_add_option(lv_obj_t * obj, const char * option, uint32_t pos);
/**
* Clear all options in a drop-down list. Works with both static and dynamic options.
* @param obj pointer to drop-down list object
*/
void lv_dropdown_clear_options(lv_obj_t * obj);
/**
* Set the selected option
* @param obj pointer to drop-down list object
* @param sel_opt id of the selected option (0 ... number of option - 1);
*/
void lv_dropdown_set_selected(lv_obj_t * obj, uint16_t sel_opt);
/**
* Set the direction of the a drop-down list
* @param obj pointer to a drop-down list object
* @param dir LV_DIR_LEFT/RIGHT/TOP/BOTTOM
*/
void lv_dropdown_set_dir(lv_obj_t * obj, lv_dir_t dir);
/**
* Set an arrow or other symbol to display when on drop-down list's button. Typically a down caret or arrow.
* @param obj pointer to drop-down list object
* @param symbol a text like `LV_SYMBOL_DOWN`, an image (pointer or path) or NULL to not draw symbol icon
* @note angle and zoom transformation can be applied if the symbol is an image.
* E.g. when drop down is checked (opened) rotate the symbol by 180 degree
*/
void lv_dropdown_set_symbol(lv_obj_t * obj, const void * symbol);
/**
* Set whether the selected option in the list should be highlighted or not
* @param obj pointer to drop-down list object
* @param en true: highlight enabled; false: disabled
*/
void lv_dropdown_set_selected_highlight(lv_obj_t * obj, bool en);
/*=====================
* Getter functions
*====================*/
/**
* Get the list of a drop-down to allow styling or other modifications
* @param obj pointer to a drop-down list object
* @return pointer to the list of the drop-down
*/
lv_obj_t * lv_dropdown_get_list(lv_obj_t * obj);
/**
* Get text of the drop-down list's button.
* @param obj pointer to a drop-down list object
* @return the text as string, `NULL` if no text
*/
const char * lv_dropdown_get_text(lv_obj_t * obj);
/**
* Get the options of a drop-down list
* @param obj pointer to drop-down list object
* @return the options separated by '\n'-s (E.g. "Option1\nOption2\nOption3")
*/
const char * lv_dropdown_get_options(const lv_obj_t * obj);
/**
* Get the index of the selected option
* @param obj pointer to drop-down list object
* @return index of the selected option (0 ... number of option - 1);
*/
uint16_t lv_dropdown_get_selected(const lv_obj_t * obj);
/**
* Get the total number of options
* @param obj pointer to drop-down list object
* @return the total number of options in the list
*/
uint16_t lv_dropdown_get_option_cnt(const lv_obj_t * obj);
/**
* Get the current selected option as a string
* @param obj pointer to drop-down object
* @param buf pointer to an array to store the string
* @param buf_size size of `buf` in bytes. 0: to ignore it.
*/
void lv_dropdown_get_selected_str(const lv_obj_t * obj, char * buf, uint32_t buf_size);
/**
* Get the index of an option.
* @param obj pointer to drop-down object
* @param option an option as string
* @return index of `option` in the list of all options. -1 if not found.
*/
int32_t lv_dropdown_get_option_index(lv_obj_t * obj, const char * option);
/**
* Get the symbol on the drop-down list. Typically a down caret or arrow.
* @param obj pointer to drop-down list object
* @return the symbol or NULL if not enabled
*/
const char * lv_dropdown_get_symbol(lv_obj_t * obj);
/**
* Get whether the selected option in the list should be highlighted or not
* @param obj pointer to drop-down list object
* @return true: highlight enabled; false: disabled
*/
bool lv_dropdown_get_selected_highlight(lv_obj_t * obj);
/**
* Get the direction of the drop-down list
* @param obj pointer to a drop-down list object
* @return LV_DIR_LEF/RIGHT/TOP/BOTTOM
*/
lv_dir_t lv_dropdown_get_dir(const lv_obj_t * obj);
/*=====================
* Other functions
*====================*/
/**
* Open the drop.down list
* @param obj pointer to drop-down list object
*/
void lv_dropdown_open(lv_obj_t * dropdown_obj);
/**
* Close (Collapse) the drop-down list
* @param obj pointer to drop-down list object
*/
void lv_dropdown_close(lv_obj_t * obj);
/**
* Tells whether the list is opened or not
* @param obj pointer to a drop-down list object
* @return true if the list os opened
*/
bool lv_dropdown_is_open(lv_obj_t * obj);
/**********************
* MACROS
**********************/
#endif /*LV_USE_DROPDOWN*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_DROPDOWN_H*/

View File

@@ -0,0 +1,693 @@
/**
* @file lv_img.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_img.h"
#if LV_USE_IMG != 0
#include "../core/lv_disp.h"
#include "../misc/lv_assert.h"
#include "../draw/lv_img_decoder.h"
#include "../misc/lv_fs.h"
#include "../misc/lv_txt.h"
#include "../misc/lv_math.h"
#include "../misc/lv_log.h"
/*********************
* DEFINES
*********************/
#define MY_CLASS &lv_img_class
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_img_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_img_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_img_event(const lv_obj_class_t * class_p, lv_event_t * e);
static void draw_img(lv_event_t * e);
/**********************
* STATIC VARIABLES
**********************/
const lv_obj_class_t lv_img_class = {
.constructor_cb = lv_img_constructor,
.destructor_cb = lv_img_destructor,
.event_cb = lv_img_event,
.width_def = LV_SIZE_CONTENT,
.height_def = LV_SIZE_CONTENT,
.instance_size = sizeof(lv_img_t),
.base_class = &lv_obj_class
};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_obj_t * lv_img_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_img_set_src(lv_obj_t * obj, const void * src)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_obj_invalidate(obj);
lv_img_src_t src_type = lv_img_src_get_type(src);
lv_img_t * img = (lv_img_t *)obj;
#if LV_USE_LOG && LV_LOG_LEVEL >= LV_LOG_LEVEL_INFO
switch(src_type) {
case LV_IMG_SRC_FILE:
LV_LOG_TRACE("lv_img_set_src: `LV_IMG_SRC_FILE` type found");
break;
case LV_IMG_SRC_VARIABLE:
LV_LOG_TRACE("lv_img_set_src: `LV_IMG_SRC_VARIABLE` type found");
break;
case LV_IMG_SRC_SYMBOL:
LV_LOG_TRACE("lv_img_set_src: `LV_IMG_SRC_SYMBOL` type found");
break;
default:
LV_LOG_WARN("lv_img_set_src: unknown type");
}
#endif
/*If the new source type is unknown free the memories of the old source*/
if(src_type == LV_IMG_SRC_UNKNOWN) {
LV_LOG_WARN("lv_img_set_src: unknown image type");
if(img->src_type == LV_IMG_SRC_SYMBOL || img->src_type == LV_IMG_SRC_FILE) {
lv_mem_free((void *)img->src);
}
img->src = NULL;
img->src_type = LV_IMG_SRC_UNKNOWN;
return;
}
lv_img_header_t header;
lv_img_decoder_get_info(src, &header);
/*Save the source*/
if(src_type == LV_IMG_SRC_VARIABLE) {
/*If memory was allocated because of the previous `src_type` then free it*/
if(img->src_type == LV_IMG_SRC_FILE || img->src_type == LV_IMG_SRC_SYMBOL) {
lv_mem_free((void *)img->src);
}
img->src = src;
}
else if(src_type == LV_IMG_SRC_FILE || src_type == LV_IMG_SRC_SYMBOL) {
/*If the new and the old src are the same then it was only a refresh.*/
if(img->src != src) {
const void * old_src = NULL;
/*If memory was allocated because of the previous `src_type` then save its pointer and free after allocation.
*It's important to allocate first to be sure the new data will be on a new address.
*Else `img_cache` wouldn't see the change in source.*/
if(img->src_type == LV_IMG_SRC_FILE || img->src_type == LV_IMG_SRC_SYMBOL) {
old_src = img->src;
}
char * new_str = lv_mem_alloc(strlen(src) + 1);
LV_ASSERT_MALLOC(new_str);
if(new_str == NULL) return;
strcpy(new_str, src);
img->src = new_str;
if(old_src) lv_mem_free((void *)old_src);
}
}
if(src_type == LV_IMG_SRC_SYMBOL) {
/*`lv_img_dsc_get_info` couldn't set the width and height of a font so set it here*/
const lv_font_t * font = lv_obj_get_style_text_font(obj, LV_PART_MAIN);
lv_coord_t letter_space = lv_obj_get_style_text_letter_space(obj, LV_PART_MAIN);
lv_coord_t line_space = lv_obj_get_style_text_line_space(obj, LV_PART_MAIN);
lv_point_t size;
lv_txt_get_size(&size, src, font, letter_space, line_space, LV_COORD_MAX, LV_TEXT_FLAG_NONE);
header.w = size.x;
header.h = size.y;
}
img->src_type = src_type;
img->w = header.w;
img->h = header.h;
img->cf = header.cf;
img->pivot.x = header.w / 2;
img->pivot.y = header.h / 2;
lv_obj_refresh_self_size(obj);
/*Provide enough room for the rotated corners*/
if(img->angle || img->zoom != LV_IMG_ZOOM_NONE) lv_obj_refresh_ext_draw_size(obj);
lv_obj_invalidate(obj);
}
void lv_img_set_offset_x(lv_obj_t * obj, lv_coord_t x)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_img_t * img = (lv_img_t *)obj;
x = x % img->w;
img->offset.x = x;
lv_obj_invalidate(obj);
}
void lv_img_set_offset_y(lv_obj_t * obj, lv_coord_t y)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_img_t * img = (lv_img_t *)obj;
y = y % img->h;
img->offset.y = y;
lv_obj_invalidate(obj);
}
void lv_img_set_angle(lv_obj_t * obj, int16_t angle)
{
if(angle < 0 || angle >= 3600) angle = angle % 3600;
lv_img_t * img = (lv_img_t *)obj;
if(angle == img->angle) return;
lv_obj_update_layout(obj); /*Be sure the object's size is calculated*/
lv_coord_t w = lv_obj_get_width(obj);
lv_coord_t h = lv_obj_get_height(obj);
lv_area_t a;
_lv_img_buf_get_transformed_area(&a, w, h, img->angle, img->zoom, &img->pivot);
a.x1 += obj->coords.x1;
a.y1 += obj->coords.y1;
a.x2 += obj->coords.x1;
a.y2 += obj->coords.y1;
lv_obj_invalidate_area(obj, &a);
img->angle = angle;
/* Disable invalidations because lv_obj_refresh_ext_draw_size would invalidate
* the whole ext draw area */
lv_disp_t * disp = lv_obj_get_disp(obj);
lv_disp_enable_invalidation(disp, false);
lv_obj_refresh_ext_draw_size(obj);
lv_disp_enable_invalidation(disp, true);
_lv_img_buf_get_transformed_area(&a, w, h, img->angle, img->zoom, &img->pivot);
a.x1 += obj->coords.x1;
a.y1 += obj->coords.y1;
a.x2 += obj->coords.x1;
a.y2 += obj->coords.y1;
lv_obj_invalidate_area(obj, &a);
}
void lv_img_set_pivot(lv_obj_t * obj, lv_coord_t x, lv_coord_t y)
{
lv_img_t * img = (lv_img_t *)obj;
if(img->pivot.x == x && img->pivot.y == y) return;
lv_obj_update_layout(obj); /*Be sure the object's size is calculated*/
lv_coord_t w = lv_obj_get_width(obj);
lv_coord_t h = lv_obj_get_height(obj);
lv_area_t a;
_lv_img_buf_get_transformed_area(&a, w, h, img->angle, img->zoom, &img->pivot);
a.x1 += obj->coords.x1;
a.y1 += obj->coords.y1;
a.x2 += obj->coords.x1;
a.y2 += obj->coords.y1;
lv_obj_invalidate_area(obj, &a);
img->pivot.x = x;
img->pivot.y = y;
/* Disable invalidations because lv_obj_refresh_ext_draw_size would invalidate
* the whole ext draw area */
lv_disp_t * disp = lv_obj_get_disp(obj);
lv_disp_enable_invalidation(disp, false);
lv_obj_refresh_ext_draw_size(obj);
lv_disp_enable_invalidation(disp, true);
_lv_img_buf_get_transformed_area(&a, w, h, img->angle, img->zoom, &img->pivot);
a.x1 += obj->coords.x1;
a.y1 += obj->coords.y1;
a.x2 += obj->coords.x1;
a.y2 += obj->coords.y1;
lv_obj_invalidate_area(obj, &a);
}
void lv_img_set_zoom(lv_obj_t * obj, uint16_t zoom)
{
lv_img_t * img = (lv_img_t *)obj;
if(zoom == img->zoom) return;
if(zoom == 0) zoom = 1;
lv_obj_update_layout(obj); /*Be sure the object's size is calculated*/
lv_coord_t w = lv_obj_get_width(obj);
lv_coord_t h = lv_obj_get_height(obj);
lv_area_t a;
_lv_img_buf_get_transformed_area(&a, w, h, img->angle, img->zoom >> 8, &img->pivot);
a.x1 += obj->coords.x1 - 1;
a.y1 += obj->coords.y1 - 1;
a.x2 += obj->coords.x1 + 1;
a.y2 += obj->coords.y1 + 1;
lv_obj_invalidate_area(obj, &a);
img->zoom = zoom;
/* Disable invalidations because lv_obj_refresh_ext_draw_size would invalidate
* the whole ext draw area */
lv_disp_t * disp = lv_obj_get_disp(obj);
lv_disp_enable_invalidation(disp, false);
lv_obj_refresh_ext_draw_size(obj);
lv_disp_enable_invalidation(disp, true);
_lv_img_buf_get_transformed_area(&a, w, h, img->angle, img->zoom, &img->pivot);
a.x1 += obj->coords.x1 - 1;
a.y1 += obj->coords.y1 - 1;
a.x2 += obj->coords.x1 + 1;
a.y2 += obj->coords.y1 + 1;
lv_obj_invalidate_area(obj, &a);
}
void lv_img_set_antialias(lv_obj_t * obj, bool antialias)
{
lv_img_t * img = (lv_img_t *)obj;
if(antialias == img->antialias) return;
img->antialias = antialias;
lv_obj_invalidate(obj);
}
void lv_img_set_size_mode(lv_obj_t * obj, lv_img_size_mode_t mode)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_img_t * img = (lv_img_t *)obj;
if(mode == img->obj_size_mode) return;
img->obj_size_mode = mode;
lv_obj_invalidate(obj);
}
/*=====================
* Getter functions
*====================*/
const void * lv_img_get_src(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_img_t * img = (lv_img_t *)obj;
return img->src;
}
lv_coord_t lv_img_get_offset_x(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_img_t * img = (lv_img_t *)obj;
return img->offset.x;
}
lv_coord_t lv_img_get_offset_y(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_img_t * img = (lv_img_t *)obj;
return img->offset.y;
}
uint16_t lv_img_get_angle(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_img_t * img = (lv_img_t *)obj;
return img->angle;
}
void lv_img_get_pivot(lv_obj_t * obj, lv_point_t * pivot)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_img_t * img = (lv_img_t *)obj;
*pivot = img->pivot;
}
uint16_t lv_img_get_zoom(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_img_t * img = (lv_img_t *)obj;
return img->zoom;
}
bool lv_img_get_antialias(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_img_t * img = (lv_img_t *)obj;
return img->antialias ? true : false;
}
lv_img_size_mode_t lv_img_get_size_mode(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_img_t * img = (lv_img_t *)obj;
return img->obj_size_mode;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_img_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
LV_TRACE_OBJ_CREATE("begin");
lv_img_t * img = (lv_img_t *)obj;
img->src = NULL;
img->src_type = LV_IMG_SRC_UNKNOWN;
img->cf = LV_IMG_CF_UNKNOWN;
img->w = lv_obj_get_width(obj);
img->h = lv_obj_get_height(obj);
img->angle = 0;
img->zoom = LV_IMG_ZOOM_NONE;
img->antialias = LV_COLOR_DEPTH > 8 ? 1 : 0;
img->offset.x = 0;
img->offset.y = 0;
img->pivot.x = 0;
img->pivot.y = 0;
img->obj_size_mode = LV_IMG_SIZE_MODE_VIRTUAL;
lv_obj_clear_flag(obj, LV_OBJ_FLAG_CLICKABLE);
lv_obj_add_flag(obj, LV_OBJ_FLAG_ADV_HITTEST);
LV_TRACE_OBJ_CREATE("finished");
}
static void lv_img_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
lv_img_t * img = (lv_img_t *)obj;
if(img->src_type == LV_IMG_SRC_FILE || img->src_type == LV_IMG_SRC_SYMBOL) {
lv_mem_free((void *)img->src);
img->src = NULL;
img->src_type = LV_IMG_SRC_UNKNOWN;
}
}
static lv_point_t lv_img_get_transformed_size(lv_obj_t * obj)
{
lv_img_t * img = (lv_img_t *)obj;
lv_area_t area_transform;
_lv_img_buf_get_transformed_area(&area_transform, img->w, img->h,
img->angle, img->zoom, &img->pivot);
return (lv_point_t) {
lv_area_get_width(&area_transform), lv_area_get_height(&area_transform)
};
}
static void lv_img_event(const lv_obj_class_t * class_p, lv_event_t * e)
{
LV_UNUSED(class_p);
lv_event_code_t code = lv_event_get_code(e);
/*Ancestor events will be called during drawing*/
if(code != LV_EVENT_DRAW_MAIN && code != LV_EVENT_DRAW_POST) {
/*Call the ancestor's event handler*/
lv_res_t res = lv_obj_event_base(MY_CLASS, e);
if(res != LV_RES_OK) return;
}
lv_obj_t * obj = lv_event_get_target(e);
lv_img_t * img = (lv_img_t *)obj;
if(code == LV_EVENT_STYLE_CHANGED) {
/*Refresh the file name to refresh the symbol text size*/
if(img->src_type == LV_IMG_SRC_SYMBOL) {
lv_img_set_src(obj, img->src);
}
else {
/*With transformation it might change*/
lv_obj_refresh_ext_draw_size(obj);
}
}
else if(code == LV_EVENT_REFR_EXT_DRAW_SIZE) {
lv_coord_t * s = lv_event_get_param(e);
/*If the image has angle provide enough room for the rotated corners*/
if(img->angle || img->zoom != LV_IMG_ZOOM_NONE) {
lv_area_t a;
lv_coord_t w = lv_obj_get_width(obj);
lv_coord_t h = lv_obj_get_height(obj);
_lv_img_buf_get_transformed_area(&a, w, h, img->angle, img->zoom, &img->pivot);
*s = LV_MAX(*s, -a.x1);
*s = LV_MAX(*s, -a.y1);
*s = LV_MAX(*s, a.x2 - w);
*s = LV_MAX(*s, a.y2 - h);
}
}
else if(code == LV_EVENT_HIT_TEST) {
lv_hit_test_info_t * info = lv_event_get_param(e);
/*If the object is exactly image sized (not cropped, not mosaic) and transformed
*perform hit test on its transformed area*/
if(img->w == lv_obj_get_width(obj) && img->h == lv_obj_get_height(obj) &&
(img->zoom != LV_IMG_ZOOM_NONE || img->angle != 0 || img->pivot.x != img->w / 2 || img->pivot.y != img->h / 2)) {
lv_coord_t w = lv_obj_get_width(obj);
lv_coord_t h = lv_obj_get_height(obj);
lv_area_t coords;
_lv_img_buf_get_transformed_area(&coords, w, h, img->angle, img->zoom, &img->pivot);
coords.x1 += obj->coords.x1;
coords.y1 += obj->coords.y1;
coords.x2 += obj->coords.x1;
coords.y2 += obj->coords.y1;
info->res = _lv_area_is_point_on(&coords, info->point, 0);
}
else {
lv_area_t a;
lv_obj_get_click_area(obj, &a);
info->res = _lv_area_is_point_on(&a, info->point, 0);
}
}
else if(code == LV_EVENT_GET_SELF_SIZE) {
lv_point_t * p = lv_event_get_param(e);
if(img->obj_size_mode == LV_IMG_SIZE_MODE_REAL) {
*p = lv_img_get_transformed_size(obj);
}
else {
p->x = img->w;
p->y = img->h;
}
}
else if(code == LV_EVENT_DRAW_MAIN || code == LV_EVENT_DRAW_POST || code == LV_EVENT_COVER_CHECK) {
draw_img(e);
}
}
static void draw_img(lv_event_t * e)
{
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * obj = lv_event_get_target(e);
lv_img_t * img = (lv_img_t *)obj;
if(code == LV_EVENT_COVER_CHECK) {
lv_cover_check_info_t * info = lv_event_get_param(e);
if(info->res == LV_COVER_RES_MASKED) return;
if(img->src_type == LV_IMG_SRC_UNKNOWN || img->src_type == LV_IMG_SRC_SYMBOL) {
info->res = LV_COVER_RES_NOT_COVER;
return;
}
/*Non true color format might have "holes"*/
if(img->cf != LV_IMG_CF_TRUE_COLOR && img->cf != LV_IMG_CF_RAW) {
info->res = LV_COVER_RES_NOT_COVER;
return;
}
/*With not LV_OPA_COVER images can't cover an area */
if(lv_obj_get_style_img_opa(obj, LV_PART_MAIN) != LV_OPA_COVER) {
info->res = LV_COVER_RES_NOT_COVER;
return;
}
if(img->angle != 0) {
info->res = LV_COVER_RES_NOT_COVER;
return;
}
const lv_area_t * clip_area = lv_event_get_param(e);
if(img->zoom == LV_IMG_ZOOM_NONE) {
if(_lv_area_is_in(clip_area, &obj->coords, 0) == false) {
info->res = LV_COVER_RES_NOT_COVER;
return;
}
}
else {
lv_area_t a;
_lv_img_buf_get_transformed_area(&a, lv_obj_get_width(obj), lv_obj_get_height(obj), 0, img->zoom, &img->pivot);
a.x1 += obj->coords.x1;
a.y1 += obj->coords.y1;
a.x2 += obj->coords.x1;
a.y2 += obj->coords.y1;
if(_lv_area_is_in(clip_area, &a, 0) == false) {
info->res = LV_COVER_RES_NOT_COVER;
return;
}
}
}
else if(code == LV_EVENT_DRAW_MAIN || code == LV_EVENT_DRAW_POST) {
lv_coord_t obj_w = lv_obj_get_width(obj);
lv_coord_t obj_h = lv_obj_get_height(obj);
lv_coord_t border_width = lv_obj_get_style_border_width(obj, LV_PART_MAIN);
lv_coord_t pleft = lv_obj_get_style_pad_left(obj, LV_PART_MAIN) + border_width;
lv_coord_t pright = lv_obj_get_style_pad_right(obj, LV_PART_MAIN) + border_width;
lv_coord_t ptop = lv_obj_get_style_pad_top(obj, LV_PART_MAIN) + border_width;
lv_coord_t pbottom = lv_obj_get_style_pad_bottom(obj, LV_PART_MAIN) + border_width;
lv_point_t bg_pivot;
bg_pivot.x = img->pivot.x + pleft;
bg_pivot.y = img->pivot.y + ptop;
lv_area_t bg_coords;
if(img->obj_size_mode == LV_IMG_SIZE_MODE_REAL) {
/*Object size equals to transformed image size*/
lv_obj_get_coords(obj, &bg_coords);
}
else {
_lv_img_buf_get_transformed_area(&bg_coords, obj_w, obj_h,
img->angle, img->zoom, &bg_pivot);
/*Modify the coordinates to draw the background for the rotated and scaled coordinates*/
bg_coords.x1 += obj->coords.x1;
bg_coords.y1 += obj->coords.y1;
bg_coords.x2 += obj->coords.x1;
bg_coords.y2 += obj->coords.y1;
}
lv_area_t ori_coords;
lv_area_copy(&ori_coords, &obj->coords);
lv_area_copy(&obj->coords, &bg_coords);
lv_res_t res = lv_obj_event_base(MY_CLASS, e);
if(res != LV_RES_OK) return;
lv_area_copy(&obj->coords, &ori_coords);
if(code == LV_EVENT_DRAW_MAIN) {
if(img->h == 0 || img->w == 0) return;
if(img->zoom == 0) return;
lv_draw_ctx_t * draw_ctx = lv_event_get_draw_ctx(e);
lv_area_t img_max_area;
lv_area_copy(&img_max_area, &obj->coords);
lv_point_t img_size_final = lv_img_get_transformed_size(obj);
if(img->obj_size_mode == LV_IMG_SIZE_MODE_REAL) {
img_max_area.x1 -= ((img->w - img_size_final.x) + 1) / 2;
img_max_area.x2 -= ((img->w - img_size_final.x) + 1) / 2;
img_max_area.y1 -= ((img->h - img_size_final.y) + 1) / 2;
img_max_area.y2 -= ((img->h - img_size_final.y) + 1) / 2;
}
else {
img_max_area.x2 = img_max_area.x1 + lv_area_get_width(&bg_coords) - 1;
img_max_area.y2 = img_max_area.y1 + lv_area_get_height(&bg_coords) - 1;
}
img_max_area.x1 += pleft;
img_max_area.y1 += ptop;
img_max_area.x2 -= pright;
img_max_area.y2 -= pbottom;
if(img->src_type == LV_IMG_SRC_FILE || img->src_type == LV_IMG_SRC_VARIABLE) {
lv_draw_img_dsc_t img_dsc;
lv_draw_img_dsc_init(&img_dsc);
lv_obj_init_draw_img_dsc(obj, LV_PART_MAIN, &img_dsc);
img_dsc.zoom = img->zoom;
img_dsc.angle = img->angle;
img_dsc.pivot.x = img->pivot.x;
img_dsc.pivot.y = img->pivot.y;
img_dsc.antialias = img->antialias;
lv_area_t img_clip_area;
img_clip_area.x1 = bg_coords.x1 + pleft;
img_clip_area.y1 = bg_coords.y1 + ptop;
img_clip_area.x2 = bg_coords.x2 - pright;
img_clip_area.y2 = bg_coords.y2 - pbottom;
const lv_area_t * clip_area_ori = draw_ctx->clip_area;
if(!_lv_area_intersect(&img_clip_area, draw_ctx->clip_area, &img_clip_area)) return;
draw_ctx->clip_area = &img_clip_area;
lv_area_t coords_tmp;
coords_tmp.y1 = img_max_area.y1 + img->offset.y;
if(coords_tmp.y1 > img_max_area.y1) coords_tmp.y1 -= img->h;
coords_tmp.y2 = coords_tmp.y1 + img->h - 1;
for(; coords_tmp.y1 < img_max_area.y2; coords_tmp.y1 += img_size_final.y, coords_tmp.y2 += img_size_final.y) {
coords_tmp.x1 = img_max_area.x1 + img->offset.x;
if(coords_tmp.x1 > img_max_area.x1) coords_tmp.x1 -= img->w;
coords_tmp.x2 = coords_tmp.x1 + img->w - 1;
for(; coords_tmp.x1 < img_max_area.x2; coords_tmp.x1 += img_size_final.x, coords_tmp.x2 += img_size_final.x) {
lv_draw_img(draw_ctx, &img_dsc, &coords_tmp, img->src);
}
}
draw_ctx->clip_area = clip_area_ori;
}
else if(img->src_type == LV_IMG_SRC_SYMBOL) {
lv_draw_label_dsc_t label_dsc;
lv_draw_label_dsc_init(&label_dsc);
lv_obj_init_draw_label_dsc(obj, LV_PART_MAIN, &label_dsc);
lv_draw_label(draw_ctx, &label_dsc, &obj->coords, img->src, NULL);
}
else {
/*Trigger the error handler of image draw*/
LV_LOG_WARN("draw_img: image source type is unknown");
lv_draw_img(draw_ctx, NULL, &obj->coords, NULL);
}
}
}
}
#endif

View File

@@ -0,0 +1,234 @@
/**
* @file lv_img.h
*
*/
#ifndef LV_IMG_H
#define LV_IMG_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../lv_conf_internal.h"
#if LV_USE_IMG != 0
/*Testing of dependencies*/
#if LV_USE_LABEL == 0
#error "lv_img: lv_label is required. Enable it in lv_conf.h (LV_USE_LABEL 1)"
#endif
#include "../core/lv_obj.h"
#include "../misc/lv_fs.h"
#include "../draw/lv_draw.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**
* Data of image
*/
typedef struct {
lv_obj_t obj;
const void * src; /*Image source: Pointer to an array or a file or a symbol*/
lv_point_t offset;
lv_coord_t w; /*Width of the image (Handled by the library)*/
lv_coord_t h; /*Height of the image (Handled by the library)*/
uint16_t angle; /*rotation angle of the image*/
lv_point_t pivot; /*rotation center of the image*/
uint16_t zoom; /*256 means no zoom, 512 double size, 128 half size*/
uint8_t src_type : 2; /*See: lv_img_src_t*/
uint8_t cf : 5; /*Color format from `lv_img_color_format_t`*/
uint8_t antialias : 1; /*Apply anti-aliasing in transformations (rotate, zoom)*/
uint8_t obj_size_mode: 2; /*Image size mode when image size and object size is different.*/
} lv_img_t;
extern const lv_obj_class_t lv_img_class;
/**
* Image size mode, when image size and object size is different
*/
enum {
/** Zoom doesn't affect the coordinates of the object,
* however if zoomed in the image is drawn out of the its coordinates.
* The layout's won't change on zoom */
LV_IMG_SIZE_MODE_VIRTUAL = 0,
/** If the object size is set to SIZE_CONTENT, then object size equals zoomed image size.
* It causes layout recalculation.
* If the object size is set explicitly, the image will be cropped when zoomed in.*/
LV_IMG_SIZE_MODE_REAL,
};
typedef uint8_t lv_img_size_mode_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create an image object
* @param parent pointer to an object, it will be the parent of the new image
* @return pointer to the created image
*/
lv_obj_t * lv_img_create(lv_obj_t * parent);
/*=====================
* Setter functions
*====================*/
/**
* Set the image data to display on the object
* @param obj pointer to an image object
* @param src_img 1) pointer to an ::lv_img_dsc_t descriptor (converted by LVGL's image converter) (e.g. &my_img) or
* 2) path to an image file (e.g. "S:/dir/img.bin")or
* 3) a SYMBOL (e.g. LV_SYMBOL_OK)
*/
void lv_img_set_src(lv_obj_t * obj, const void * src);
/**
* Set an offset for the source of an image so the image will be displayed from the new origin.
* @param obj pointer to an image
* @param x the new offset along x axis.
*/
void lv_img_set_offset_x(lv_obj_t * obj, lv_coord_t x);
/**
* Set an offset for the source of an image.
* so the image will be displayed from the new origin.
* @param obj pointer to an image
* @param y the new offset along y axis.
*/
void lv_img_set_offset_y(lv_obj_t * obj, lv_coord_t y);
/**
* Set the rotation angle of the image.
* The image will be rotated around the set pivot set by `lv_img_set_pivot()`
* Note that indexed and alpha only images can't be transformed.
* @param obj pointer to an image object
* @param angle rotation angle in degree with 0.1 degree resolution (0..3600: clock wise)
*/
void lv_img_set_angle(lv_obj_t * obj, int16_t angle);
/**
* Set the rotation center of the image.
* The image will be rotated around this point.
* @param obj pointer to an image object
* @param x rotation center x of the image
* @param y rotation center y of the image
*/
void lv_img_set_pivot(lv_obj_t * obj, lv_coord_t x, lv_coord_t y);
/**
* Set the zoom factor of the image.
* Note that indexed and alpha only images can't be transformed.
* @param img pointer to an image object
* @param zoom the zoom factor.
* @example 256 or LV_ZOOM_IMG_NONE for no zoom
* @example <256: scale down
* @example >256 scale up
* @example 128 half size
* @example 512 double size
*/
void lv_img_set_zoom(lv_obj_t * obj, uint16_t zoom);
/**
* Enable/disable anti-aliasing for the transformations (rotate, zoom) or not.
* The quality is better with anti-aliasing looks better but slower.
* @param obj pointer to an image object
* @param antialias true: anti-aliased; false: not anti-aliased
*/
void lv_img_set_antialias(lv_obj_t * obj, bool antialias);
/**
* Set the image object size mode.
*
* @param obj pointer to an image object
* @param mode the new size mode.
*/
void lv_img_set_size_mode(lv_obj_t * obj, lv_img_size_mode_t mode);
/*=====================
* Getter functions
*====================*/
/**
* Get the source of the image
* @param obj pointer to an image object
* @return the image source (symbol, file name or ::lv-img_dsc_t for C arrays)
*/
const void * lv_img_get_src(lv_obj_t * obj);
/**
* Get the offset's x attribute of the image object.
* @param img pointer to an image
* @return offset X value.
*/
lv_coord_t lv_img_get_offset_x(lv_obj_t * obj);
/**
* Get the offset's y attribute of the image object.
* @param obj pointer to an image
* @return offset Y value.
*/
lv_coord_t lv_img_get_offset_y(lv_obj_t * obj);
/**
* Get the rotation angle of the image.
* @param obj pointer to an image object
* @return rotation angle in 0.1 degrees (0..3600)
*/
uint16_t lv_img_get_angle(lv_obj_t * obj);
/**
* Get the pivot (rotation center) of the image.
* @param img pointer to an image object
* @param pivot store the rotation center here
*/
void lv_img_get_pivot(lv_obj_t * obj, lv_point_t * pivot);
/**
* Get the zoom factor of the image.
* @param obj pointer to an image object
* @return zoom factor (256: no zoom)
*/
uint16_t lv_img_get_zoom(lv_obj_t * obj);
/**
* Get whether the transformations (rotate, zoom) are anti-aliased or not
* @param obj pointer to an image object
* @return true: anti-aliased; false: not anti-aliased
*/
bool lv_img_get_antialias(lv_obj_t * obj);
/**
* Get the size mode of the image
* @param obj pointer to an image object
* @return element of @ref lv_img_size_mode_t
*/
lv_img_size_mode_t lv_img_get_size_mode(lv_obj_t * obj);
/**********************
* MACROS
**********************/
/** Use this macro to declare an image in a C file*/
#define LV_IMG_DECLARE(var_name) extern const lv_img_dsc_t var_name;
#endif /*LV_USE_IMG*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_IMG_H*/

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,246 @@
/**
* @file lv_label.h
*
*/
#ifndef LV_LABEL_H
#define LV_LABEL_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../lv_conf_internal.h"
#if LV_USE_LABEL != 0
#include <stdarg.h>
#include "../core/lv_obj.h"
#include "../font/lv_font.h"
#include "../font/lv_symbol_def.h"
#include "../misc/lv_txt.h"
#include "../draw/lv_draw.h"
/*********************
* DEFINES
*********************/
#define LV_LABEL_WAIT_CHAR_COUNT 3
#define LV_LABEL_DOT_NUM 3
#define LV_LABEL_POS_LAST 0xFFFF
#define LV_LABEL_TEXT_SELECTION_OFF LV_DRAW_LABEL_NO_TXT_SEL
LV_EXPORT_CONST_INT(LV_LABEL_DOT_NUM);
LV_EXPORT_CONST_INT(LV_LABEL_POS_LAST);
LV_EXPORT_CONST_INT(LV_LABEL_TEXT_SELECTION_OFF);
/**********************
* TYPEDEFS
**********************/
/** Long mode behaviors. Used in 'lv_label_ext_t'*/
enum {
LV_LABEL_LONG_WRAP, /**< Keep the object width, wrap the too long lines and expand the object height*/
LV_LABEL_LONG_DOT, /**< Keep the size and write dots at the end if the text is too long*/
LV_LABEL_LONG_SCROLL, /**< Keep the size and roll the text back and forth*/
LV_LABEL_LONG_SCROLL_CIRCULAR, /**< Keep the size and roll the text circularly*/
LV_LABEL_LONG_CLIP, /**< Keep the size and clip the text out of it*/
};
typedef uint8_t lv_label_long_mode_t;
typedef struct {
lv_obj_t obj;
char * text;
union {
char * tmp_ptr; /*Pointer to the allocated memory containing the character replaced by dots*/
char tmp[LV_LABEL_DOT_NUM + 1]; /*Directly store the characters if <=4 characters*/
} dot;
uint32_t dot_end; /*The real text length, used in dot mode*/
#if LV_LABEL_LONG_TXT_HINT
lv_draw_label_hint_t hint;
#endif
#if LV_LABEL_TEXT_SELECTION
uint32_t sel_start;
uint32_t sel_end;
#endif
lv_point_t offset; /*Text draw position offset*/
lv_label_long_mode_t long_mode : 3; /*Determine what to do with the long texts*/
uint8_t static_txt : 1; /*Flag to indicate the text is static*/
uint8_t recolor : 1; /*Enable in-line letter re-coloring*/
uint8_t expand : 1; /*Ignore real width (used by the library with LV_LABEL_LONG_SCROLL)*/
uint8_t dot_tmp_alloc : 1; /*1: dot is allocated, 0: dot directly holds up to 4 chars*/
} lv_label_t;
extern const lv_obj_class_t lv_label_class;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a label object
* @param parent pointer to an object, it will be the parent of the new label.
* @return pointer to the created button
*/
lv_obj_t * lv_label_create(lv_obj_t * parent);
/*=====================
* Setter functions
*====================*/
/**
* Set a new text for a label. Memory will be allocated to store the text by the label.
* @param obj pointer to a label object
* @param text '\0' terminated character string. NULL to refresh with the current text.
*/
void lv_label_set_text(lv_obj_t * obj, const char * text);
/**
* Set a new formatted text for a label. Memory will be allocated to store the text by the label.
* @param obj pointer to a label object
* @param fmt `printf`-like format
* @example lv_label_set_text_fmt(label1, "%d user", user_num);
*/
void lv_label_set_text_fmt(lv_obj_t * obj, const char * fmt, ...) LV_FORMAT_ATTRIBUTE(2, 3);
/**
* Set a static text. It will not be saved by the label so the 'text' variable
* has to be 'alive' while the label exists.
* @param obj pointer to a label object
* @param text pointer to a text. NULL to refresh with the current text.
*/
void lv_label_set_text_static(lv_obj_t * obj, const char * text);
/**
* Set the behavior of the label with longer text then the object size
* @param obj pointer to a label object
* @param long_mode the new mode from 'lv_label_long_mode' enum.
* In LV_LONG_WRAP/DOT/SCROLL/SCROLL_CIRC the size of the label should be set AFTER this function
*/
void lv_label_set_long_mode(lv_obj_t * obj, lv_label_long_mode_t long_mode);
/**
* Enable the recoloring by in-line commands
* @param obj pointer to a label object
* @param en true: enable recoloring, false: disable
* @example "This is a #ff0000 red# word"
*/
void lv_label_set_recolor(lv_obj_t * obj, bool en);
/**
* Set where text selection should start
* @param obj pointer to a label object
* @param index character index from where selection should start. `LV_LABEL_TEXT_SELECTION_OFF` for no selection
*/
void lv_label_set_text_sel_start(lv_obj_t * obj, uint32_t index);
/**
* Set where text selection should end
* @param obj pointer to a label object
* @param index character index where selection should end. `LV_LABEL_TEXT_SELECTION_OFF` for no selection
*/
void lv_label_set_text_sel_end(lv_obj_t * obj, uint32_t index);
/*=====================
* Getter functions
*====================*/
/**
* Get the text of a label
* @param obj pointer to a label object
* @return the text of the label
*/
char * lv_label_get_text(const lv_obj_t * obj);
/**
* Get the long mode of a label
* @param obj pointer to a label object
* @return the current long mode
*/
lv_label_long_mode_t lv_label_get_long_mode(const lv_obj_t * obj);
/**
* Get the recoloring attribute
* @param obj pointer to a label object
* @return true: recoloring is enabled, false: disable
*/
bool lv_label_get_recolor(const lv_obj_t * obj);
/**
* Get the relative x and y coordinates of a letter
* @param obj pointer to a label object
* @param index index of the character [0 ... text length - 1].
* Expressed in character index, not byte index (different in UTF-8)
* @param pos store the result here (E.g. index = 0 gives 0;0 coordinates if the text if aligned to the left)
*/
void lv_label_get_letter_pos(const lv_obj_t * obj, uint32_t char_id, lv_point_t * pos);
/**
* Get the index of letter on a relative point of a label.
* @param obj pointer to label object
* @param pos pointer to point with coordinates on a the label
* @return The index of the letter on the 'pos_p' point (E.g. on 0;0 is the 0. letter if aligned to the left)
* Expressed in character index and not byte index (different in UTF-8)
*/
uint32_t lv_label_get_letter_on(const lv_obj_t * obj, lv_point_t * pos_in);
/**
* Check if a character is drawn under a point.
* @param obj pointer to a label object
* @param pos Point to check for character under
* @return whether a character is drawn under the point
*/
bool lv_label_is_char_under_pos(const lv_obj_t * obj, lv_point_t * pos);
/**
* @brief Get the selection start index.
* @param obj pointer to a label object.
* @return selection start index. `LV_LABEL_TEXT_SELECTION_OFF` if nothing is selected.
*/
uint32_t lv_label_get_text_selection_start(const lv_obj_t * obj);
/**
* @brief Get the selection end index.
* @param obj pointer to a label object.
* @return selection end index. `LV_LABEL_TXT_SEL_OFF` if nothing is selected.
*/
uint32_t lv_label_get_text_selection_end(const lv_obj_t * obj);
/*=====================
* Other functions
*====================*/
/**
* Insert a text to a label. The label text can not be static.
* @param obj pointer to a label object
* @param pos character index to insert. Expressed in character index and not byte index.
* 0: before first char. LV_LABEL_POS_LAST: after last char.
* @param txt pointer to the text to insert
*/
void lv_label_ins_text(lv_obj_t * obj, uint32_t pos, const char * txt);
/**
* Delete characters from a label. The label text can not be static.
* @param obj pointer to a label object
* @param pos character index from where to cut. Expressed in character index and not byte index.
* 0: start in from of the first character
* @param cnt number of characters to cut
*/
void lv_label_cut_text(lv_obj_t * obj, uint32_t pos, uint32_t cnt);
/**********************
* MACROS
**********************/
#endif /*LV_USE_LABEL*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_LABEL_H*/

View File

@@ -0,0 +1,201 @@
/**
* @file lv_line.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_line.h"
#if LV_USE_LINE != 0
#include "../misc/lv_assert.h"
#include "../draw/lv_draw.h"
#include "../misc/lv_math.h"
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
/*********************
* DEFINES
*********************/
#define MY_CLASS &lv_line_class
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_line_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_line_event(const lv_obj_class_t * class_p, lv_event_t * e);
/**********************
* STATIC VARIABLES
**********************/
const lv_obj_class_t lv_line_class = {
.constructor_cb = lv_line_constructor,
.event_cb = lv_line_event,
.width_def = LV_SIZE_CONTENT,
.height_def = LV_SIZE_CONTENT,
.instance_size = sizeof(lv_line_t),
.base_class = &lv_obj_class
};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_obj_t * lv_line_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_line_set_points(lv_obj_t * obj, const lv_point_t points[], uint16_t point_num)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_line_t * line = (lv_line_t *)obj;
line->point_array = points;
line->point_num = point_num;
lv_obj_refresh_self_size(obj);
lv_obj_invalidate(obj);
}
void lv_line_set_y_invert(lv_obj_t * obj, bool en)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_line_t * line = (lv_line_t *)obj;
if(line->y_inv == en) return;
line->y_inv = en ? 1U : 0U;
lv_obj_invalidate(obj);
}
/*=====================
* Getter functions
*====================*/
bool lv_line_get_y_invert(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_line_t * line = (lv_line_t *)obj;
return line->y_inv == 1U;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_line_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
LV_TRACE_OBJ_CREATE("begin");
lv_line_t * line = (lv_line_t *)obj;
line->point_num = 0;
line->point_array = NULL;
line->y_inv = 0;
lv_obj_clear_flag(obj, LV_OBJ_FLAG_CLICKABLE);
LV_TRACE_OBJ_CREATE("finished");
}
static void lv_line_event(const lv_obj_class_t * class_p, lv_event_t * e)
{
LV_UNUSED(class_p);
lv_res_t res;
/*Call the ancestor's event handler*/
res = lv_obj_event_base(MY_CLASS, e);
if(res != LV_RES_OK) return;
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * obj = lv_event_get_target(e);
if(code == LV_EVENT_REFR_EXT_DRAW_SIZE) {
/*The corner of the skew lines is out of the intended area*/
lv_coord_t line_width = lv_obj_get_style_line_width(obj, LV_PART_MAIN);
lv_coord_t * s = lv_event_get_param(e);
if(*s < line_width) *s = line_width;
}
else if(code == LV_EVENT_GET_SELF_SIZE) {
lv_line_t * line = (lv_line_t *)obj;
if(line->point_num == 0 || line->point_array == NULL) return;
lv_point_t * p = lv_event_get_param(e);
lv_coord_t w = 0;
lv_coord_t h = 0;
uint16_t i;
for(i = 0; i < line->point_num; i++) {
w = LV_MAX(line->point_array[i].x, w);
h = LV_MAX(line->point_array[i].y, h);
}
lv_coord_t line_width = lv_obj_get_style_line_width(obj, LV_PART_MAIN);
w += line_width;
h += line_width;
p->x = w;
p->y = h;
}
else if(code == LV_EVENT_DRAW_MAIN) {
lv_line_t * line = (lv_line_t *)obj;
lv_draw_ctx_t * draw_ctx = lv_event_get_draw_ctx(e);
if(line->point_num == 0 || line->point_array == NULL) return;
lv_area_t area;
lv_obj_get_coords(obj, &area);
lv_coord_t x_ofs = area.x1 - lv_obj_get_scroll_x(obj);
lv_coord_t y_ofs = area.y1 - lv_obj_get_scroll_y(obj);
lv_coord_t h = lv_obj_get_height(obj);
lv_draw_line_dsc_t line_dsc;
lv_draw_line_dsc_init(&line_dsc);
lv_obj_init_draw_line_dsc(obj, LV_PART_MAIN, &line_dsc);
/*Read all points and draw the lines*/
uint16_t i;
for(i = 0; i < line->point_num - 1; i++) {
lv_point_t p1;
lv_point_t p2;
p1.x = line->point_array[i].x + x_ofs;
p2.x = line->point_array[i + 1].x + x_ofs;
if(line->y_inv == 0) {
p1.y = line->point_array[i].y + y_ofs;
p2.y = line->point_array[i + 1].y + y_ofs;
}
else {
p1.y = h - line->point_array[i].y + y_ofs;
p2.y = h - line->point_array[i + 1].y + y_ofs;
}
lv_draw_line(draw_ctx, &line_dsc, &p1, &p2);
line_dsc.round_start = 0; /*Draw the rounding only on the end points after the first line*/
}
}
}
#endif

View File

@@ -0,0 +1,93 @@
/**
* @file lv_line.h
*
*/
#ifndef LV_LINE_H
#define LV_LINE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../lv_conf_internal.h"
#if LV_USE_LINE != 0
#include "../core/lv_obj.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/*Data of line*/
typedef struct {
lv_obj_t obj;
const lv_point_t * point_array; /**< Pointer to an array with the points of the line*/
uint16_t point_num; /**< Number of points in 'point_array'*/
uint8_t y_inv : 1; /**< 1: y == 0 will be on the bottom*/
} lv_line_t;
extern const lv_obj_class_t lv_line_class;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a line object
* @param parent pointer to an object, it will be the parent of the new line
* @return pointer to the created line
*/
lv_obj_t * lv_line_create(lv_obj_t * parent);
/*=====================
* Setter functions
*====================*/
/**
* Set an array of points. The line object will connect these points.
* @param obj pointer to a line object
* @param points an array of points. Only the address is saved, so the array needs to be alive while the line exists
* @param point_num number of points in 'point_a'
*/
void lv_line_set_points(lv_obj_t * obj, const lv_point_t points[], uint16_t point_num);
/**
* Enable (or disable) the y coordinate inversion.
* If enabled then y will be subtracted from the height of the object,
* therefore the y = 0 coordinate will be on the bottom.
* @param obj pointer to a line object
* @param en true: enable the y inversion, false:disable the y inversion
*/
void lv_line_set_y_invert(lv_obj_t * obj, bool en);
/*=====================
* Getter functions
*====================*/
/**
* Get the y inversion attribute
* @param obj pointer to a line object
* @return true: y inversion is enabled, false: disabled
*/
bool lv_line_get_y_invert(const lv_obj_t * obj);
/**********************
* MACROS
**********************/
#endif /*LV_USE_LINE*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_LINE_H*/

View File

@@ -0,0 +1,140 @@
/**
* @file lv_templ.c
*
*/
/**
* TODO Remove these instructions
* Search and replace: templ -> object short name with lower case(e.g. btn, label etc)
* TEMPL -> object short name with upper case (e.g. BTN, LABEL etc.)
*
* You can remove the defined() clause from the #if statement below. This exists because
* LV_USE_TEMPL is not in lv_conf.h or lv_conf_template.h by default.
*/
/*********************
* INCLUDES
*********************/
//#include "lv_templ.h" /*TODO uncomment this*/
#if defined(LV_USE_TEMPL) && LV_USE_TEMPL != 0
/*********************
* DEFINES
*********************/
#define MY_CLASS &lv_templ_class
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_templ_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_templ_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_templ_event(const lv_obj_class_t * class_p, lv_event_t * e);
/**********************
* STATIC VARIABLES
**********************/
const lv_obj_class_t lv_templ_class = {
.constructor_cb = lv_templ_constructor,
.destructor_cb = lv_templ_destructor,
.event_cb = lv_templ_event,
.width_def = LV_DPI_DEF,
.height_def = LV_DPI_DEF,
.instance_size = sizeof(lv_templ_t),
.group_def = LV_OBJ_CLASS_GROUP_DEF_INHERIT,
.editable = LV_OBJ_CLASS_EDITABLE_INHERIT,
.base_class = &lv_templ_class
};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_obj_t * lv_templ_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;
}
/*======================
* Add/remove functions
*=====================*/
/*
* New object specific "add" or "remove" functions come here
*/
/*=====================
* Setter functions
*====================*/
/*
* New object specific "set" functions come here
*/
/*=====================
* Getter functions
*====================*/
/*
* New object specific "get" functions come here
*/
/*=====================
* Other functions
*====================*/
/*
* New object specific "other" functions come here
*/
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_templ_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
LV_TRACE_OBJ_CREATE("begin");
lv_templ_t * templ = (lv_templ_t *)obj;
/*Initialize the widget's data*/
LV_TRACE_OBJ_CREATE("finished");
}
static void lv_templ_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
lv_templ_t * templ = (lv_templ_t *)obj;
/*Free the widget specific data*/
}
static void lv_templ_event(const lv_obj_class_t * class_p, lv_event_t * e)
{
LV_UNUSED(class_p);
lv_res_t res;
/*Call the ancestor's event handler*/
res = lv_obj_event_base(MY_CLASS, e);
if(res != LV_RES_OK) return;
/*Add the widget specific event handling here*/
}
#else /*Enable this file at the top*/
/*This dummy typedef exists purely to silence -Wpedantic.*/
typedef int keep_pedantic_happy;
#endif

View File

@@ -0,0 +1,81 @@
/**
* @file lv_templ.h
*
*/
/**
* TODO Remove these instructions
* Search and replace: templ -> object short name with lower case(e.g. btn, label etc)
* TEMPL -> object short name with upper case (e.g. BTN, LABEL etc.)
*
*/
#ifndef LV_TEMPL_H
#define LV_TEMPL_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../lv_conf_internal.h"
#if LV_USE_TEMPL != 0
#include "../core/lv_obj.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/*Data of template*/
typedef struct {
lv_ANCESTOR_t ancestor; /*The ancestor widget, e.g. lv_slider_t slider*/
/*New data for this type*/
} lv_templ_t;
extern const lv_obj_class_t lv_templ_class;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a templ object
* @param parent pointer to an object, it will be the parent of the new templ
* @return pointer to the created bar
*/
lv_obj_t * lv_templ_create(lv_obj_t * parent);
/*======================
* Add/remove functions
*=====================*/
/*=====================
* Setter functions
*====================*/
/*=====================
* Getter functions
*====================*/
/*=====================
* Other functions
*====================*/
/**********************
* MACROS
**********************/
#endif /*LV_USE_TEMPL*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_TEMPL_H*/

View File

@@ -0,0 +1,786 @@
/**
* @file lv_roller.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_roller.h"
#if LV_USE_ROLLER != 0
#include "../misc/lv_assert.h"
#include "../draw/lv_draw.h"
#include "../core/lv_group.h"
#include "../core/lv_indev.h"
#include "../core/lv_indev_scroll.h"
/*********************
* DEFINES
*********************/
#define MY_CLASS &lv_roller_class
#define MY_CLASS_LABEL &lv_roller_label_class
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_roller_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_roller_event(const lv_obj_class_t * class_p, lv_event_t * e);
static void lv_roller_label_event(const lv_obj_class_t * class_p, lv_event_t * e);
static void draw_main(lv_event_t * e);
static void draw_label(lv_event_t * e);
static void get_sel_area(lv_obj_t * obj, lv_area_t * sel_area);
static void refr_position(lv_obj_t * obj, lv_anim_enable_t animen);
static lv_res_t release_handler(lv_obj_t * obj);
static void inf_normalize(lv_obj_t * obj_scrl);
static lv_obj_t * get_label(const lv_obj_t * obj);
static lv_coord_t get_selected_label_width(const lv_obj_t * obj);
static void scroll_anim_ready_cb(lv_anim_t * a);
static void set_y_anim(void * obj, int32_t v);
/**********************
* STATIC VARIABLES
**********************/
const lv_obj_class_t lv_roller_class = {
.constructor_cb = lv_roller_constructor,
.event_cb = lv_roller_event,
.width_def = LV_SIZE_CONTENT,
.height_def = LV_DPI_DEF,
.instance_size = sizeof(lv_roller_t),
.editable = LV_OBJ_CLASS_EDITABLE_TRUE,
.group_def = LV_OBJ_CLASS_GROUP_DEF_TRUE,
.base_class = &lv_obj_class
};
const lv_obj_class_t lv_roller_label_class = {
.event_cb = lv_roller_label_event,
.instance_size = sizeof(lv_label_t),
.base_class = &lv_label_class
};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Create a roller object
* @param parent pointer to an object, it will be the parent of the new roller
* @return pointer to the created roller
*/
lv_obj_t * lv_roller_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
*====================*/
/**
* Set the options on a roller
* @param roller pointer to roller object
* @param options a string with '\n' separated options. E.g. "One\nTwo\nThree"
* @param mode `LV_ROLLER_MODE_NORMAL` or `LV_ROLLER_MODE_INFINITE`
*/
void lv_roller_set_options(lv_obj_t * obj, const char * options, lv_roller_mode_t mode)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
LV_ASSERT_NULL(options);
lv_roller_t * roller = (lv_roller_t *)obj;
lv_obj_t * label = get_label(obj);
roller->sel_opt_id = 0;
roller->sel_opt_id_ori = 0;
/*Count the '\n'-s to determine the number of options*/
roller->option_cnt = 0;
uint32_t cnt;
for(cnt = 0; options[cnt] != '\0'; cnt++) {
if(options[cnt] == '\n') roller->option_cnt++;
}
roller->option_cnt++; /*Last option has no `\n`*/
if(mode == LV_ROLLER_MODE_NORMAL) {
roller->mode = LV_ROLLER_MODE_NORMAL;
lv_label_set_text(label, options);
}
else {
roller->mode = LV_ROLLER_MODE_INFINITE;
size_t opt_len = strlen(options) + 1; /*+1 to add '\n' after option lists*/
char * opt_extra = lv_mem_buf_get(opt_len * LV_ROLLER_INF_PAGES);
uint8_t i;
for(i = 0; i < LV_ROLLER_INF_PAGES; i++) {
strcpy(&opt_extra[opt_len * i], options);
opt_extra[opt_len * (i + 1) - 1] = '\n';
}
opt_extra[opt_len * LV_ROLLER_INF_PAGES - 1] = '\0';
lv_label_set_text(label, opt_extra);
lv_mem_buf_release(opt_extra);
roller->sel_opt_id = ((LV_ROLLER_INF_PAGES / 2) + 0) * roller->option_cnt;
roller->option_cnt = roller->option_cnt * LV_ROLLER_INF_PAGES;
inf_normalize(obj);
}
roller->sel_opt_id_ori = roller->sel_opt_id;
/*If the selected text has larger font the label needs some extra draw padding to draw it.*/
lv_obj_refresh_ext_draw_size(label);
}
/**
* Set the selected option
* @param roller pointer to a roller object
* @param sel_opt id of the selected option (0 ... number of option - 1);
* @param anim_en LV_ANIM_ON: set with animation; LV_ANOM_OFF set immediately
*/
void lv_roller_set_selected(lv_obj_t * obj, uint16_t sel_opt, lv_anim_enable_t anim)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
/*Set the value even if it's the same as the current value because
*if moving to the next option with an animation which was just deleted in the PRESS Call the ancestor's event handler
*nothing will continue the animation.*/
lv_roller_t * roller = (lv_roller_t *)obj;
/*In infinite mode interpret the new ID relative to the currently visible "page"*/
if(roller->mode == LV_ROLLER_MODE_INFINITE) {
uint32_t real_option_cnt = roller->option_cnt / LV_ROLLER_INF_PAGES;
uint16_t current_page = roller->sel_opt_id / real_option_cnt;
/*Set by the user to e.g. 0, 1, 2, 3...
*Upscale the value to the current page*/
if(sel_opt < real_option_cnt) {
uint16_t act_opt = roller->sel_opt_id - current_page * real_option_cnt;
int32_t sel_opt_signed = sel_opt;
/*Huge jump? Probably from last to first or first to last option.*/
if(LV_ABS((int16_t)act_opt - sel_opt) > real_option_cnt / 2) {
if(act_opt > sel_opt) sel_opt_signed += real_option_cnt;
else sel_opt_signed -= real_option_cnt;
}
sel_opt = sel_opt_signed + real_option_cnt * current_page;
}
}
roller->sel_opt_id = sel_opt < roller->option_cnt ? sel_opt : roller->option_cnt - 1;
roller->sel_opt_id_ori = roller->sel_opt_id;
refr_position(obj, anim);
}
/**
* Set the height to show the given number of rows (options)
* @param roller pointer to a roller object
* @param row_cnt number of desired visible rows
*/
void lv_roller_set_visible_row_count(lv_obj_t * obj, uint8_t row_cnt)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
const lv_font_t * font = lv_obj_get_style_text_font(obj, LV_PART_MAIN);
lv_coord_t line_space = lv_obj_get_style_text_line_space(obj, LV_PART_MAIN);
lv_coord_t border_width = lv_obj_get_style_border_width(obj, LV_PART_MAIN);
lv_obj_set_height(obj, (lv_font_get_line_height(font) + line_space) * row_cnt + 2 * border_width);
}
/*=====================
* Getter functions
*====================*/
/**
* Get the id of the selected option
* @param roller pointer to a roller object
* @return id of the selected option (0 ... number of option - 1);
*/
uint16_t lv_roller_get_selected(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_roller_t * roller = (lv_roller_t *)obj;
if(roller->mode == LV_ROLLER_MODE_INFINITE) {
uint16_t real_id_cnt = roller->option_cnt / LV_ROLLER_INF_PAGES;
return roller->sel_opt_id % real_id_cnt;
}
else {
return roller->sel_opt_id;
}
}
/**
* Get the current selected option as a string
* @param ddlist pointer to ddlist object
* @param buf pointer to an array to store the string
* @param buf_size size of `buf` in bytes. 0: to ignore it.
*/
void lv_roller_get_selected_str(const lv_obj_t * obj, char * buf, uint32_t buf_size)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_roller_t * roller = (lv_roller_t *)obj;
lv_obj_t * label = get_label(obj);
uint32_t i;
uint16_t line = 0;
const char * opt_txt = lv_label_get_text(label);
size_t txt_len = strlen(opt_txt);
for(i = 0; i < txt_len && line != roller->sel_opt_id; i++) {
if(opt_txt[i] == '\n') line++;
}
uint32_t c;
for(c = 0; i < txt_len && opt_txt[i] != '\n'; c++, i++) {
if(buf_size && c >= buf_size - 1) {
LV_LOG_WARN("lv_dropdown_get_selected_str: the buffer was too small");
break;
}
buf[c] = opt_txt[i];
}
buf[c] = '\0';
}
/**
* Get the options of a roller
* @param roller pointer to roller object
* @return the options separated by '\n'-s (E.g. "Option1\nOption2\nOption3")
*/
const char * lv_roller_get_options(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
return lv_label_get_text(get_label(obj));
}
/**
* Get the total number of options
* @param roller pointer to a roller object
* @return the total number of options
*/
uint16_t lv_roller_get_option_cnt(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_roller_t * roller = (lv_roller_t *)obj;
if(roller->mode == LV_ROLLER_MODE_INFINITE) {
return roller->option_cnt / LV_ROLLER_INF_PAGES;
}
else {
return roller->option_cnt;
}
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_roller_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
lv_roller_t * roller = (lv_roller_t *)obj;
roller->mode = LV_ROLLER_MODE_NORMAL;
roller->option_cnt = 0;
roller->sel_opt_id = 0;
roller->sel_opt_id_ori = 0;
lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLL_CHAIN_VER);
LV_LOG_INFO("begin");
lv_obj_t * label = lv_obj_class_create_obj(&lv_roller_label_class, obj);
lv_obj_class_init_obj(label);
lv_roller_set_options(obj, "Option 1\nOption 2\nOption 3\nOption 4\nOption 5", LV_ROLLER_MODE_NORMAL);
LV_LOG_TRACE("finshed");
}
static void lv_roller_event(const lv_obj_class_t * class_p, lv_event_t * e)
{
LV_UNUSED(class_p);
lv_res_t res;
/*Call the ancestor's event handler*/
res = lv_obj_event_base(MY_CLASS, e);
if(res != LV_RES_OK) return;
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * obj = lv_event_get_target(e);
lv_roller_t * roller = (lv_roller_t *)obj;
if(code == LV_EVENT_GET_SELF_SIZE) {
lv_point_t * p = lv_event_get_param(e);
p->x = get_selected_label_width(obj);
}
else if(code == LV_EVENT_STYLE_CHANGED) {
lv_obj_t * label = get_label(obj);
/*Be sure the label's style is updated before processing the roller*/
if(label) lv_event_send(label, LV_EVENT_STYLE_CHANGED, NULL);
lv_obj_refresh_self_size(obj);
refr_position(obj, LV_ANIM_OFF);
}
else if(code == LV_EVENT_SIZE_CHANGED) {
refr_position(obj, LV_ANIM_OFF);
}
else if(code == LV_EVENT_PRESSED) {
roller->moved = 0;
lv_anim_del(get_label(obj), set_y_anim);
}
else if(code == LV_EVENT_PRESSING) {
lv_indev_t * indev = lv_indev_get_act();
lv_point_t p;
lv_indev_get_vect(indev, &p);
if(p.y) {
lv_obj_t * label = get_label(obj);
lv_obj_set_y(label, lv_obj_get_y(label) + p.y);
roller->moved = 1;
}
}
else if(code == LV_EVENT_RELEASED || code == LV_EVENT_PRESS_LOST) {
release_handler(obj);
}
else if(code == LV_EVENT_FOCUSED) {
lv_group_t * g = lv_obj_get_group(obj);
bool editing = lv_group_get_editing(g);
lv_indev_type_t indev_type = lv_indev_get_type(lv_indev_get_act());
/*Encoders need special handling*/
if(indev_type == LV_INDEV_TYPE_ENCODER) {
/*In navigate mode revert the original value*/
if(!editing) {
if(roller->sel_opt_id != roller->sel_opt_id_ori) {
roller->sel_opt_id = roller->sel_opt_id_ori;
refr_position(obj, LV_ANIM_ON);
}
}
/*Save the current state when entered to edit mode*/
else {
roller->sel_opt_id_ori = roller->sel_opt_id;
}
}
else {
roller->sel_opt_id_ori = roller->sel_opt_id; /*Save the current value. Used to revert this state if
ENTER won't be pressed*/
}
}
else if(code == LV_EVENT_DEFOCUSED) {
/*Revert the original state*/
if(roller->sel_opt_id != roller->sel_opt_id_ori) {
roller->sel_opt_id = roller->sel_opt_id_ori;
refr_position(obj, LV_ANIM_ON);
}
}
else if(code == LV_EVENT_KEY) {
char c = *((char *)lv_event_get_param(e));
if(c == LV_KEY_RIGHT || c == LV_KEY_DOWN) {
if(roller->sel_opt_id + 1 < roller->option_cnt) {
uint16_t ori_id = roller->sel_opt_id_ori; /*lv_roller_set_selected will overwrite this*/
lv_roller_set_selected(obj, roller->sel_opt_id + 1, LV_ANIM_ON);
roller->sel_opt_id_ori = ori_id;
}
}
else if(c == LV_KEY_LEFT || c == LV_KEY_UP) {
if(roller->sel_opt_id > 0) {
uint16_t ori_id = roller->sel_opt_id_ori; /*lv_roller_set_selected will overwrite this*/
lv_roller_set_selected(obj, roller->sel_opt_id - 1, LV_ANIM_ON);
roller->sel_opt_id_ori = ori_id;
}
}
}
else if(code == LV_EVENT_REFR_EXT_DRAW_SIZE) {
lv_obj_t * label = get_label(obj);
lv_obj_refresh_ext_draw_size(label);
}
else if(code == LV_EVENT_DRAW_MAIN || code == LV_EVENT_DRAW_POST) {
draw_main(e);
}
}
static void lv_roller_label_event(const lv_obj_class_t * class_p, lv_event_t * e)
{
LV_UNUSED(class_p);
lv_res_t res;
lv_event_code_t code = lv_event_get_code(e);
/*LV_EVENT_DRAW_MAIN will be called in the draw function*/
if(code != LV_EVENT_DRAW_MAIN) {
/* Call the ancestor's event handler */
res = lv_obj_event_base(MY_CLASS_LABEL, e);
if(res != LV_RES_OK) return;
}
lv_obj_t * label = lv_event_get_target(e);
if(code == LV_EVENT_REFR_EXT_DRAW_SIZE) {
/*If the selected text has a larger font it needs some extra space to draw it*/
lv_coord_t * s = lv_event_get_param(e);
lv_obj_t * obj = lv_obj_get_parent(label);
lv_coord_t sel_w = get_selected_label_width(obj);
lv_coord_t label_w = lv_obj_get_width(label);
*s = LV_MAX(*s, sel_w - label_w);
}
else if(code == LV_EVENT_SIZE_CHANGED) {
refr_position(lv_obj_get_parent(label), LV_ANIM_OFF);
}
else if(code == LV_EVENT_DRAW_MAIN) {
draw_label(e);
}
}
static void draw_main(lv_event_t * e)
{
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * obj = lv_event_get_target(e);
if(code == LV_EVENT_DRAW_MAIN) {
/*Draw the selected rectangle*/
lv_draw_ctx_t * draw_ctx = lv_event_get_draw_ctx(e);
lv_area_t sel_area;
get_sel_area(obj, &sel_area);
lv_draw_rect_dsc_t sel_dsc;
lv_draw_rect_dsc_init(&sel_dsc);
lv_obj_init_draw_rect_dsc(obj, LV_PART_SELECTED, &sel_dsc);
lv_draw_rect(draw_ctx, &sel_dsc, &sel_area);
}
/*Post draw when the children are drawn*/
else if(code == LV_EVENT_DRAW_POST) {
lv_draw_ctx_t * draw_ctx = lv_event_get_draw_ctx(e);
lv_draw_label_dsc_t label_dsc;
lv_draw_label_dsc_init(&label_dsc);
lv_obj_init_draw_label_dsc(obj, LV_PART_SELECTED, &label_dsc);
/*Redraw the text on the selected area*/
lv_area_t sel_area;
get_sel_area(obj, &sel_area);
lv_area_t mask_sel;
bool area_ok;
area_ok = _lv_area_intersect(&mask_sel, draw_ctx->clip_area, &sel_area);
if(area_ok) {
lv_obj_t * label = get_label(obj);
/*Get the size of the "selected text"*/
lv_point_t res_p;
lv_txt_get_size(&res_p, lv_label_get_text(label), label_dsc.font, label_dsc.letter_space, label_dsc.line_space,
lv_obj_get_width(obj), LV_TEXT_FLAG_EXPAND);
/*Move the selected label proportionally with the background label*/
lv_coord_t roller_h = lv_obj_get_height(obj);
int32_t label_y_prop = label->coords.y1 - (roller_h / 2 +
obj->coords.y1); /*label offset from the middle line of the roller*/
label_y_prop = (label_y_prop * 16384) / lv_obj_get_height(
label); /*Proportional position from the middle line (upscaled by << 14)*/
/*Apply a correction with different line heights*/
const lv_font_t * normal_label_font = lv_obj_get_style_text_font(obj, LV_PART_MAIN);
lv_coord_t corr = (label_dsc.font->line_height - normal_label_font->line_height) / 2;
/*Apply the proportional position to the selected text*/
res_p.y -= corr;
int32_t label_sel_y = roller_h / 2 + obj->coords.y1;
label_sel_y += (label_y_prop * res_p.y) >> 14;
label_sel_y -= corr;
lv_coord_t bwidth = lv_obj_get_style_border_width(obj, LV_PART_MAIN);
lv_coord_t pleft = lv_obj_get_style_pad_left(obj, LV_PART_MAIN);
lv_coord_t pright = lv_obj_get_style_pad_right(obj, LV_PART_MAIN);
/*Draw the selected text*/
lv_area_t label_sel_area;
label_sel_area.x1 = obj->coords.x1 + pleft + bwidth;
label_sel_area.y1 = label_sel_y;
label_sel_area.x2 = obj->coords.x2 - pright - bwidth;
label_sel_area.y2 = label_sel_area.y1 + res_p.y;
label_dsc.flag |= LV_TEXT_FLAG_EXPAND;
const lv_area_t * clip_area_ori = draw_ctx->clip_area;
draw_ctx->clip_area = &mask_sel;
lv_draw_label(draw_ctx, &label_dsc, &label_sel_area, lv_label_get_text(label), NULL);
draw_ctx->clip_area = clip_area_ori;
}
}
}
static void draw_label(lv_event_t * e)
{
/* Split the drawing of the label into an upper (above the selected area)
* and a lower (below the selected area)*/
lv_obj_t * label_obj = lv_event_get_target(e);
lv_obj_t * roller = lv_obj_get_parent(label_obj);
lv_draw_label_dsc_t label_draw_dsc;
lv_draw_label_dsc_init(&label_draw_dsc);
lv_obj_init_draw_label_dsc(roller, LV_PART_MAIN, &label_draw_dsc);
lv_draw_ctx_t * draw_ctx = lv_event_get_draw_ctx(e);
/*If the roller has shadow or outline it has some ext. draw size
*therefore the label can overflow the roller's boundaries.
*To solve this limit the clip area to the "plain" roller.*/
const lv_area_t * clip_area_ori = draw_ctx->clip_area;
lv_area_t roller_clip_area;
if(!_lv_area_intersect(&roller_clip_area, draw_ctx->clip_area, &roller->coords)) return;
draw_ctx->clip_area = &roller_clip_area;
lv_area_t sel_area;
get_sel_area(roller, &sel_area);
lv_area_t clip2;
clip2.x1 = label_obj->coords.x1;
clip2.y1 = label_obj->coords.y1;
clip2.x2 = label_obj->coords.x2;
clip2.y2 = sel_area.y1;
if(_lv_area_intersect(&clip2, draw_ctx->clip_area, &clip2)) {
const lv_area_t * clip_area_ori2 = draw_ctx->clip_area;
draw_ctx->clip_area = &clip2;
lv_draw_label(draw_ctx, &label_draw_dsc, &label_obj->coords, lv_label_get_text(label_obj), NULL);
draw_ctx->clip_area = clip_area_ori2;
}
clip2.x1 = label_obj->coords.x1;
clip2.y1 = sel_area.y2;
clip2.x2 = label_obj->coords.x2;
clip2.y2 = label_obj->coords.y2;
if(_lv_area_intersect(&clip2, draw_ctx->clip_area, &clip2)) {
const lv_area_t * clip_area_ori2 = draw_ctx->clip_area;
draw_ctx->clip_area = &clip2;
lv_draw_label(draw_ctx, &label_draw_dsc, &label_obj->coords, lv_label_get_text(label_obj), NULL);
draw_ctx->clip_area = clip_area_ori2;
}
draw_ctx->clip_area = clip_area_ori;
}
static void get_sel_area(lv_obj_t * obj, lv_area_t * sel_area)
{
const lv_font_t * font_main = lv_obj_get_style_text_font(obj, LV_PART_MAIN);
const lv_font_t * font_sel = lv_obj_get_style_text_font(obj, LV_PART_SELECTED);
lv_coord_t font_main_h = lv_font_get_line_height(font_main);
lv_coord_t font_sel_h = lv_font_get_line_height(font_sel);
lv_coord_t line_space = lv_obj_get_style_text_line_space(obj, LV_PART_MAIN);
lv_coord_t d = (font_sel_h + font_main_h) / 2 + line_space;
sel_area->y1 = obj->coords.y1 + lv_obj_get_height(obj) / 2 - d / 2;
sel_area->y2 = sel_area->y1 + d;
lv_area_t roller_coords;
lv_obj_get_coords(obj, &roller_coords);
sel_area->x1 = roller_coords.x1;
sel_area->x2 = roller_coords.x2;
}
/**
* Refresh the position of the roller. It uses the id stored in: roller->ddlist.selected_option_id
* @param roller pointer to a roller object
* @param anim_en LV_ANIM_ON: refresh with animation; LV_ANOM_OFF: without animation
*/
static void refr_position(lv_obj_t * obj, lv_anim_enable_t anim_en)
{
lv_obj_t * label = get_label(obj);
if(label == NULL) return;
lv_text_align_t align = lv_obj_calculate_style_text_align(label, LV_PART_MAIN, lv_label_get_text(label));
switch(align) {
case LV_TEXT_ALIGN_CENTER:
lv_obj_set_x(label, (lv_obj_get_content_width(obj) - lv_obj_get_width(label)) / 2);
break;
case LV_TEXT_ALIGN_RIGHT:
lv_obj_set_x(label, lv_obj_get_content_width(obj) - lv_obj_get_width(label));
break;
case LV_TEXT_ALIGN_LEFT:
lv_obj_set_x(label, 0);
break;
}
lv_roller_t * roller = (lv_roller_t *)obj;
const lv_font_t * font = lv_obj_get_style_text_font(obj, LV_PART_MAIN);
lv_coord_t line_space = lv_obj_get_style_text_line_space(obj, LV_PART_MAIN);
lv_coord_t font_h = lv_font_get_line_height(font);
lv_coord_t h = lv_obj_get_content_height(obj);
uint16_t anim_time = lv_obj_get_style_anim_time(obj, LV_PART_MAIN);
/*Normally the animation's `end_cb` sets correct position of the roller if infinite.
*But without animations do it manually*/
if(anim_en == LV_ANIM_OFF || anim_time == 0) {
inf_normalize(obj);
}
int32_t id = roller->sel_opt_id;
lv_coord_t sel_y1 = id * (font_h + line_space);
lv_coord_t mid_y1 = h / 2 - font_h / 2;
lv_coord_t new_y = mid_y1 - sel_y1;
if(anim_en == LV_ANIM_OFF || anim_time == 0) {
lv_anim_del(label, set_y_anim);
lv_obj_set_y(label, new_y);
}
else {
lv_anim_t a;
lv_anim_init(&a);
lv_anim_set_var(&a, label);
lv_anim_set_exec_cb(&a, set_y_anim);
lv_anim_set_values(&a, lv_obj_get_y(label), new_y);
lv_anim_set_time(&a, anim_time);
lv_anim_set_ready_cb(&a, scroll_anim_ready_cb);
lv_anim_set_path_cb(&a, lv_anim_path_ease_out);
lv_anim_start(&a);
}
}
static lv_res_t release_handler(lv_obj_t * obj)
{
lv_obj_t * label = get_label(obj);
if(label == NULL) return LV_RES_OK;
lv_indev_t * indev = lv_indev_get_act();
lv_roller_t * roller = (lv_roller_t *)obj;
/*Leave edit mode once a new option is selected*/
lv_indev_type_t indev_type = lv_indev_get_type(indev);
if(indev_type == LV_INDEV_TYPE_ENCODER || indev_type == LV_INDEV_TYPE_KEYPAD) {
roller->sel_opt_id_ori = roller->sel_opt_id;
if(indev_type == LV_INDEV_TYPE_ENCODER) {
lv_group_t * g = lv_obj_get_group(obj);
if(lv_group_get_editing(g)) {
lv_group_set_editing(g, false);
}
}
}
if(lv_indev_get_type(indev) == LV_INDEV_TYPE_POINTER || lv_indev_get_type(indev) == LV_INDEV_TYPE_BUTTON) {
/*Search the clicked option (For KEYPAD and ENCODER the new value should be already set)*/
int16_t new_opt = -1;
if(roller->moved == 0) {
new_opt = 0;
lv_point_t p;
lv_indev_get_point(indev, &p);
p.y -= label->coords.y1;
p.x -= label->coords.x1;
uint32_t letter_i;
letter_i = lv_label_get_letter_on(label, &p);
const char * txt = lv_label_get_text(label);
uint32_t i = 0;
uint32_t i_prev = 0;
uint32_t letter_cnt = 0;
for(letter_cnt = 0; letter_cnt < letter_i; letter_cnt++) {
uint32_t letter = _lv_txt_encoded_next(txt, &i);
/*Count he lines to reach the clicked letter. But ignore the last '\n' because it
* still belongs to the clicked line*/
if(letter == '\n' && i_prev != letter_i) new_opt++;
i_prev = i;
}
}
else {
/*If dragged then align the list to have an element in the middle*/
const lv_font_t * font = lv_obj_get_style_text_font(obj, LV_PART_MAIN);
lv_coord_t line_space = lv_obj_get_style_text_line_space(obj, LV_PART_MAIN);
lv_coord_t font_h = lv_font_get_line_height(font);
lv_coord_t label_unit = font_h + line_space;
lv_coord_t mid = obj->coords.y1 + (obj->coords.y2 - obj->coords.y1) / 2;
lv_coord_t label_y1 = label->coords.y1 + lv_indev_scroll_throw_predict(indev, LV_DIR_VER);
int32_t id = (mid - label_y1) / label_unit;
if(id < 0) id = 0;
if(id >= roller->option_cnt) id = roller->option_cnt - 1;
new_opt = id;
}
if(new_opt >= 0) {
lv_roller_set_selected(obj, new_opt, LV_ANIM_ON);
}
}
uint32_t id = roller->sel_opt_id; /*Just to use uint32_t in event data*/
lv_res_t res = lv_event_send(obj, LV_EVENT_VALUE_CHANGED, &id);
return res;
}
/**
* Set the middle page for the roller if infinite is enabled
* @param roller pointer to a roller object
*/
static void inf_normalize(lv_obj_t * obj)
{
lv_roller_t * roller = (lv_roller_t *)obj;
if(roller->mode == LV_ROLLER_MODE_INFINITE) {
uint16_t real_id_cnt = roller->option_cnt / LV_ROLLER_INF_PAGES;
roller->sel_opt_id = roller->sel_opt_id % real_id_cnt;
roller->sel_opt_id += (LV_ROLLER_INF_PAGES / 2) * real_id_cnt; /*Select the middle page*/
roller->sel_opt_id_ori = roller->sel_opt_id % real_id_cnt;
roller->sel_opt_id_ori += (LV_ROLLER_INF_PAGES / 2) * real_id_cnt; /*Select the middle page*/
/*Move to the new id*/
const lv_font_t * font = lv_obj_get_style_text_font(obj, LV_PART_MAIN);
lv_coord_t line_space = lv_obj_get_style_text_line_space(obj, LV_PART_MAIN);
lv_coord_t font_h = lv_font_get_line_height(font);
lv_coord_t h = lv_obj_get_content_height(obj);
lv_obj_t * label = get_label(obj);
lv_coord_t sel_y1 = roller->sel_opt_id * (font_h + line_space);
lv_coord_t mid_y1 = h / 2 - font_h / 2;
lv_coord_t new_y = mid_y1 - sel_y1;
lv_obj_set_y(label, new_y);
}
}
static lv_obj_t * get_label(const lv_obj_t * obj)
{
return lv_obj_get_child(obj, 0);
}
static lv_coord_t get_selected_label_width(const lv_obj_t * obj)
{
lv_obj_t * label = get_label(obj);
if(label == NULL) return 0;
const lv_font_t * font = lv_obj_get_style_text_font(obj, LV_PART_SELECTED);
lv_coord_t letter_space = lv_obj_get_style_text_letter_space(obj, LV_PART_SELECTED);
const char * txt = lv_label_get_text(label);
lv_point_t size;
lv_txt_get_size(&size, txt, font, letter_space, 0, LV_COORD_MAX, LV_TEXT_FLAG_NONE);
return size.x;
}
static void scroll_anim_ready_cb(lv_anim_t * a)
{
lv_obj_t * obj = lv_obj_get_parent(a->var); /*The label is animated*/
inf_normalize(obj);
}
static void set_y_anim(void * obj, int32_t v)
{
lv_obj_set_y(obj, v);
}
#endif

View File

@@ -0,0 +1,138 @@
/**
* @file lv_roller.h
*
*/
#ifndef LV_ROLLER_H
#define LV_ROLLER_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../lv_conf_internal.h"
#if LV_USE_ROLLER != 0
/*Testing of dependencies*/
#if LV_USE_LABEL == 0
#error "lv_roller: lv_label is required. Enable it in lv_conf.h (LV_USE_ROLLER 1)"
#endif
#include "../core/lv_obj.h"
#include "lv_label.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/** Roller mode.*/
enum {
LV_ROLLER_MODE_NORMAL, /**< Normal mode (roller ends at the end of the options).*/
LV_ROLLER_MODE_INFINITE, /**< Infinite mode (roller can be scrolled forever).*/
};
typedef uint8_t lv_roller_mode_t;
typedef struct {
lv_obj_t obj;
uint16_t option_cnt; /**< Number of options*/
uint16_t sel_opt_id; /**< Index of the current option*/
uint16_t sel_opt_id_ori; /**< Store the original index on focus*/
lv_roller_mode_t mode : 1;
uint32_t moved : 1;
} lv_roller_t;
extern const lv_obj_class_t lv_roller_class;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a roller object
* @param parent pointer to an object, it will be the parent of the new roller.
* @return pointer to the created roller
*/
lv_obj_t * lv_roller_create(lv_obj_t * parent);
/*=====================
* Setter functions
*====================*/
/**
* Set the options on a roller
* @param obj pointer to roller object
* @param options a string with '\n' separated options. E.g. "One\nTwo\nThree"
* @param mode `LV_ROLLER_MODE_NORMAL` or `LV_ROLLER_MODE_INFINITE`
*/
void lv_roller_set_options(lv_obj_t * obj, const char * options, lv_roller_mode_t mode);
/**
* Set the selected option
* @param obj pointer to a roller object
* @param sel_opt index of the selected option (0 ... number of option - 1);
* @param anim_en LV_ANIM_ON: set with animation; LV_ANOM_OFF set immediately
*/
void lv_roller_set_selected(lv_obj_t * obj, uint16_t sel_opt, lv_anim_enable_t anim);
/**
* Set the height to show the given number of rows (options)
* @param obj pointer to a roller object
* @param row_cnt number of desired visible rows
*/
void lv_roller_set_visible_row_count(lv_obj_t * obj, uint8_t row_cnt);
/*=====================
* Getter functions
*====================*/
/**
* Get the index of the selected option
* @param obj pointer to a roller object
* @return index of the selected option (0 ... number of option - 1);
*/
uint16_t lv_roller_get_selected(const lv_obj_t * obj);
/**
* Get the current selected option as a string.
* @param obj pointer to ddlist object
* @param buf pointer to an array to store the string
* @param buf_size size of `buf` in bytes. 0: to ignore it.
*/
void lv_roller_get_selected_str(const lv_obj_t * obj, char * buf, uint32_t buf_size);
/**
* Get the options of a roller
* @param obj pointer to roller object
* @return the options separated by '\n'-s (E.g. "Option1\nOption2\nOption3")
*/
const char * lv_roller_get_options(const lv_obj_t * obj);
/**
* Get the total number of options
* @param obj pointer to a roller object
* @return the total number of options
*/
uint16_t lv_roller_get_option_cnt(const lv_obj_t * obj);
/**********************
* MACROS
**********************/
#endif /*LV_USE_ROLLER*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_ROLLER_H*/

View File

@@ -0,0 +1,443 @@
/**
* @file lv_slider.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_slider.h"
#if LV_USE_SLIDER != 0
#include "../misc/lv_assert.h"
#include "../core/lv_group.h"
#include "../core/lv_indev.h"
#include "../draw/lv_draw.h"
#include "../misc/lv_math.h"
#include "../core/lv_disp.h"
#include "lv_img.h"
/*********************
* DEFINES
*********************/
#define MY_CLASS &lv_slider_class
#define LV_SLIDER_KNOB_COORD(is_rtl, area) (is_rtl ? area.x1 : area.x2)
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_slider_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_slider_event(const lv_obj_class_t * class_p, lv_event_t * e);
static void position_knob(lv_obj_t * obj, lv_area_t * knob_area, const lv_coord_t knob_size, const bool hor);
static void draw_knob(lv_event_t * e);
static bool is_slider_horizontal(lv_obj_t * obj);
/**********************
* STATIC VARIABLES
**********************/
const lv_obj_class_t lv_slider_class = {
.constructor_cb = lv_slider_constructor,
.event_cb = lv_slider_event,
.editable = LV_OBJ_CLASS_EDITABLE_TRUE,
.group_def = LV_OBJ_CLASS_GROUP_DEF_TRUE,
.instance_size = sizeof(lv_slider_t),
.base_class = &lv_bar_class
};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_obj_t * lv_slider_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;
}
bool lv_slider_is_dragged(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_slider_t * slider = (lv_slider_t *)obj;
return slider->dragging ? true : false;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_slider_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
lv_slider_t * slider = (lv_slider_t *)obj;
/*Initialize the allocated 'slider'*/
slider->value_to_set = NULL;
slider->dragging = 0U;
slider->left_knob_focus = 0U;
lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLL_CHAIN);
lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_set_ext_click_area(obj, LV_DPX(8));
}
static void lv_slider_event(const lv_obj_class_t * class_p, lv_event_t * e)
{
LV_UNUSED(class_p);
lv_res_t res;
/*Call the ancestor's event handler*/
res = lv_obj_event_base(MY_CLASS, e);
if(res != LV_RES_OK) return;
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * obj = lv_event_get_target(e);
lv_slider_t * slider = (lv_slider_t *)obj;
lv_slider_mode_t type = lv_slider_get_mode(obj);
/*Advanced hit testing: react only on dragging the knob(s)*/
if(code == LV_EVENT_HIT_TEST) {
lv_hit_test_info_t * info = lv_event_get_param(e);
lv_coord_t ext_click_area = obj->spec_attr ? obj->spec_attr->ext_click_pad : 0;
/*Ordinary slider: was the knob area hit?*/
lv_area_t a;
lv_area_copy(&a, &slider->right_knob_area);
lv_area_increase(&a, ext_click_area, ext_click_area);
info->res = _lv_area_is_point_on(&a, info->point, 0);
/*There's still a chance that there is a hit if there is another knob*/
if((info->res == false) && (type == LV_SLIDER_MODE_RANGE)) {
lv_area_copy(&a, &slider->left_knob_area);
lv_area_increase(&a, ext_click_area, ext_click_area);
info->res = _lv_area_is_point_on(&a, info->point, 0);
}
}
else if(code == LV_EVENT_PRESSED) {
lv_obj_invalidate(obj);
lv_point_t p;
slider->dragging = true;
if(type == LV_SLIDER_MODE_NORMAL || type == LV_SLIDER_MODE_SYMMETRICAL) {
slider->value_to_set = &slider->bar.cur_value;
}
else if(type == LV_SLIDER_MODE_RANGE) {
lv_indev_get_point(lv_indev_get_act(), &p);
bool hor = lv_obj_get_width(obj) >= lv_obj_get_height(obj);
lv_base_dir_t base_dir = lv_obj_get_style_base_dir(obj, LV_PART_MAIN);
lv_coord_t dist_left, dist_right;
if(hor) {
if((base_dir != LV_BASE_DIR_RTL && p.x > slider->right_knob_area.x2) || (base_dir == LV_BASE_DIR_RTL &&
p.x < slider->right_knob_area.x1)) {
slider->value_to_set = &slider->bar.cur_value;
}
else if((base_dir != LV_BASE_DIR_RTL && p.x < slider->left_knob_area.x1) || (base_dir == LV_BASE_DIR_RTL &&
p.x > slider->left_knob_area.x2)) {
slider->value_to_set = &slider->bar.start_value;
}
else {
/*Calculate the distance from each knob*/
dist_left = LV_ABS((slider->left_knob_area.x1 + (slider->left_knob_area.x2 - slider->left_knob_area.x1) / 2) - p.x);
dist_right = LV_ABS((slider->right_knob_area.x1 + (slider->right_knob_area.x2 - slider->right_knob_area.x1) / 2) - p.x);
/*Use whichever one is closer*/
if(dist_right < dist_left) {
slider->value_to_set = &slider->bar.cur_value;
slider->left_knob_focus = 0;
}
else {
slider->value_to_set = &slider->bar.start_value;
slider->left_knob_focus = 1;
}
}
}
else {
if(p.y < slider->right_knob_area.y1) {
slider->value_to_set = &slider->bar.cur_value;
}
else if(p.y > slider->left_knob_area.y2) {
slider->value_to_set = &slider->bar.start_value;
}
else {
/*Calculate the distance from each knob*/
dist_left = LV_ABS((slider->left_knob_area.y1 + (slider->left_knob_area.y2 - slider->left_knob_area.y1) / 2) - p.y);
dist_right = LV_ABS((slider->right_knob_area.y1 + (slider->right_knob_area.y2 - slider->right_knob_area.y1) / 2) - p.y);
/*Use whichever one is closer*/
if(dist_right < dist_left) {
slider->value_to_set = &slider->bar.cur_value;
slider->left_knob_focus = 0;
}
else {
slider->value_to_set = &slider->bar.start_value;
slider->left_knob_focus = 1;
}
}
}
}
}
else if(code == LV_EVENT_PRESSING && slider->value_to_set != NULL) {
lv_indev_t * indev = lv_indev_get_act();
if(lv_indev_get_type(indev) != LV_INDEV_TYPE_POINTER) return;
lv_point_t p;
lv_indev_get_point(indev, &p);
int32_t new_value = 0;
const int32_t range = slider->bar.max_value - slider->bar.min_value;
if(is_slider_horizontal(obj)) {
const lv_coord_t bg_left = lv_obj_get_style_pad_left(obj, LV_PART_MAIN);
const lv_coord_t bg_right = lv_obj_get_style_pad_right(obj, LV_PART_MAIN);
const lv_coord_t w = lv_obj_get_width(obj);
const lv_coord_t indic_w = w - bg_left - bg_right;
if(lv_obj_get_style_base_dir(obj, LV_PART_MAIN) == LV_BASE_DIR_RTL) {
/*Make the point relative to the indicator*/
new_value = (obj->coords.x2 - bg_right) - p.x;
}
else {
/*Make the point relative to the indicator*/
new_value = p.x - (obj->coords.x1 + bg_left);
}
new_value = (new_value * range + indic_w / 2) / indic_w;
new_value += slider->bar.min_value;
}
else {
const lv_coord_t bg_top = lv_obj_get_style_pad_top(obj, LV_PART_MAIN);
const lv_coord_t bg_bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_MAIN);
const lv_coord_t h = lv_obj_get_height(obj);
const lv_coord_t indic_h = h - bg_bottom - bg_top;
/*Make the point relative to the indicator*/
new_value = p.y - (obj->coords.y2 + bg_bottom);
new_value = (-new_value * range + indic_h / 2) / indic_h;
new_value += slider->bar.min_value;
}
int32_t real_max_value = slider->bar.max_value;
int32_t real_min_value = slider->bar.min_value;
/*Figure out the min. and max. for this mode*/
if(slider->value_to_set == &slider->bar.start_value) {
real_max_value = slider->bar.cur_value;
}
else {
real_min_value = slider->bar.start_value;
}
new_value = LV_CLAMP(real_min_value, new_value, real_max_value);
if(*slider->value_to_set != new_value) {
*slider->value_to_set = new_value;
lv_obj_invalidate(obj);
res = lv_event_send(obj, LV_EVENT_VALUE_CHANGED, NULL);
if(res != LV_RES_OK) return;
}
}
else if(code == LV_EVENT_RELEASED || code == LV_EVENT_PRESS_LOST) {
slider->dragging = false;
slider->value_to_set = NULL;
lv_obj_invalidate(obj);
/*Leave edit mode if released. (No need to wait for LONG_PRESS)*/
lv_group_t * g = lv_obj_get_group(obj);
bool editing = lv_group_get_editing(g);
lv_indev_type_t indev_type = lv_indev_get_type(lv_indev_get_act());
if(indev_type == LV_INDEV_TYPE_ENCODER) {
if(editing) {
if(lv_slider_get_mode(obj) == LV_SLIDER_MODE_RANGE) {
if(slider->left_knob_focus == 0) slider->left_knob_focus = 1;
else {
slider->left_knob_focus = 0;
lv_group_set_editing(g, false);
}
}
else {
lv_group_set_editing(g, false);
}
}
}
}
else if(code == LV_EVENT_FOCUSED) {
lv_indev_type_t indev_type = lv_indev_get_type(lv_indev_get_act());
if(indev_type == LV_INDEV_TYPE_ENCODER || indev_type == LV_INDEV_TYPE_KEYPAD) {
slider->left_knob_focus = 0;
}
}
else if(code == LV_EVENT_SIZE_CHANGED) {
lv_obj_refresh_ext_draw_size(obj);
}
else if(code == LV_EVENT_REFR_EXT_DRAW_SIZE) {
lv_coord_t knob_left = lv_obj_get_style_pad_left(obj, LV_PART_KNOB);
lv_coord_t knob_right = lv_obj_get_style_pad_right(obj, LV_PART_KNOB);
lv_coord_t knob_top = lv_obj_get_style_pad_top(obj, LV_PART_KNOB);
lv_coord_t knob_bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_KNOB);
/*The smaller size is the knob diameter*/
lv_coord_t zoom = lv_obj_get_style_transform_zoom(obj, LV_PART_KNOB);
lv_coord_t trans_w = lv_obj_get_style_transform_width(obj, LV_PART_KNOB);
lv_coord_t trans_h = lv_obj_get_style_transform_height(obj, LV_PART_KNOB);
lv_coord_t knob_size = LV_MIN(lv_obj_get_width(obj) + 2 * trans_w, lv_obj_get_height(obj) + 2 * trans_h) >> 1;
knob_size = (knob_size * zoom) >> 8;
knob_size += LV_MAX(LV_MAX(knob_left, knob_right), LV_MAX(knob_bottom, knob_top));
knob_size += 2; /*For rounding error*/
knob_size += lv_obj_calculate_ext_draw_size(obj, LV_PART_KNOB);
/*Indic. size is handled by bar*/
lv_coord_t * s = lv_event_get_param(e);
*s = LV_MAX(*s, knob_size);
}
else if(code == LV_EVENT_KEY) {
char c = *((char *)lv_event_get_param(e));
if(c == LV_KEY_RIGHT || c == LV_KEY_UP) {
if(!slider->left_knob_focus) lv_slider_set_value(obj, lv_slider_get_value(obj) + 1, LV_ANIM_ON);
else lv_slider_set_left_value(obj, lv_slider_get_left_value(obj) + 1, LV_ANIM_ON);
}
else if(c == LV_KEY_LEFT || c == LV_KEY_DOWN) {
if(!slider->left_knob_focus) lv_slider_set_value(obj, lv_slider_get_value(obj) - 1, LV_ANIM_ON);
else lv_slider_set_left_value(obj, lv_slider_get_left_value(obj) - 1, LV_ANIM_ON);
}
else {
return;
}
res = lv_event_send(obj, LV_EVENT_VALUE_CHANGED, NULL);
if(res != LV_RES_OK) return;
}
else if(code == LV_EVENT_DRAW_MAIN) {
draw_knob(e);
}
}
static void draw_knob(lv_event_t * e)
{
lv_obj_t * obj = lv_event_get_target(e);
lv_slider_t * slider = (lv_slider_t *)obj;
lv_draw_ctx_t * draw_ctx = lv_event_get_draw_ctx(e);
const bool is_rtl = LV_BASE_DIR_RTL == lv_obj_get_style_base_dir(obj, LV_PART_MAIN);
const bool is_horizontal = is_slider_horizontal(obj);
lv_area_t knob_area;
lv_coord_t knob_size;
bool is_symmetrical = false;
if(slider->bar.mode == LV_BAR_MODE_SYMMETRICAL && slider->bar.min_value < 0 &&
slider->bar.max_value > 0) is_symmetrical = true;
if(is_horizontal) {
knob_size = lv_obj_get_height(obj);
if(is_symmetrical && slider->bar.cur_value < 0) knob_area.x1 = slider->bar.indic_area.x1;
else knob_area.x1 = LV_SLIDER_KNOB_COORD(is_rtl, slider->bar.indic_area);
}
else {
knob_size = lv_obj_get_width(obj);
if(is_symmetrical && slider->bar.cur_value < 0) knob_area.y1 = slider->bar.indic_area.y2;
else knob_area.y1 = slider->bar.indic_area.y1;
}
lv_draw_rect_dsc_t knob_rect_dsc;
lv_draw_rect_dsc_init(&knob_rect_dsc);
lv_obj_init_draw_rect_dsc(obj, LV_PART_KNOB, &knob_rect_dsc);
/* Update knob area with knob style */
position_knob(obj, &knob_area, knob_size, is_horizontal);
/* Update right knob area with calculated knob area */
lv_area_copy(&slider->right_knob_area, &knob_area);
lv_obj_draw_part_dsc_t part_draw_dsc;
lv_obj_draw_dsc_init(&part_draw_dsc, draw_ctx);
part_draw_dsc.part = LV_PART_KNOB;
part_draw_dsc.class_p = MY_CLASS;
part_draw_dsc.type = LV_SLIDER_DRAW_PART_KNOB;
part_draw_dsc.id = 0;
part_draw_dsc.draw_area = &slider->right_knob_area;
part_draw_dsc.rect_dsc = &knob_rect_dsc;
if(lv_slider_get_mode(obj) != LV_SLIDER_MODE_RANGE) {
lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &part_draw_dsc);
lv_draw_rect(draw_ctx, &knob_rect_dsc, &slider->right_knob_area);
lv_event_send(obj, LV_EVENT_DRAW_PART_END, &part_draw_dsc);
}
else {
/*Save the draw part_draw_dsc. because it can be modified in the event*/
lv_draw_rect_dsc_t knob_rect_dsc_tmp;
lv_memcpy(&knob_rect_dsc_tmp, &knob_rect_dsc, sizeof(lv_draw_rect_dsc_t));
/* Draw the right knob */
lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &part_draw_dsc);
lv_draw_rect(draw_ctx, &knob_rect_dsc, &slider->right_knob_area);
lv_event_send(obj, LV_EVENT_DRAW_PART_END, &part_draw_dsc);
/*Calculate the second knob area*/
if(is_horizontal) {
/*use !is_rtl to get the other knob*/
knob_area.x1 = LV_SLIDER_KNOB_COORD(!is_rtl, slider->bar.indic_area);
}
else {
knob_area.y1 = slider->bar.indic_area.y2;
}
position_knob(obj, &knob_area, knob_size, is_horizontal);
lv_area_copy(&slider->left_knob_area, &knob_area);
lv_memcpy(&knob_rect_dsc, &knob_rect_dsc_tmp, sizeof(lv_draw_rect_dsc_t));
part_draw_dsc.type = LV_SLIDER_DRAW_PART_KNOB_LEFT;
part_draw_dsc.draw_area = &slider->left_knob_area;
part_draw_dsc.rect_dsc = &knob_rect_dsc;
part_draw_dsc.id = 1;
lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &part_draw_dsc);
lv_draw_rect(draw_ctx, &knob_rect_dsc, &slider->left_knob_area);
lv_event_send(obj, LV_EVENT_DRAW_PART_END, &part_draw_dsc);
}
}
static void position_knob(lv_obj_t * obj, lv_area_t * knob_area, const lv_coord_t knob_size, const bool hor)
{
if(hor) {
knob_area->x1 -= (knob_size >> 1);
knob_area->x2 = knob_area->x1 + knob_size - 1;
knob_area->y1 = obj->coords.y1;
knob_area->y2 = obj->coords.y2;
}
else {
knob_area->y1 -= (knob_size >> 1);
knob_area->y2 = knob_area->y1 + knob_size - 1;
knob_area->x1 = obj->coords.x1;
knob_area->x2 = obj->coords.x2;
}
lv_coord_t knob_left = lv_obj_get_style_pad_left(obj, LV_PART_KNOB);
lv_coord_t knob_right = lv_obj_get_style_pad_right(obj, LV_PART_KNOB);
lv_coord_t knob_top = lv_obj_get_style_pad_top(obj, LV_PART_KNOB);
lv_coord_t knob_bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_KNOB);
lv_coord_t transf_w = lv_obj_get_style_transform_width(obj, LV_PART_KNOB);
lv_coord_t transf_h = lv_obj_get_style_transform_height(obj, LV_PART_KNOB);
/*Apply the paddings on the knob area*/
knob_area->x1 -= knob_left + transf_w;
knob_area->x2 += knob_right + transf_w;
knob_area->y1 -= knob_top + transf_h;
knob_area->y2 += knob_bottom + transf_h;
}
static bool is_slider_horizontal(lv_obj_t * obj)
{
return lv_obj_get_width(obj) >= lv_obj_get_height(obj);
}
#endif

View File

@@ -0,0 +1,195 @@
/**
* @file lv_slider.h
*
*/
#ifndef LV_SLIDER_H
#define LV_SLIDER_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../lv_conf_internal.h"
#if LV_USE_SLIDER != 0
/*Testing of dependencies*/
#if LV_USE_BAR == 0
#error "lv_slider: lv_bar is required. Enable it in lv_conf.h (LV_USE_BAR 1)"
#endif
#include "../core/lv_obj.h"
#include "lv_bar.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
enum {
LV_SLIDER_MODE_NORMAL = LV_BAR_MODE_NORMAL,
LV_SLIDER_MODE_SYMMETRICAL = LV_BAR_MODE_SYMMETRICAL,
LV_SLIDER_MODE_RANGE = LV_BAR_MODE_RANGE
};
typedef uint8_t lv_slider_mode_t;
typedef struct {
lv_bar_t bar; /*Add the ancestor's type first*/
lv_area_t left_knob_area;
lv_area_t right_knob_area;
int32_t * value_to_set; /*Which bar value to set*/
uint8_t dragging : 1; /*1: the slider is being dragged*/
uint8_t left_knob_focus : 1; /*1: with encoder now the right knob can be adjusted*/
} lv_slider_t;
extern const lv_obj_class_t lv_slider_class;
/**
* `type` field in `lv_obj_draw_part_dsc_t` if `class_p = lv_slider_class`
* Used in `LV_EVENT_DRAW_PART_BEGIN` and `LV_EVENT_DRAW_PART_END`
*/
typedef enum {
LV_SLIDER_DRAW_PART_KNOB, /**< The main (right) knob's rectangle*/
LV_SLIDER_DRAW_PART_KNOB_LEFT, /**< The left knob's rectangle*/
} lv_slider_draw_part_type_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a slider object
* @param parent pointer to an object, it will be the parent of the new slider.
* @return pointer to the created slider
*/
lv_obj_t * lv_slider_create(lv_obj_t * parent);
/*=====================
* Setter functions
*====================*/
/**
* Set a new value on the slider
* @param obj pointer to a slider object
* @param value the new value
* @param anim LV_ANIM_ON: set the value with an animation; LV_ANIM_OFF: change the value immediately
*/
static inline void lv_slider_set_value(lv_obj_t * obj, int32_t value, lv_anim_enable_t anim)
{
lv_bar_set_value(obj, value, anim);
}
/**
* Set a new value for the left knob of a slider
* @param obj pointer to a slider object
* @param value new value
* @param anim LV_ANIM_ON: set the value with an animation; LV_ANIM_OFF: change the value immediately
*/
static inline void lv_slider_set_left_value(lv_obj_t * obj, int32_t value, lv_anim_enable_t anim)
{
lv_bar_set_start_value(obj, value, anim);
}
/**
* Set minimum and the maximum values of a bar
* @param obj pointer to the slider object
* @param min minimum value
* @param max maximum value
*/
static inline void lv_slider_set_range(lv_obj_t * obj, int32_t min, int32_t max)
{
lv_bar_set_range(obj, min, max);
}
/**
* Set the mode of slider.
* @param obj pointer to a slider object
* @param mode the mode of the slider. See ::lv_slider_mode_t
*/
static inline void lv_slider_set_mode(lv_obj_t * obj, lv_slider_mode_t mode)
{
lv_bar_set_mode(obj, (lv_bar_mode_t)mode);
}
/*=====================
* Getter functions
*====================*/
/**
* Get the value of the main knob of a slider
* @param obj pointer to a slider object
* @return the value of the main knob of the slider
*/
static inline int32_t lv_slider_get_value(const lv_obj_t * obj)
{
return lv_bar_get_value(obj);
}
/**
* Get the value of the left knob of a slider
* @param obj pointer to a slider object
* @return the value of the left knob of the slider
*/
static inline int32_t lv_slider_get_left_value(const lv_obj_t * obj)
{
return lv_bar_get_start_value(obj);
}
/**
* Get the minimum value of a slider
* @param obj pointer to a slider object
* @return the minimum value of the slider
*/
static inline int32_t lv_slider_get_min_value(const lv_obj_t * obj)
{
return lv_bar_get_min_value(obj);
}
/**
* Get the maximum value of a slider
* @param obj pointer to a slider object
* @return the maximum value of the slider
*/
static inline int32_t lv_slider_get_max_value(const lv_obj_t * obj)
{
return lv_bar_get_max_value(obj);
}
/**
* Give the slider is being dragged or not
* @param obj pointer to a slider object
* @return true: drag in progress false: not dragged
*/
bool lv_slider_is_dragged(const lv_obj_t * obj);
/**
* Get the mode of the slider.
* @param obj pointer to a bar object
* @return see ::lv_slider_mode_t
*/
static inline lv_slider_mode_t lv_slider_get_mode(lv_obj_t * slider)
{
lv_bar_mode_t mode = lv_bar_get_mode(slider);
if(mode == LV_BAR_MODE_SYMMETRICAL) return LV_SLIDER_MODE_SYMMETRICAL;
else if(mode == LV_BAR_MODE_RANGE) return LV_SLIDER_MODE_RANGE;
else return LV_SLIDER_MODE_NORMAL;
}
/**********************
* MACROS
**********************/
#endif /*LV_USE_SLIDER*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_SLIDER_H*/

View File

@@ -0,0 +1,277 @@
/**
* @file lv_sw.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_switch.h"
#if LV_USE_SWITCH != 0
#include "../misc/lv_assert.h"
#include "../misc/lv_math.h"
#include "../misc/lv_anim.h"
#include "../core/lv_indev.h"
#include "../core/lv_disp.h"
#include "lv_img.h"
/*********************
* DEFINES
*********************/
#define MY_CLASS &lv_switch_class
#define LV_SWITCH_IS_ANIMATING(sw) (((sw)->anim_state) != LV_SWITCH_ANIM_STATE_INV)
/** Switch animation start value. (Not the real value of the switch just indicates process animation)*/
#define LV_SWITCH_ANIM_STATE_START 0
/** Switch animation end value. (Not the real value of the switch just indicates process animation)*/
#define LV_SWITCH_ANIM_STATE_END 256
/** Mark no animation is in progress*/
#define LV_SWITCH_ANIM_STATE_INV -1
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_switch_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_switch_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_switch_event(const lv_obj_class_t * class_p, lv_event_t * e);
static void draw_main(lv_event_t * e);
static void lv_switch_anim_exec_cb(void * sw, int32_t value);
static void lv_switch_trigger_anim(lv_obj_t * obj);
static void lv_switch_anim_ready(lv_anim_t * a);
/**********************
* STATIC VARIABLES
**********************/
const lv_obj_class_t lv_switch_class = {
.constructor_cb = lv_switch_constructor,
.destructor_cb = lv_switch_destructor,
.event_cb = lv_switch_event,
.width_def = (4 * LV_DPI_DEF) / 10,
.height_def = (4 * LV_DPI_DEF) / 17,
.group_def = LV_OBJ_CLASS_GROUP_DEF_TRUE,
.instance_size = sizeof(lv_switch_t),
.base_class = &lv_obj_class
};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_obj_t * lv_switch_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;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_switch_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
LV_TRACE_OBJ_CREATE("begin");
lv_switch_t * sw = (lv_switch_t *)obj;
sw->anim_state = LV_SWITCH_ANIM_STATE_INV;
lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_add_flag(obj, LV_OBJ_FLAG_CHECKABLE);
lv_obj_add_flag(obj, LV_OBJ_FLAG_SCROLL_ON_FOCUS);
LV_TRACE_OBJ_CREATE("finished");
}
static void lv_switch_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
lv_switch_t * sw = (lv_switch_t *)obj;
lv_anim_del(sw, NULL);
}
static void lv_switch_event(const lv_obj_class_t * class_p, lv_event_t * e)
{
LV_UNUSED(class_p);
lv_res_t res;
/*Call the ancestor's event handler*/
res = lv_obj_event_base(MY_CLASS, e);
if(res != LV_RES_OK) return;
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * obj = lv_event_get_target(e);
if(code == LV_EVENT_REFR_EXT_DRAW_SIZE) {
lv_coord_t knob_left = lv_obj_get_style_pad_left(obj, LV_PART_KNOB);
lv_coord_t knob_right = lv_obj_get_style_pad_right(obj, LV_PART_KNOB);
lv_coord_t knob_top = lv_obj_get_style_pad_top(obj, LV_PART_KNOB);
lv_coord_t knob_bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_KNOB);
/*The smaller size is the knob diameter*/
lv_coord_t knob_size = LV_MAX4(knob_left, knob_right, knob_bottom, knob_top);
knob_size += _LV_SWITCH_KNOB_EXT_AREA_CORRECTION;
knob_size += lv_obj_calculate_ext_draw_size(obj, LV_PART_KNOB);
lv_coord_t * s = lv_event_get_param(e);
*s = LV_MAX(*s, knob_size);
*s = LV_MAX(*s, lv_obj_calculate_ext_draw_size(obj, LV_PART_INDICATOR));
}
else if(code == LV_EVENT_VALUE_CHANGED) {
lv_switch_trigger_anim(obj);
lv_obj_invalidate(obj);
}
else if(code == LV_EVENT_DRAW_MAIN) {
draw_main(e);
}
}
static void draw_main(lv_event_t * e)
{
lv_obj_t * obj = lv_event_get_target(e);
lv_switch_t * sw = (lv_switch_t *)obj;
lv_draw_ctx_t * draw_ctx = lv_event_get_draw_ctx(e);
/*Calculate the indicator area*/
lv_coord_t bg_left = lv_obj_get_style_pad_left(obj, LV_PART_MAIN);
lv_coord_t bg_right = lv_obj_get_style_pad_right(obj, LV_PART_MAIN);
lv_coord_t bg_top = lv_obj_get_style_pad_top(obj, LV_PART_MAIN);
lv_coord_t bg_bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_MAIN);
/*Draw the indicator*/
/*Respect the background's padding*/
lv_area_t indic_area;
lv_area_copy(&indic_area, &obj->coords);
indic_area.x1 += bg_left;
indic_area.x2 -= bg_right;
indic_area.y1 += bg_top;
indic_area.y2 -= bg_bottom;
lv_draw_rect_dsc_t draw_indic_dsc;
lv_draw_rect_dsc_init(&draw_indic_dsc);
lv_obj_init_draw_rect_dsc(obj, LV_PART_INDICATOR, &draw_indic_dsc);
lv_draw_rect(draw_ctx, &draw_indic_dsc, &indic_area);
/*Draw the knob*/
lv_coord_t anim_value_x = 0;
lv_coord_t knob_size = lv_obj_get_height(obj);
lv_coord_t anim_length = lv_area_get_width(&obj->coords) - knob_size;
if(LV_SWITCH_IS_ANIMATING(sw)) {
/* Use the animation's coordinate */
anim_value_x = (anim_length * sw->anim_state) / LV_SWITCH_ANIM_STATE_END;
}
else {
/* Use LV_STATE_CHECKED to decide the coordinate */
bool chk = lv_obj_get_state(obj) & LV_STATE_CHECKED;
anim_value_x = chk ? anim_length : 0;
}
if(LV_BASE_DIR_RTL == lv_obj_get_style_base_dir(obj, LV_PART_MAIN)) {
anim_value_x = anim_length - anim_value_x;
}
lv_area_t knob_area;
knob_area.x1 = obj->coords.x1 + anim_value_x;
knob_area.x2 = knob_area.x1 + knob_size;
knob_area.y1 = obj->coords.y1;
knob_area.y2 = obj->coords.y2;
lv_coord_t knob_left = lv_obj_get_style_pad_left(obj, LV_PART_KNOB);
lv_coord_t knob_right = lv_obj_get_style_pad_right(obj, LV_PART_KNOB);
lv_coord_t knob_top = lv_obj_get_style_pad_top(obj, LV_PART_KNOB);
lv_coord_t knob_bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_KNOB);
/*Apply the paddings on the knob area*/
knob_area.x1 -= knob_left;
knob_area.x2 += knob_right;
knob_area.y1 -= knob_top;
knob_area.y2 += knob_bottom;
lv_draw_rect_dsc_t knob_rect_dsc;
lv_draw_rect_dsc_init(&knob_rect_dsc);
lv_obj_init_draw_rect_dsc(obj, LV_PART_KNOB, &knob_rect_dsc);
lv_draw_rect(draw_ctx, &knob_rect_dsc, &knob_area);
}
static void lv_switch_anim_exec_cb(void * var, int32_t value)
{
lv_switch_t * sw = var;
sw->anim_state = value;
lv_obj_invalidate((lv_obj_t *)sw);
}
/**
* Resets the switch's animation state to "no animation in progress".
*/
static void lv_switch_anim_ready(lv_anim_t * a)
{
lv_switch_t * sw = a->var;
sw->anim_state = LV_SWITCH_ANIM_STATE_INV;
lv_obj_invalidate((lv_obj_t *)sw);
}
/**
* Starts an animation for the switch knob. if the anim_time style property is greater than 0
* @param obj the switch to animate
*/
static void lv_switch_trigger_anim(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_switch_t * sw = (lv_switch_t *)obj;
uint32_t anim_dur_full = lv_obj_get_style_anim_time(obj, LV_PART_MAIN);
if(anim_dur_full > 0) {
bool chk = lv_obj_get_state(obj) & LV_STATE_CHECKED;
int32_t anim_start;
int32_t anim_end;
/*No animation in progress -> simply set the values*/
if(sw->anim_state == LV_SWITCH_ANIM_STATE_INV) {
anim_start = chk ? LV_SWITCH_ANIM_STATE_START : LV_SWITCH_ANIM_STATE_END;
anim_end = chk ? LV_SWITCH_ANIM_STATE_END : LV_SWITCH_ANIM_STATE_START;
}
/*Animation in progress. Start from the animation end value*/
else {
anim_start = sw->anim_state;
anim_end = chk ? LV_SWITCH_ANIM_STATE_END : LV_SWITCH_ANIM_STATE_START;
}
/*Calculate actual animation duration*/
uint32_t anim_dur = (anim_dur_full * LV_ABS(anim_start - anim_end)) / LV_SWITCH_ANIM_STATE_END;
/*Stop the previous animation if it exists*/
lv_anim_del(sw, NULL);
lv_anim_t a;
lv_anim_init(&a);
lv_anim_set_var(&a, sw);
lv_anim_set_exec_cb(&a, lv_switch_anim_exec_cb);
lv_anim_set_values(&a, anim_start, anim_end);
lv_anim_set_ready_cb(&a, lv_switch_anim_ready);
lv_anim_set_time(&a, anim_dur);
lv_anim_start(&a);
}
}
#endif

View File

@@ -0,0 +1,61 @@
/**
* @file lv_switch.h
*
*/
#ifndef LV_SWITCH_H
#define LV_SWITCH_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../lv_conf_internal.h"
#if LV_USE_SWITCH != 0
#include "../core/lv_obj.h"
/*********************
* DEFINES
*********************/
/** Switch knob extra area correction factor */
#define _LV_SWITCH_KNOB_EXT_AREA_CORRECTION 2
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_obj_t obj;
int32_t anim_state;
} lv_switch_t;
extern const lv_obj_class_t lv_switch_class;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a switch object
* @param parent pointer to an object, it will be the parent of the new switch
* @return pointer to the created switch
*/
lv_obj_t * lv_switch_create(lv_obj_t * parent);
/**********************
* MACROS
**********************/
#endif /*LV_USE_SWITCH*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_SWITCH_H*/

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,210 @@
/**
* @file lv_table.h
*
*/
#ifndef LV_TABLE_H
#define LV_TABLE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../lv_conf_internal.h"
#if LV_USE_TABLE != 0
/*Testing of dependencies*/
#if LV_USE_LABEL == 0
#error "lv_table: lv_label is required. Enable it in lv_conf.h (LV_USE_LABEL 1)"
#endif
#include "../core/lv_obj.h"
#include "lv_label.h"
/*********************
* DEFINES
*********************/
#define LV_TABLE_CELL_NONE 0XFFFF
LV_EXPORT_CONST_INT(LV_TABLE_CELL_NONE);
/**********************
* TYPEDEFS
**********************/
enum {
LV_TABLE_CELL_CTRL_MERGE_RIGHT = 1 << 0,
LV_TABLE_CELL_CTRL_TEXT_CROP = 1 << 1,
LV_TABLE_CELL_CTRL_CUSTOM_1 = 1 << 4,
LV_TABLE_CELL_CTRL_CUSTOM_2 = 1 << 5,
LV_TABLE_CELL_CTRL_CUSTOM_3 = 1 << 6,
LV_TABLE_CELL_CTRL_CUSTOM_4 = 1 << 7,
};
typedef uint8_t lv_table_cell_ctrl_t;
/*Data of table*/
typedef struct {
lv_obj_t obj;
uint16_t col_cnt;
uint16_t row_cnt;
char ** cell_data;
lv_coord_t * row_h;
lv_coord_t * col_w;
uint16_t col_act;
uint16_t row_act;
} lv_table_t;
extern const lv_obj_class_t lv_table_class;
/**
* `type` field in `lv_obj_draw_part_dsc_t` if `class_p = lv_table_class`
* Used in `LV_EVENT_DRAW_PART_BEGIN` and `LV_EVENT_DRAW_PART_END`
*/
typedef enum {
LV_TABLE_DRAW_PART_CELL, /**< A cell*/
} lv_table_draw_part_type_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a table object
* @param parent pointer to an object, it will be the parent of the new table
* @return pointer to the created table
*/
lv_obj_t * lv_table_create(lv_obj_t * parent);
/*=====================
* Setter functions
*====================*/
/**
* Set the value of a cell.
* @param obj pointer to a Table object
* @param row id of the row [0 .. row_cnt -1]
* @param col id of the column [0 .. col_cnt -1]
* @param txt text to display in the cell. It will be copied and saved so this variable is not required after this function call.
* @note New roes/columns are added automatically if required
*/
void lv_table_set_cell_value(lv_obj_t * obj, uint16_t row, uint16_t col, const char * txt);
/**
* Set the value of a cell. Memory will be allocated to store the text by the table.
* @param obj pointer to a Table object
* @param row id of the row [0 .. row_cnt -1]
* @param col id of the column [0 .. col_cnt -1]
* @param fmt `printf`-like format
* @note New roes/columns are added automatically if required
*/
void lv_table_set_cell_value_fmt(lv_obj_t * obj, uint16_t row, uint16_t col, const char * fmt, ...);
/**
* Set the number of rows
* @param obj table pointer to a Table object
* @param row_cnt number of rows
*/
void lv_table_set_row_cnt(lv_obj_t * obj, uint16_t row_cnt);
/**
* Set the number of columns
* @param obj table pointer to a Table object
* @param col_cnt number of columns.
*/
void lv_table_set_col_cnt(lv_obj_t * obj, uint16_t col_cnt);
/**
* Set the width of a column
* @param obj table pointer to a Table object
* @param col_id id of the column [0 .. LV_TABLE_COL_MAX -1]
* @param w width of the column
*/
void lv_table_set_col_width(lv_obj_t * obj, uint16_t col_id, lv_coord_t w);
/**
* Add control bits to the cell.
* @param obj pointer to a Table object
* @param row id of the row [0 .. row_cnt -1]
* @param col id of the column [0 .. col_cnt -1]
* @param ctrl OR-ed values from ::lv_table_cell_ctrl_t
*/
void lv_table_add_cell_ctrl(lv_obj_t * obj, uint16_t row, uint16_t col, lv_table_cell_ctrl_t ctrl);
/**
* Clear control bits of the cell.
* @param obj pointer to a Table object
* @param row id of the row [0 .. row_cnt -1]
* @param col id of the column [0 .. col_cnt -1]
* @param ctrl OR-ed values from ::lv_table_cell_ctrl_t
*/
void lv_table_clear_cell_ctrl(lv_obj_t * obj, uint16_t row, uint16_t col, lv_table_cell_ctrl_t ctrl);
/*=====================
* Getter functions
*====================*/
/**
* Get the value of a cell.
* @param obj pointer to a Table object
* @param row id of the row [0 .. row_cnt -1]
* @param col id of the column [0 .. col_cnt -1]
* @return text in the cell
*/
const char * lv_table_get_cell_value(lv_obj_t * obj, uint16_t row, uint16_t col);
/**
* Get the number of rows.
* @param obj table pointer to a Table object
* @return number of rows.
*/
uint16_t lv_table_get_row_cnt(lv_obj_t * obj);
/**
* Get the number of columns.
* @param obj table pointer to a Table object
* @return number of columns.
*/
uint16_t lv_table_get_col_cnt(lv_obj_t * obj);
/**
* Get the width of a column
* @param obj table pointer to a Table object
* @param col id of the column [0 .. LV_TABLE_COL_MAX -1]
* @return width of the column
*/
lv_coord_t lv_table_get_col_width(lv_obj_t * obj, uint16_t col);
/**
* Get whether a cell has the control bits
* @param obj pointer to a Table object
* @param row id of the row [0 .. row_cnt -1]
* @param col id of the column [0 .. col_cnt -1]
* @param ctrl OR-ed values from ::lv_table_cell_ctrl_t
* @return true: all control bits are set; false: not all control bits are set
*/
bool lv_table_has_cell_ctrl(lv_obj_t * obj, uint16_t row, uint16_t col, lv_table_cell_ctrl_t ctrl);
/**
* Get the selected cell (pressed and or focused)
* @param obj pointer to a table object
* @param row pointer to variable to store the selected row (LV_TABLE_CELL_NONE: if no cell selected)
* @param col pointer to variable to store the selected column (LV_TABLE_CELL_NONE: if no cell selected)
*/
void lv_table_get_selected_cell(lv_obj_t * obj, uint16_t * row, uint16_t * col);
/**********************
* MACROS
**********************/
#endif /*LV_USE_TABLE*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_TABLE_H*/

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,358 @@
/**
* @file lv_textarea.h
*
*/
#ifndef LV_TEXTAREA_H
#define LV_TEXTAREA_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../lv_conf_internal.h"
#if LV_USE_TEXTAREA != 0
/*Testing of dependencies*/
#if LV_USE_LABEL == 0
#error "lv_ta: lv_label is required. Enable it in lv_conf.h (LV_USE_LABEL 1)"
#endif
#include "../core/lv_obj.h"
#include "lv_label.h"
/*********************
* DEFINES
*********************/
#define LV_TEXTAREA_CURSOR_LAST (0x7FFF) /*Put the cursor after the last character*/
LV_EXPORT_CONST_INT(LV_TEXTAREA_CURSOR_LAST);
/**********************
* TYPEDEFS
**********************/
/*Data of text area*/
typedef struct {
lv_obj_t obj;
lv_obj_t * label; /*Label of the text area*/
char * placeholder_txt; /*Place holder label. only visible if text is an empty string*/
char * pwd_tmp; /*Used to store the original text in password mode*/
char * pwd_bullet; /*Replacement characters displayed in password mode*/
const char * accepted_chars; /*Only these characters will be accepted. NULL: accept all*/
uint32_t max_length; /*The max. number of characters. 0: no limit*/
uint16_t pwd_show_time; /*Time to show characters in password mode before change them to '*'*/
struct {
lv_coord_t valid_x; /*Used when stepping up/down to a shorter line.
*(Used by the library)*/
uint32_t pos; /*The current cursor position
*(0: before 1st letter; 1: before 2nd letter ...)*/
lv_area_t area; /*Cursor area relative to the Text Area*/
uint32_t txt_byte_pos; /*Byte index of the letter after (on) the cursor*/
uint8_t show : 1; /*Cursor is visible now or not (Handled by the library)*/
uint8_t click_pos : 1; /*1: Enable positioning the cursor by clicking the text area*/
} cursor;
#if LV_LABEL_TEXT_SELECTION
uint32_t sel_start; /*Temporary values for text selection*/
uint32_t sel_end;
uint8_t text_sel_in_prog : 1; /*User is in process of selecting*/
uint8_t text_sel_en : 1; /*Text can be selected on this text area*/
#endif
uint8_t pwd_mode : 1; /*Replace characters with '*'*/
uint8_t one_line : 1; /*One line mode (ignore line breaks)*/
} lv_textarea_t;
extern const lv_obj_class_t lv_textarea_class;
enum {
LV_PART_TEXTAREA_PLACEHOLDER = LV_PART_CUSTOM_FIRST,
};
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a text area object
* @param parent pointer to an object, it will be the parent of the new text area
* @return pointer to the created text area
*/
lv_obj_t * lv_textarea_create(lv_obj_t * parent);
/*======================
* Add/remove functions
*=====================*/
/**
* Insert a character to the current cursor position.
* To add a wide char, e.g. 'Á' use `_lv_txt_encoded_conv_wc('Á')`
* @param obj pointer to a text area object
* @param c a character (e.g. 'a')
*/
void lv_textarea_add_char(lv_obj_t * obj, uint32_t c);
/**
* Insert a text to the current cursor position
* @param obj pointer to a text area object
* @param txt a '\0' terminated string to insert
*/
void lv_textarea_add_text(lv_obj_t * obj, const char * txt);
/**
* Delete a the left character from the current cursor position
* @param obj pointer to a text area object
*/
void lv_textarea_del_char(lv_obj_t * obj);
/**
* Delete the right character from the current cursor position
* @param obj pointer to a text area object
*/
void lv_textarea_del_char_forward(lv_obj_t * obj);
/*=====================
* Setter functions
*====================*/
/**
* Set the text of a text area
* @param obj pointer to a text area object
* @param txt pointer to the text
*/
void lv_textarea_set_text(lv_obj_t * obj, const char * txt);
/**
* Set the placeholder text of a text area
* @param obj pointer to a text area object
* @param txt pointer to the text
*/
void lv_textarea_set_placeholder_text(lv_obj_t * obj, const char * txt);
/**
* Set the cursor position
* @param obj pointer to a text area object
* @param pos the new cursor position in character index
* < 0 : index from the end of the text
* LV_TEXTAREA_CURSOR_LAST: go after the last character
*/
void lv_textarea_set_cursor_pos(lv_obj_t * obj, int32_t pos);
/**
* Enable/Disable the positioning of the cursor by clicking the text on the text area.
* @param obj pointer to a text area object
* @param en true: enable click positions; false: disable
*/
void lv_textarea_set_cursor_click_pos(lv_obj_t * obj, bool en);
/**
* Enable/Disable password mode
* @param obj pointer to a text area object
* @param en true: enable, false: disable
*/
void lv_textarea_set_password_mode(lv_obj_t * obj, bool en);
/**
* Set the replacement characters to show in password mode
* @param obj pointer to a text area object
* @param bullet pointer to the replacement text
*/
void lv_textarea_set_password_bullet(lv_obj_t * obj, const char * bullet);
/**
* Configure the text area to one line or back to normal
* @param obj pointer to a text area object
* @param en true: one line, false: normal
*/
void lv_textarea_set_one_line(lv_obj_t * obj, bool en);
/**
* Set a list of characters. Only these characters will be accepted by the text area
* @param obj pointer to a text area object
* @param list list of characters. Only the pointer is saved. E.g. "+-.,0123456789"
*/
void lv_textarea_set_accepted_chars(lv_obj_t * obj, const char * list);
/**
* Set max length of a Text Area.
* @param obj pointer to a text area object
* @param num the maximal number of characters can be added (`lv_textarea_set_text` ignores it)
*/
void lv_textarea_set_max_length(lv_obj_t * obj, uint32_t num);
/**
* In `LV_EVENT_INSERT` the text which planned to be inserted can be replaced by an other text.
* It can be used to add automatic formatting to the text area.
* @param obj pointer to a text area object
* @param txt pointer to a new string to insert. If `""` no text will be added.
* The variable must be live after the `event_cb` exists. (Should be `global` or `static`)
*/
void lv_textarea_set_insert_replace(lv_obj_t * obj, const char * txt);
/**
* Enable/disable selection mode.
* @param obj pointer to a text area object
* @param en true or false to enable/disable selection mode
*/
void lv_textarea_set_text_selection(lv_obj_t * obj, bool en);
/**
* Set how long show the password before changing it to '*'
* @param obj pointer to a text area object
* @param time show time in milliseconds. 0: hide immediately.
*/
void lv_textarea_set_password_show_time(lv_obj_t * obj, uint16_t time);
/**
* Deprecated: use the normal text_align style property instead
* Set the label's alignment.
* It sets where the label is aligned (in one line mode it can be smaller than the text area)
* and how the lines of the area align in case of multiline text area
* @param obj pointer to a text area object
* @param align the align mode from ::lv_text_align_t
*/
void lv_textarea_set_align(lv_obj_t * obj, lv_text_align_t align);
/*=====================
* Getter functions
*====================*/
/**
* Get the text of a text area. In password mode it gives the real text (not '*'s).
* @param obj pointer to a text area object
* @return pointer to the text
*/
const char * lv_textarea_get_text(const lv_obj_t * obj);
/**
* Get the placeholder text of a text area
* @param obj pointer to a text area object
* @return pointer to the text
*/
const char * lv_textarea_get_placeholder_text(lv_obj_t * obj);
/**
* Get the label of a text area
* @param obj pointer to a text area object
* @return pointer to the label object
*/
lv_obj_t * lv_textarea_get_label(const lv_obj_t * obj);
/**
* Get the current cursor position in character index
* @param obj pointer to a text area object
* @return the cursor position
*/
uint32_t lv_textarea_get_cursor_pos(const lv_obj_t * obj);
/**
* Get whether the cursor click positioning is enabled or not.
* @param obj pointer to a text area object
* @return true: enable click positions; false: disable
*/
bool lv_textarea_get_cursor_click_pos(lv_obj_t * obj);
/**
* Get the password mode attribute
* @param obj pointer to a text area object
* @return true: password mode is enabled, false: disabled
*/
bool lv_textarea_get_password_mode(const lv_obj_t * obj);
/**
* Get the replacement characters to show in password mode
* @param obj pointer to a text area object
* @return pointer to the replacement text
*/
const char * lv_textarea_get_password_bullet(lv_obj_t * obj);
/**
* Get the one line configuration attribute
* @param obj pointer to a text area object
* @return true: one line configuration is enabled, false: disabled
*/
bool lv_textarea_get_one_line(const lv_obj_t * obj);
/**
* Get a list of accepted characters.
* @param obj pointer to a text area object
* @return list of accented characters.
*/
const char * lv_textarea_get_accepted_chars(lv_obj_t * obj);
/**
* Get max length of a Text Area.
* @param obj pointer to a text area object
* @return the maximal number of characters to be add
*/
uint32_t lv_textarea_get_max_length(lv_obj_t * obj);
/**
* Find whether text is selected or not.
* @param obj pointer to a text area object
* @return whether text is selected or not
*/
bool lv_textarea_text_is_selected(const lv_obj_t * obj);
/**
* Find whether selection mode is enabled.
* @param obj pointer to a text area object
* @return true: selection mode is enabled, false: disabled
*/
bool lv_textarea_get_text_selection(lv_obj_t * obj);
/**
* Set how long show the password before changing it to '*'
* @param obj pointer to a text area object
* @return show time in milliseconds. 0: hide immediately.
*/
uint16_t lv_textarea_get_password_show_time(lv_obj_t * obj);
/*=====================
* Other functions
*====================*/
/**
* Clear the selection on the text area.
* @param obj pointer to a text area object
*/
void lv_textarea_clear_selection(lv_obj_t * obj);
/**
* Move the cursor one character right
* @param obj pointer to a text area object
*/
void lv_textarea_cursor_right(lv_obj_t * obj);
/**
* Move the cursor one character left
* @param obj pointer to a text area object
*/
void lv_textarea_cursor_left(lv_obj_t * obj);
/**
* Move the cursor one line down
* @param obj pointer to a text area object
*/
void lv_textarea_cursor_down(lv_obj_t * obj);
/**
* Move the cursor one line up
* @param obj pointer to a text area object
*/
void lv_textarea_cursor_up(lv_obj_t * obj);
/**********************
* MACROS
**********************/
#endif /*LV_USE_TEXTAREA_H*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_TEXTAREA_H*/

View File

@@ -0,0 +1,20 @@
CSRCS += lv_arc.c
CSRCS += lv_bar.c
CSRCS += lv_btn.c
CSRCS += lv_btnmatrix.c
CSRCS += lv_canvas.c
CSRCS += lv_checkbox.c
CSRCS += lv_dropdown.c
CSRCS += lv_img.c
CSRCS += lv_label.c
CSRCS += lv_line.c
CSRCS += lv_roller.c
CSRCS += lv_slider.c
CSRCS += lv_switch.c
CSRCS += lv_table.c
CSRCS += lv_textarea.c
DEPPATH += --dep-path $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/widgets
VPATH += :$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/widgets
CFLAGS += "-I$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/widgets"