Introducing Bootloader Development Kit (BDK)

BDK will allow developers to use the full collection of drivers,
with limited editing, if any, for making payloads for Nintendo Switch.

Using a single source for everything will also help decoupling
Switch specific code and easily port it to other Tegra X1/X1+ platforms.
And maybe even to lower targets.

Everything is now centrilized into bdk folder.
Every module or project can utilize it by simply including it.

This is just the start and it will continue to improve.
This commit is contained in:
CTCaer
2020-06-14 15:25:21 +03:00
parent 9b1c61fbcf
commit 185526d134
386 changed files with 0 additions and 31374 deletions

View File

@@ -0,0 +1,310 @@
/**
* @file lv_arc.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_arc.h"
#if USE_LV_ARC != 0
#include "../lv_misc/lv_math.h"
#include "../lv_draw/lv_draw_arc.h"
#include "../lv_themes/lv_theme.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static bool lv_arc_design(lv_obj_t * arc, const lv_area_t * mask, lv_design_mode_t mode);
static lv_res_t lv_arc_signal(lv_obj_t * arc, lv_signal_t sign, void * param);
/**********************
* STATIC VARIABLES
**********************/
static lv_signal_func_t ancestor_signal;
static lv_design_func_t ancestor_design;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Create a arc object
* @param par pointer to an object, it will be the parent of the new arc
* @param copy pointer to a arc object, if not NULL then the new object will be copied from it
* @return pointer to the created arc
*/
lv_obj_t * lv_arc_create(lv_obj_t * par, const lv_obj_t * copy)
{
LV_LOG_TRACE("arc create started");
/*Create the ancestor of arc*/
lv_obj_t * new_arc = lv_obj_create(par, copy);
lv_mem_assert(new_arc);
if(new_arc == NULL) return NULL;
/*Allocate the arc type specific extended data*/
lv_arc_ext_t * ext = lv_obj_allocate_ext_attr(new_arc, sizeof(lv_arc_ext_t));
lv_mem_assert(ext);
if(ext == NULL) return NULL;
if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_arc);
if(ancestor_design == NULL) ancestor_design = lv_obj_get_design_func(new_arc);
/*Initialize the allocated 'ext' */
ext->angle_start = 45;
ext->angle_end = 315;
/*The signal and design functions are not copied so set them here*/
lv_obj_set_signal_func(new_arc, lv_arc_signal);
lv_obj_set_design_func(new_arc, lv_arc_design);
/*Init the new arc arc*/
if(copy == NULL) {
/*Set the default styles*/
lv_theme_t * th = lv_theme_get_current();
if(th) {
lv_arc_set_style(new_arc, LV_ARC_STYLE_MAIN, th->arc);
} else {
lv_arc_set_style(new_arc, LV_ARC_STYLE_MAIN, &lv_style_plain_color);
}
}
/*Copy an existing arc*/
else {
lv_arc_ext_t * copy_ext = lv_obj_get_ext_attr(copy);
ext->angle_start = copy_ext->angle_start;
ext->angle_end = copy_ext->angle_end;
/*Refresh the style with new signal function*/
lv_obj_refresh_style(new_arc);
}
LV_LOG_INFO("arc created");
return new_arc;
}
/*======================
* Add/remove functions
*=====================*/
/*
* New object specific "add" or "remove" functions come here
*/
/*=====================
* Setter functions
*====================*/
/**
* Set the start and end angles of an arc. 0 deg: bottom, 90 deg: right etc.
* @param arc pointer to an arc object
* @param start the start angle [0..360]
* @param end the end angle [0..360]
*/
void lv_arc_set_angles(lv_obj_t * arc, uint16_t start, uint16_t end)
{
lv_arc_ext_t * ext = lv_obj_get_ext_attr(arc);
if(start > 360) start = 360;
if(end > 360) end = 360;
ext->angle_start = start;
ext->angle_end = end;
lv_obj_invalidate(arc);
}
/**
* Set a style of a arc.
* @param arc pointer to arc object
* @param type which style should be set
* @param style pointer to a style
* */
void lv_arc_set_style(lv_obj_t * arc, lv_arc_style_t type, lv_style_t * style)
{
switch(type) {
case LV_ARC_STYLE_MAIN:
lv_obj_set_style(arc, style);
break;
}
}
/*=====================
* Getter functions
*====================*/
/**
* Get the start angle of an arc.
* @param arc pointer to an arc object
* @return the start angle [0..360]
*/
uint16_t lv_arc_get_angle_start(lv_obj_t * arc)
{
lv_arc_ext_t * ext = lv_obj_get_ext_attr(arc);
return ext->angle_start;
}
/**
* Get the end angle of an arc.
* @param arc pointer to an arc object
* @return the end angle [0..360]
*/
uint16_t lv_arc_get_angle_end(lv_obj_t * arc)
{
lv_arc_ext_t * ext = lv_obj_get_ext_attr(arc);
return ext->angle_end;
}
/**
* Get style of a arc.
* @param arc pointer to arc object
* @param type which style should be get
* @return style pointer to the style
* */
lv_style_t * lv_arc_get_style(const lv_obj_t * arc, lv_arc_style_t type)
{
lv_style_t * style = NULL;
switch(type) {
case LV_ARC_STYLE_MAIN:
style = lv_obj_get_style(arc);
break;
default:
style = NULL;
break;
}
return style;
}
/*=====================
* Other functions
*====================*/
/*
* New object specific "other" functions come here
*/
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Handle the drawing related tasks of the arcs
* @param arc pointer to an object
* @param mask the object will be drawn only in this area
* @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area
* (return 'true' if yes)
* LV_DESIGN_DRAW: draw the object (always return 'true')
* LV_DESIGN_DRAW_POST: drawing after every children are drawn
* @param return true/false, depends on 'mode'
*/
static bool lv_arc_design(lv_obj_t * arc, const lv_area_t * mask, lv_design_mode_t mode)
{
/*Return false if the object is not covers the mask_p area*/
if(mode == LV_DESIGN_COVER_CHK) {
return false;
}
/*Draw the object*/
else if(mode == LV_DESIGN_DRAW_MAIN) {
lv_arc_ext_t * ext = lv_obj_get_ext_attr(arc);
lv_style_t * style = lv_arc_get_style(arc, LV_ARC_STYLE_MAIN);
lv_coord_t r = (LV_MATH_MIN(lv_obj_get_width(arc), lv_obj_get_height(arc))) / 2;
lv_coord_t x = arc->coords.x1 + lv_obj_get_width(arc) / 2;
lv_coord_t y = arc->coords.y1 + lv_obj_get_height(arc) / 2;
lv_opa_t opa_scale = lv_obj_get_opa_scale(arc);
lv_draw_arc(x, y, r, mask, ext->angle_start, ext->angle_end, style, opa_scale);
/*Draw circle on the ends if enabled */
if(style->line.rounded) {
lv_coord_t thick_half = style->line.width / 2;
lv_coord_t cir_x = ((r - thick_half) * lv_trigo_sin(ext->angle_start) >> LV_TRIGO_SHIFT);
lv_coord_t cir_y = ((r - thick_half) * lv_trigo_sin(ext->angle_start + 90) >> LV_TRIGO_SHIFT);
lv_style_t cir_style;
lv_style_copy(&cir_style, &lv_style_plain);
cir_style.body.grad_color = style->line.color;
cir_style.body.main_color = cir_style.body.grad_color;
cir_style.body.radius = LV_RADIUS_CIRCLE;
lv_area_t cir_area;
cir_area.x1 = cir_x + x - thick_half;
cir_area.y1 = cir_y + y - thick_half;
cir_area.x2 = cir_x + x + thick_half;
cir_area.y2 = cir_y + y + thick_half;
lv_draw_rect(&cir_area, mask, &cir_style, opa_scale);
cir_x = ((r - thick_half) * lv_trigo_sin(ext->angle_end) >> LV_TRIGO_SHIFT);
cir_y = ((r - thick_half) * lv_trigo_sin(ext->angle_end + 90) >> LV_TRIGO_SHIFT);
cir_area.x1 = cir_x + x - thick_half;
cir_area.y1 = cir_y + y - thick_half;
cir_area.x2 = cir_x + x + thick_half;
cir_area.y2 = cir_y + y + thick_half;
lv_draw_rect(&cir_area, mask, &cir_style, opa_scale);
}
}
/*Post draw when the children are drawn*/
else if(mode == LV_DESIGN_DRAW_POST) {
}
return true;
}
/**
* Signal function of the arc
* @param arc pointer to a arc object
* @param sign a signal type from lv_signal_t enum
* @param param pointer to a signal specific variable
* @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
*/
static lv_res_t lv_arc_signal(lv_obj_t * arc, lv_signal_t sign, void * param)
{
lv_res_t res;
/* Include the ancient signal function */
res = ancestor_signal(arc, sign, param);
if(res != LV_RES_OK) return res;
if(sign == LV_SIGNAL_CLEANUP) {
/*Nothing to cleanup. (No dynamically allocated memory in 'ext')*/
} else if(sign == LV_SIGNAL_GET_TYPE) {
lv_obj_type_t * buf = param;
uint8_t i;
for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/
if(buf->type[i] == NULL) break;
}
buf->type[i] = "lv_arc";
}
return res;
}
#endif

View File

@@ -0,0 +1,127 @@
/**
* @file lv_arc.h
*
*/
#ifndef LV_ARC_H
#define LV_ARC_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#ifdef LV_CONF_INCLUDE_SIMPLE
#include "lv_conf.h"
#else
#include "../../lv_conf.h"
#endif
#if USE_LV_ARC != 0
#include "../lv_core/lv_obj.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/*Data of arc*/
typedef struct {
/*New data for this type */
lv_coord_t angle_start;
lv_coord_t angle_end;
} lv_arc_ext_t;
/*Styles*/
enum {
LV_ARC_STYLE_MAIN,
};
typedef uint8_t lv_arc_style_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a arc objects
* @param par pointer to an object, it will be the parent of the new arc
* @param copy pointer to a arc object, if not NULL then the new object will be copied from it
* @return pointer to the created arc
*/
lv_obj_t * lv_arc_create(lv_obj_t * par, const lv_obj_t * copy);
/*======================
* Add/remove functions
*=====================*/
/*=====================
* Setter functions
*====================*/
/**
* Set the start and end angles of an arc. 0 deg: bottom, 90 deg: right etc.
* @param arc pointer to an arc object
* @param start the start angle [0..360]
* @param end the end angle [0..360]
*/
void lv_arc_set_angles(lv_obj_t * arc, uint16_t start, uint16_t end);
/**
* Set a style of a arc.
* @param arc pointer to arc object
* @param type which style should be set
* @param style pointer to a style
* */
void lv_arc_set_style(lv_obj_t * arc, lv_arc_style_t type, lv_style_t *style);
/*=====================
* Getter functions
*====================*/
/**
* Get the start angle of an arc.
* @param arc pointer to an arc object
* @return the start angle [0..360]
*/
uint16_t lv_arc_get_angle_start(lv_obj_t * arc);
/**
* Get the end angle of an arc.
* @param arc pointer to an arc object
* @return the end angle [0..360]
*/
uint16_t lv_arc_get_angle_end(lv_obj_t * arc);
/**
* Get style of a arc.
* @param arc pointer to arc object
* @param type which style should be get
* @return style pointer to the style
* */
lv_style_t * lv_arc_get_style(const lv_obj_t * arc, lv_arc_style_t type);
/*=====================
* Other functions
*====================*/
/**********************
* MACROS
**********************/
#endif /*USE_LV_ARC*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_ARC_H*/

View File

@@ -0,0 +1,429 @@
/**
* @file lv_bar.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_bar.h"
#if USE_LV_BAR != 0
#include "../lv_draw/lv_draw.h"
#include "../lv_themes/lv_theme.h"
#include "../lv_misc/lv_anim.h"
#include <stdio.h>
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static bool lv_bar_design(lv_obj_t * bar, const lv_area_t * mask, lv_design_mode_t mode);
static lv_res_t lv_bar_signal(lv_obj_t * bar, lv_signal_t sign, void * param);
/**********************
* STATIC VARIABLES
**********************/
static lv_design_func_t ancestor_design_f;
static lv_signal_func_t ancestor_signal;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Create a bar objects
* @param par pointer to an object, it will be the parent of the new bar
* @param copy pointer to a bar object, if not NULL then the new object will be copied from it
* @return pointer to the created bar
*/
lv_obj_t * lv_bar_create(lv_obj_t * par, const lv_obj_t * copy)
{
LV_LOG_TRACE("lv_bar create started");
/*Create the ancestor basic object*/
lv_obj_t * new_bar = lv_obj_create(par, copy);
lv_mem_assert(new_bar);
if(new_bar == NULL) return NULL;
if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_bar);
if(ancestor_design_f == NULL) ancestor_design_f = lv_obj_get_design_func(new_bar);
/*Allocate the object type specific extended data*/
lv_bar_ext_t * ext = lv_obj_allocate_ext_attr(new_bar, sizeof(lv_bar_ext_t));
lv_mem_assert(ext);
if(ext == NULL) return NULL;
ext->min_value = 0;
ext->max_value = 100;
ext->cur_value = 0;
ext->sym = 0;
ext->style_indic = &lv_style_pretty_color;
lv_obj_set_signal_func(new_bar, lv_bar_signal);
lv_obj_set_design_func(new_bar, lv_bar_design);
/*Init the new bar object*/
if(copy == NULL) {
lv_obj_set_click(new_bar, false);
lv_obj_set_size(new_bar, LV_DPI * 2, LV_DPI / 3);
lv_bar_set_value(new_bar, ext->cur_value);
lv_theme_t * th = lv_theme_get_current();
if(th) {
lv_bar_set_style(new_bar, LV_BAR_STYLE_BG, th->bar.bg);
lv_bar_set_style(new_bar, LV_BAR_STYLE_INDIC, th->bar.indic);
} else {
lv_obj_set_style(new_bar, &lv_style_pretty);
}
} else {
lv_bar_ext_t * ext_copy = lv_obj_get_ext_attr(copy);
ext->min_value = ext_copy->min_value;
ext->max_value = ext_copy->max_value;
ext->cur_value = ext_copy->cur_value;
ext->style_indic = ext_copy->style_indic;
ext->sym = ext_copy->sym;
/*Refresh the style with new signal function*/
lv_obj_refresh_style(new_bar);
lv_bar_set_value(new_bar, ext->cur_value);
}
LV_LOG_INFO("bar created");
return new_bar;
}
/*=====================
* Setter functions
*====================*/
/**
* Set a new value on the bar
* @param bar pointer to a bar object
* @param value new value
*/
void lv_bar_set_value(lv_obj_t * bar, int16_t value)
{
lv_bar_ext_t * ext = lv_obj_get_ext_attr(bar);
if(ext->cur_value == value) return;
ext->cur_value = value > ext->max_value ? ext->max_value : value;
ext->cur_value = ext->cur_value < ext->min_value ? ext->min_value : ext->cur_value;
lv_obj_invalidate(bar);
}
#if USE_LV_ANIMATION
/**
* Set a new value with animation on the bar
* @param bar pointer to a bar object
* @param value new value
* @param anim_time animation time in milliseconds
*/
void lv_bar_set_value_anim(lv_obj_t * bar, int16_t value, uint16_t anim_time)
{
lv_bar_ext_t * ext = lv_obj_get_ext_attr(bar);
if(ext->cur_value == value) return;
int16_t new_value;
new_value = value > ext->max_value ? ext->max_value : value;
new_value = new_value < ext->min_value ? ext->min_value : new_value;
lv_anim_t a;
a.var = bar;
a.start = ext->cur_value;
a.end = new_value;
a.fp = (lv_anim_fp_t)lv_bar_set_value;
a.path = lv_anim_path_linear;
a.end_cb = NULL;
a.act_time = 0;
a.time = anim_time;
a.playback = 0;
a.playback_pause = 0;
a.repeat = 0;
a.repeat_pause = 0;
lv_anim_create(&a);
}
#endif
/**
* Set minimum and the maximum values of a bar
* @param bar pointer to the bar object
* @param min minimum value
* @param max maximum value
*/
void lv_bar_set_range(lv_obj_t * bar, int16_t min, int16_t max)
{
lv_bar_ext_t * ext = lv_obj_get_ext_attr(bar);
if(ext->min_value == min && ext->max_value == max) return;
ext->max_value = max;
ext->min_value = min;
if(ext->cur_value > max) {
ext->cur_value = max;
lv_bar_set_value(bar, ext->cur_value);
}
if(ext->cur_value < min) {
ext->cur_value = min;
lv_bar_set_value(bar, ext->cur_value);
}
lv_obj_invalidate(bar);
}
/**
* Make the bar symmetric to zero. The indicator will grow from zero instead of the minimum position.
* @param bar pointer to a bar object
* @param en true: enable disable symmetric behavior; false: disable
*/
void lv_bar_set_sym(lv_obj_t * bar, bool en)
{
lv_bar_ext_t * ext = lv_obj_get_ext_attr(bar);
ext->sym = en ? 1 : 0;
}
/**
* Set a style of a bar
* @param bar pointer to a bar object
* @param type which style should be set
* @param style pointer to a style
*/
void lv_bar_set_style(lv_obj_t * bar, lv_bar_style_t type, lv_style_t * style)
{
lv_bar_ext_t * ext = lv_obj_get_ext_attr(bar);
switch(type) {
case LV_BAR_STYLE_BG:
lv_obj_set_style(bar, style);
break;
case LV_BAR_STYLE_INDIC:
ext->style_indic = style;
lv_obj_refresh_ext_size(bar);
break;
}
}
/*=====================
* Getter functions
*====================*/
/**
* Get the value of a bar
* @param bar pointer to a bar object
* @return the value of the bar
*/
int16_t lv_bar_get_value(const lv_obj_t * bar)
{
lv_bar_ext_t * ext = lv_obj_get_ext_attr(bar);
return ext->cur_value;
}
/**
* Get the minimum value of a bar
* @param bar pointer to a bar object
* @return the minimum value of the bar
*/
int16_t lv_bar_get_min_value(const lv_obj_t * bar)
{
lv_bar_ext_t * ext = lv_obj_get_ext_attr(bar);
return ext->min_value;
}
/**
* Get the maximum value of a bar
* @param bar pointer to a bar object
* @return the maximum value of the bar
*/
int16_t lv_bar_get_max_value(const lv_obj_t * bar)
{
lv_bar_ext_t * ext = lv_obj_get_ext_attr(bar);
return ext->max_value;
}
/**
* Get whether the bar is symmetric or not.
* @param bar pointer to a bar object
* @return true: symmetric is enabled; false: disable
*/
bool lv_bar_get_sym(lv_obj_t * bar)
{
lv_bar_ext_t * ext = lv_obj_get_ext_attr(bar);
return ext->sym ? true : false;
}
/**
* Get a style of a bar
* @param bar pointer to a bar object
* @param type which style should be get
* @return style pointer to a style
*/
lv_style_t * lv_bar_get_style(const lv_obj_t * bar, lv_bar_style_t type)
{
lv_style_t * style = NULL;
lv_bar_ext_t * ext = lv_obj_get_ext_attr(bar);
switch(type) {
case LV_BAR_STYLE_BG:
style = lv_obj_get_style(bar);
break;
case LV_BAR_STYLE_INDIC:
style = ext->style_indic;
break;
default:
style = NULL;
break;
}
return style;
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Handle the drawing related tasks of the bars
* @param bar pointer to an object
* @param mask the object will be drawn only in this area
* @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area
* (return 'true' if yes)
* LV_DESIGN_DRAW: draw the object (always return 'true')
* LV_DESIGN_DRAW_POST: drawing after every children are drawn
* @param return true/false, depends on 'mode'
*/
static bool lv_bar_design(lv_obj_t * bar, const lv_area_t * mask, lv_design_mode_t mode)
{
if(mode == LV_DESIGN_COVER_CHK) {
/*Return false if the object is not covers the mask area*/
return ancestor_design_f(bar, mask, mode);
} else if(mode == LV_DESIGN_DRAW_MAIN) {
lv_opa_t opa_scale = lv_obj_get_opa_scale(bar);
#if USE_LV_GROUP == 0
ancestor_design_f(bar, mask, mode);
#else
/* Draw the borders later if the bar is focused.
* At value = 100% the indicator can cover to whole background and the focused style won't be visible*/
if(lv_obj_is_focused(bar)) {
lv_style_t * style_bg = lv_bar_get_style(bar, LV_BAR_STYLE_BG);
lv_style_t style_tmp;
lv_style_copy(&style_tmp, style_bg);
style_tmp.body.border.width = 0;
lv_draw_rect(&bar->coords, mask, &style_tmp, opa_scale);
} else {
ancestor_design_f(bar, mask, mode);
}
#endif
lv_bar_ext_t * ext = lv_obj_get_ext_attr(bar);
if(ext->cur_value != ext->min_value || ext->sym) {
lv_style_t * style_indic = lv_bar_get_style(bar, LV_BAR_STYLE_INDIC);
lv_area_t indic_area;
lv_area_copy(&indic_area, &bar->coords);
indic_area.x1 += style_indic->body.padding.hor;
indic_area.x2 -= style_indic->body.padding.hor;
indic_area.y1 += style_indic->body.padding.ver;
indic_area.y2 -= style_indic->body.padding.ver;
lv_coord_t w = lv_area_get_width(&indic_area);
lv_coord_t h = lv_area_get_height(&indic_area);
if(w >= h) {
/*Horizontal*/
indic_area.x2 = (int32_t)((int32_t)w * (ext->cur_value - ext->min_value)) / (ext->max_value - ext->min_value);
indic_area.x2 = indic_area.x1 + indic_area.x2 - 1;
if(ext->sym && ext->min_value < 0 && ext->max_value > 0) {
/*Calculate the coordinate of the zero point*/
lv_coord_t zero;
zero = indic_area.x1 + (-ext->min_value * w) / (ext->max_value - ext->min_value);
if(indic_area.x2 > zero) indic_area.x1 = zero;
else {
indic_area.x1 = indic_area.x2;
indic_area.x2 = zero;
}
}
} else {
indic_area.y1 = (int32_t)((int32_t)h * (ext->cur_value - ext->min_value)) / (ext->max_value - ext->min_value);
indic_area.y1 = indic_area.y2 - indic_area.y1 + 1;
if(ext->sym && ext->min_value < 0 && ext->max_value > 0) {
/*Calculate the coordinate of the zero point*/
lv_coord_t zero;
zero = indic_area.y2 - (-ext->min_value * h) / (ext->max_value - ext->min_value);
if(indic_area.y1 < zero) indic_area.y2 = zero;
else {
indic_area.y2 = indic_area.y1;
indic_area.y1 = zero;
}
}
}
/*Draw the indicator*/
lv_draw_rect(&indic_area, mask, style_indic, opa_scale);
}
} else if(mode == LV_DESIGN_DRAW_POST) {
#if USE_LV_GROUP
/*Draw the border*/
if(lv_obj_is_focused(bar)) {
lv_opa_t opa_scale = lv_obj_get_opa_scale(bar);
lv_style_t * style_bg = lv_bar_get_style(bar, LV_BAR_STYLE_BG);
lv_style_t style_tmp;
lv_style_copy(&style_tmp, style_bg);
style_tmp.body.empty = 1;
style_tmp.body.shadow.width = 0;
lv_draw_rect(&bar->coords, mask, &style_tmp, opa_scale);
}
#endif
}
return true;
}
/**
* Signal function of the bar
* @param bar pointer to a bar object
* @param sign a signal type from lv_signal_t enum
* @param param pointer to a signal specific variable
* @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
*/
static lv_res_t lv_bar_signal(lv_obj_t * bar, lv_signal_t sign, void * param)
{
lv_res_t res;
/* Include the ancient signal function */
res = ancestor_signal(bar, sign, param);
if(res != LV_RES_OK) return res;
if(sign == LV_SIGNAL_REFR_EXT_SIZE) {
lv_style_t * style_indic = lv_bar_get_style(bar, LV_BAR_STYLE_INDIC);
if(style_indic->body.shadow.width > bar->ext_size) bar->ext_size = style_indic->body.shadow.width;
} else if(sign == LV_SIGNAL_GET_TYPE) {
lv_obj_type_t * buf = param;
uint8_t i;
for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/
if(buf->type[i] == NULL) break;
}
buf->type[i] = "lv_bar";
}
return res;
}
#endif

View File

@@ -0,0 +1,160 @@
/**
* @file lv_bar.h
*
*/
#ifndef LV_BAR_H
#define LV_BAR_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#ifdef LV_CONF_INCLUDE_SIMPLE
#include "lv_conf.h"
#else
#include "../../lv_conf.h"
#endif
#if USE_LV_BAR != 0
#include "../lv_core/lv_obj.h"
#include "lv_cont.h"
#include "lv_btn.h"
#include "lv_label.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/*Data of bar*/
typedef struct
{
/*No inherited ext*/ /*Ext. of ancestor*/
/*New data for this type */
int16_t cur_value; /*Current value of the bar*/
int16_t min_value; /*Minimum value of the bar*/
int16_t max_value; /*Maximum value of the bar*/
uint8_t sym :1; /*Symmetric: means the center is around zero value*/
lv_style_t *style_indic; /*Style of the indicator*/
} lv_bar_ext_t;
enum {
LV_BAR_STYLE_BG,
LV_BAR_STYLE_INDIC,
};
typedef uint8_t lv_bar_style_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a bar objects
* @param par pointer to an object, it will be the parent of the new bar
* @param copy pointer to a bar object, if not NULL then the new object will be copied from it
* @return pointer to the created bar
*/
lv_obj_t * lv_bar_create(lv_obj_t * par, const lv_obj_t * copy);
/*=====================
* Setter functions
*====================*/
/**
* Set a new value on the bar
* @param bar pointer to a bar object
* @param value new value
*/
void lv_bar_set_value(lv_obj_t * bar, int16_t value);
/**
* Set a new value with animation on the bar
* @param bar pointer to a bar object
* @param value new value
* @param anim_time animation time in milliseconds
*/
void lv_bar_set_value_anim(lv_obj_t * bar, int16_t value, uint16_t anim_time);
/**
* Set minimum and the maximum values of a bar
* @param bar pointer to the bar object
* @param min minimum value
* @param max maximum value
*/
void lv_bar_set_range(lv_obj_t * bar, int16_t min, int16_t max);
/**
* Make the bar symmetric to zero. The indicator will grow from zero instead of the minimum position.
* @param bar pointer to a bar object
* @param en true: enable disable symmetric behavior; false: disable
*/
void lv_bar_set_sym(lv_obj_t * bar, bool en);
/**
* Set a style of a bar
* @param bar pointer to a bar object
* @param type which style should be set
* @param style pointer to a style
*/
void lv_bar_set_style(lv_obj_t *bar, lv_bar_style_t type, lv_style_t *style);
/*=====================
* Getter functions
*====================*/
/**
* Get the value of a bar
* @param bar pointer to a bar object
* @return the value of the bar
*/
int16_t lv_bar_get_value(const lv_obj_t * bar);
/**
* Get the minimum value of a bar
* @param bar pointer to a bar object
* @return the minimum value of the bar
*/
int16_t lv_bar_get_min_value(const lv_obj_t * bar);
/**
* Get the maximum value of a bar
* @param bar pointer to a bar object
* @return the maximum value of the bar
*/
int16_t lv_bar_get_max_value(const lv_obj_t * bar);
/**
* Get whether the bar is symmetric or not.
* @param bar pointer to a bar object
* @return true: symmetric is enabled; false: disable
*/
bool lv_bar_get_sym(lv_obj_t * bar);
/**
* Get a style of a bar
* @param bar pointer to a bar object
* @param type which style should be get
* @return style pointer to a style
*/
lv_style_t * lv_bar_get_style(const lv_obj_t *bar, lv_bar_style_t type);
/**********************
* MACROS
**********************/
#endif /*USE_LV_BAR*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_BAR_H*/

View File

@@ -0,0 +1,763 @@
/**
* @file lv_btn.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_btn.h"
#if USE_LV_BTN != 0
#include <string.h>
#include "../lv_core/lv_group.h"
#include "../lv_draw/lv_draw.h"
#include "../lv_themes/lv_theme.h"
#include "../lv_misc/lv_area.h"
#include "../lv_misc/lv_color.h"
#include "../lv_misc/lv_math.h"
/*********************
* DEFINES
*********************/
#define LV_BTN_INK_VALUE_MAX 256
#define LV_BTN_INK_VALUE_MAX_SHIFT 8
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static bool lv_btn_design(lv_obj_t * btn, const lv_area_t * mask, lv_design_mode_t mode);
static lv_res_t lv_btn_signal(lv_obj_t * btn, lv_signal_t sign, void * param);
#if USE_LV_ANIMATION && LV_BTN_INK_EFFECT
static void lv_btn_ink_effect_anim(lv_obj_t * btn, int32_t val);
static void lv_btn_ink_effect_anim_ready(void * p);
#endif
/**********************
* STATIC VARIABLES
**********************/
static lv_signal_func_t ancestor_signal;
static lv_design_func_t ancestor_design;
#if USE_LV_ANIMATION && LV_BTN_INK_EFFECT
static lv_coord_t ink_act_value;
static lv_obj_t * ink_obj;
static lv_btn_state_t ink_bg_state;
static lv_btn_state_t ink_top_state;
static bool ink_ready;
static bool ink_playback;
static lv_point_t ink_point;
#endif
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Create a button objects
* @param par pointer to an object, it will be the parent of the new button
* @param copy pointer to a button object, if not NULL then the new object will be copied from it
* @return pointer to the created button
*/
lv_obj_t * lv_btn_create(lv_obj_t * par, const lv_obj_t * copy)
{
LV_LOG_TRACE("button create started");
lv_obj_t * new_btn;
new_btn = lv_cont_create(par, copy);
lv_mem_assert(new_btn);
if(new_btn == NULL) return NULL;
if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_btn);
if(ancestor_design == NULL) ancestor_design = lv_obj_get_design_func(new_btn);
/*Allocate the extended data*/
lv_btn_ext_t * ext = lv_obj_allocate_ext_attr(new_btn, sizeof(lv_btn_ext_t));
lv_mem_assert(ext);
if(ext == NULL) return NULL;
ext->state = LV_BTN_STATE_REL;
ext->actions[LV_BTN_ACTION_PR] = NULL;
ext->actions[LV_BTN_ACTION_CLICK] = NULL;
ext->actions[LV_BTN_ACTION_LONG_PR] = NULL;
ext->actions[LV_BTN_ACTION_LONG_PR_REPEAT] = NULL;
ext->styles[LV_BTN_STATE_REL] = &lv_style_btn_rel;
ext->styles[LV_BTN_STATE_PR] = &lv_style_btn_pr;
ext->styles[LV_BTN_STATE_TGL_REL] = &lv_style_btn_tgl_rel;
ext->styles[LV_BTN_STATE_TGL_PR] = &lv_style_btn_tgl_pr;
ext->styles[LV_BTN_STATE_INA] = &lv_style_btn_ina;
ext->long_pr_action_executed = 0;
ext->toggle = 0;
ext->idx = 0;
#if USE_LV_ANIMATION && LV_BTN_INK_EFFECT
ext->ink_in_time = 0;
ext->ink_wait_time = 0;
ext->ink_out_time = 0;
#endif
lv_obj_set_signal_func(new_btn, lv_btn_signal);
lv_obj_set_design_func(new_btn, lv_btn_design);
/*If no copy do the basic initialization*/
if(copy == NULL) {
/*Set layout if the button is not a screen*/
if(par != NULL) {
lv_btn_set_layout(new_btn, LV_LAYOUT_CENTER);
}
lv_obj_set_click(new_btn, true); /*Be sure the button is clickable*/
/*Set the default styles*/
lv_theme_t * th = lv_theme_get_current();
if(th) {
lv_btn_set_style(new_btn, LV_BTN_STYLE_REL, th->btn.rel);
lv_btn_set_style(new_btn, LV_BTN_STYLE_PR, th->btn.pr);
lv_btn_set_style(new_btn, LV_BTN_STYLE_TGL_REL, th->btn.tgl_rel);
lv_btn_set_style(new_btn, LV_BTN_STYLE_TGL_PR, th->btn.tgl_pr);
lv_btn_set_style(new_btn, LV_BTN_STYLE_INA, th->btn.ina);
} else {
lv_obj_set_style(new_btn, ext->styles[LV_BTN_STATE_REL]);
}
}
/*Copy 'copy'*/
else {
lv_btn_ext_t * copy_ext = lv_obj_get_ext_attr(copy);
ext->state = copy_ext->state;
ext->toggle = copy_ext->toggle;
#if USE_LV_ANIMATION && LV_BTN_INK_EFFECT
ext->ink_in_time = copy_ext->ink_in_time;
ext->ink_wait_time = copy_ext->ink_wait_time;
ext->ink_out_time = copy_ext->ink_out_time;
#endif
memcpy(ext->actions, copy_ext->actions, sizeof(ext->actions));
memcpy(ext->styles, copy_ext->styles, sizeof(ext->styles));
/*Refresh the style with new signal function*/
lv_obj_refresh_style(new_btn);
}
LV_LOG_INFO("button created");
return new_btn;
}
/*=====================
* Setter functions
*====================*/
/**
* Enable the toggled states
* @param btn pointer to a button object
* @param tgl true: enable toggled states, false: disable
*/
void lv_btn_set_toggle(lv_obj_t * btn, bool tgl)
{
lv_btn_ext_t * ext = lv_obj_get_ext_attr(btn);
ext->toggle = tgl != false ? 1 : 0;
}
/**
* Set the state of the button
* @param btn pointer to a button object
* @param state the new state of the button (from lv_btn_state_t enum)
*/
void lv_btn_set_state(lv_obj_t * btn, lv_btn_state_t state)
{
lv_btn_ext_t * ext = lv_obj_get_ext_attr(btn);
if(ext->state != state) {
ext->state = state;
lv_obj_set_style(btn, ext->styles[state]);
}
}
/**
* Toggle the state of the button (ON->OFF, OFF->ON)
* @param btn pointer to a button object
*/
void lv_btn_toggle(lv_obj_t * btn)
{
lv_btn_ext_t * ext = lv_obj_get_ext_attr(btn);
switch(ext->state) {
case LV_BTN_STATE_REL:
lv_btn_set_state(btn, LV_BTN_STATE_TGL_REL);
break;
case LV_BTN_STATE_PR:
lv_btn_set_state(btn, LV_BTN_STATE_TGL_PR);
break;
case LV_BTN_STATE_TGL_REL:
lv_btn_set_state(btn, LV_BTN_STATE_REL);
break;
case LV_BTN_STATE_TGL_PR:
lv_btn_set_state(btn, LV_BTN_STATE_PR);
break;
default:
break;
}
}
/**
* Set a function to call when a button event happens
* @param btn pointer to a button object
* @param action type of event form 'lv_action_t' (press, release, long press, long press repeat)
*/
void lv_btn_set_action(lv_obj_t * btn, lv_btn_action_t type, lv_action_t action)
{
if(type >= LV_BTN_ACTION_NUM) return;
lv_btn_ext_t * ext = lv_obj_get_ext_attr(btn);
ext->actions[type] = action;
}
/**
* Set time of the ink effect (draw a circle on click to animate in the new state)
* @param btn pointer to a button object
* @param time the time of the ink animation
*/
void lv_btn_set_ink_in_time(lv_obj_t * btn, uint16_t time)
{
#if USE_LV_ANIMATION && LV_BTN_INK_EFFECT
lv_btn_ext_t * ext = lv_obj_get_ext_attr(btn);
ext->ink_in_time = time;
#else
(void)btn; /*Unused*/
(void)time; /*Unused*/
LV_LOG_WARN("`lv_btn_set_ink_ink_time` has no effect if LV_BTN_INK_EFEFCT or USE_LV_ANIMATION is disabled")
#endif
}
/**
* Set the wait time before the ink disappears
* @param btn pointer to a button object
* @param time the time of the ink animation
*/
void lv_btn_set_ink_wait_time(lv_obj_t * btn, uint16_t time)
{
#if USE_LV_ANIMATION && LV_BTN_INK_EFFECT
lv_btn_ext_t * ext = lv_obj_get_ext_attr(btn);
ext->ink_wait_time = time;
#else
(void)btn; /*Unused*/
(void)time; /*Unused*/
LV_LOG_WARN("`lv_btn_set_ink_wait_time` has no effect if LV_BTN_INK_EFEFCT or USE_LV_ANIMATION is disabled")
#endif
}
/**
* Set time of the ink out effect (animate to the released state)
* @param btn pointer to a button object
* @param time the time of the ink animation
*/
void lv_btn_set_ink_out_time(lv_obj_t * btn, uint16_t time)
{
#if USE_LV_ANIMATION && LV_BTN_INK_EFFECT
lv_btn_ext_t * ext = lv_obj_get_ext_attr(btn);
ext->ink_out_time = time;
#else
(void)btn; /*Unused*/
(void)time; /*Unused*/
LV_LOG_WARN("`lv_btn_set_ink_out_time` has no effect if LV_BTN_INK_EFEFCT or USE_LV_ANIMATION is disabled")
#endif
}
/**
* Set a style of a button
* @param btn pointer to a button object
* @param type which style should be set
* @param style pointer to a style
*/
void lv_btn_set_style(lv_obj_t * btn, lv_btn_style_t type, lv_style_t * style)
{
lv_btn_ext_t * ext = lv_obj_get_ext_attr(btn);
switch(type) {
case LV_BTN_STYLE_REL:
ext->styles[LV_BTN_STATE_REL] = style;
break;
case LV_BTN_STYLE_PR:
ext->styles[LV_BTN_STATE_PR] = style;
break;
case LV_BTN_STYLE_TGL_REL:
ext->styles[LV_BTN_STATE_TGL_REL] = style;
break;
case LV_BTN_STYLE_TGL_PR:
ext->styles[LV_BTN_STATE_TGL_PR] = style;
break;
case LV_BTN_STYLE_INA:
ext->styles[LV_BTN_STATE_INA] = style;
break;
}
/*Refresh the object with the new style*/
lv_obj_set_style(btn, ext->styles[ext->state]);
}
/*=====================
* Getter functions
*====================*/
/**
* Get the current state of the button
* @param btn pointer to a button object
* @return the state of the button (from lv_btn_state_t enum)
*/
lv_btn_state_t lv_btn_get_state(const lv_obj_t * btn)
{
lv_btn_ext_t * ext = lv_obj_get_ext_attr(btn);
return ext->state;
}
/**
* Get the toggle enable attribute of the button
* @param btn pointer to a button object
* @return ture: toggle enabled, false: disabled
*/
bool lv_btn_get_toggle(const lv_obj_t * btn)
{
lv_btn_ext_t * ext = lv_obj_get_ext_attr(btn);
return ext->toggle != 0 ? true : false;
}
/**
* Get the release action of a button
* @param btn pointer to a button object
* @return pointer to the release action function
*/
lv_action_t lv_btn_get_action(const lv_obj_t * btn, lv_btn_action_t type)
{
if(type >= LV_BTN_ACTION_NUM) return NULL;
lv_btn_ext_t * ext = lv_obj_get_ext_attr(btn);
return ext->actions[type];
}
/**
* Get time of the ink in effect (draw a circle on click to animate in the new state)
* @param btn pointer to a button object
* @return the time of the ink animation
*/
uint16_t lv_btn_get_ink_in_time(const lv_obj_t * btn)
{
#if USE_LV_ANIMATION && LV_BTN_INK_EFFECT
lv_btn_ext_t * ext = lv_obj_get_ext_attr(btn);
return ext->ink_in_time;
#else
(void)btn; /*Unused*/
return 0;
#endif
}
/**
* Get the wait time before the ink disappears
* @param btn pointer to a button object
* @return the time of the ink animation
*/
uint16_t lv_btn_get_ink_wait_time(const lv_obj_t * btn)
{
#if USE_LV_ANIMATION && LV_BTN_INK_EFFECT
lv_btn_ext_t * ext = lv_obj_get_ext_attr(btn);
return ext->ink_wait_time;
#else
(void)btn; /*Unused*/
return 0;
#endif
}
/**
* Get time of the ink out effect (animate to the releases state)
* @param btn pointer to a button object
* @return the time of the ink animation
*/
uint16_t lv_btn_get_ink_out_time(const lv_obj_t * btn)
{
#if USE_LV_ANIMATION && LV_BTN_INK_EFFECT
lv_btn_ext_t * ext = lv_obj_get_ext_attr(btn);
return ext->ink_in_time;
#else
(void)btn; /*Unused*/
return 0;
#endif
}
/**
* Get a style of a button
* @param btn pointer to a button object
* @param type which style should be get
* @return style pointer to a style
*/
lv_style_t * lv_btn_get_style(const lv_obj_t * btn, lv_btn_style_t type)
{
lv_style_t * style = NULL;
lv_btn_ext_t * ext = lv_obj_get_ext_attr(btn);
switch(type) {
case LV_BTN_STYLE_REL:
style = ext->styles[LV_BTN_STATE_REL];
break;
case LV_BTN_STYLE_PR:
style = ext->styles[LV_BTN_STATE_PR];
break;
case LV_BTN_STYLE_TGL_REL:
style = ext->styles[LV_BTN_STATE_TGL_REL];
break;
case LV_BTN_STYLE_TGL_PR:
style = ext->styles[LV_BTN_STATE_TGL_PR];
break;
case LV_BTN_STYLE_INA:
style = ext->styles[LV_BTN_STATE_INA];
break;
default:
style = NULL;
break;
}
return style;
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Handle the drawing related tasks of the drop down lists
* @param btn pointer to an object
* @param mask the object will be drawn only in this area
* @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area
* (return 'true' if yes)
* LV_DESIGN_DRAW: draw the object (always return 'true')
* LV_DESIGN_DRAW_POST: drawing after every children are drawn
* @param return true/false, depends on 'mode'
*/
static bool lv_btn_design(lv_obj_t * btn, const lv_area_t * mask, lv_design_mode_t mode)
{
if(mode == LV_DESIGN_COVER_CHK) {
return false;
} else if(mode == LV_DESIGN_DRAW_MAIN) {
#if USE_LV_ANIMATION && LV_BTN_INK_EFFECT
if(btn != ink_obj) {
ancestor_design(btn, mask, mode);
} else {
lv_opa_t opa_scale = lv_obj_get_opa_scale(btn);
lv_btn_ext_t * ext = lv_obj_get_ext_attr(btn);
/*Draw the normal button*/
if(ink_playback == false) {
lv_style_t style_tmp;
lv_style_copy(&style_tmp, ext->styles[ink_bg_state]);
style_tmp.body.shadow.width = ext->styles[ink_top_state]->body.shadow.width;
lv_draw_rect(&btn->coords, mask, &style_tmp, opa_scale);
lv_coord_t w = lv_obj_get_width(btn);
lv_coord_t h = lv_obj_get_height(btn);
lv_coord_t r_max = LV_MATH_MIN(w, h) / 2;
/*In the first part of the animation increase the size of the circle (ink effect) */
lv_area_t cir_area;
lv_coord_t coord_state = ink_act_value < LV_BTN_INK_VALUE_MAX / 2 ? ink_act_value : LV_BTN_INK_VALUE_MAX / 2;
lv_point_t p_act;
p_act.x = ink_point.x;
p_act.y = ink_point.y;
lv_coord_t x_err = (btn->coords.x1 + w / 2) - p_act.x;
lv_coord_t y_err = (btn->coords.y1 + h / 2) - p_act.y;
p_act.x += (x_err * coord_state) >> (LV_BTN_INK_VALUE_MAX_SHIFT - 1);
p_act.y += (y_err * coord_state) >> (LV_BTN_INK_VALUE_MAX_SHIFT - 1);
lv_coord_t half_side = LV_MATH_MAX(w, h) / 2;
cir_area.x1 = p_act.x - ((half_side * coord_state) >> (LV_BTN_INK_VALUE_MAX_SHIFT - 1));
cir_area.y1 = p_act.y - ((half_side * coord_state) >> (LV_BTN_INK_VALUE_MAX_SHIFT - 1));
cir_area.x2 = p_act.x + ((half_side * coord_state) >> (LV_BTN_INK_VALUE_MAX_SHIFT - 1));
cir_area.y2 = p_act.y + ((half_side * coord_state) >> (LV_BTN_INK_VALUE_MAX_SHIFT - 1));
lv_area_intersect(&cir_area, &btn->coords, &cir_area); /*Limit the area. (It might be too big on the smaller side)*/
/*In the second part animate the radius. Circle -> body.radius*/
lv_coord_t r_state = ink_act_value > LV_BTN_INK_VALUE_MAX / 2 ? ink_act_value - LV_BTN_INK_VALUE_MAX / 2 : 0;
lv_style_copy(&style_tmp, ext->styles[ink_top_state]);
style_tmp.body.radius = r_max + (((ext->styles[ink_bg_state]->body.radius - r_max) * r_state) >> (LV_BTN_INK_VALUE_MAX_SHIFT - 1));
style_tmp.body.border.width = 0;
/*Draw the circle*/
lv_draw_rect(&cir_area, mask, &style_tmp, opa_scale);
} else {
lv_style_t res;
lv_style_copy(&res, ext->styles[ink_bg_state]);
lv_style_mix(ext->styles[ink_bg_state], ext->styles[ink_top_state], &res, ink_act_value);
lv_draw_rect(&btn->coords, mask, &res, opa_scale);
}
}
#else
ancestor_design(btn, mask, mode);
#endif
} else if(mode == LV_DESIGN_DRAW_POST) {
ancestor_design(btn, mask, mode);
}
return true;
}
/**
* Signal function of the button
* @param btn pointer to a button object
* @param sign a signal type from lv_signal_t enum
* @param param pointer to a signal specific variable
* @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
*/
static lv_res_t lv_btn_signal(lv_obj_t * btn, lv_signal_t sign, void * param)
{
lv_res_t res;
/* Include the ancient signal function */
res = ancestor_signal(btn, sign, param);
if(res != LV_RES_OK) return res;
lv_btn_ext_t * ext = lv_obj_get_ext_attr(btn);
lv_btn_state_t state = lv_btn_get_state(btn);
bool tgl = lv_btn_get_toggle(btn);
if(sign == LV_SIGNAL_PRESSED) {
/*Refresh the state*/
if(ext->state == LV_BTN_STATE_REL) {
lv_btn_set_state(btn, LV_BTN_STATE_PR);
#if USE_LV_ANIMATION && LV_BTN_INK_EFFECT
ink_bg_state = LV_BTN_STATE_REL;
ink_top_state = LV_BTN_STATE_PR;
#endif
} else if(ext->state == LV_BTN_STATE_TGL_REL) {
lv_btn_set_state(btn, LV_BTN_STATE_TGL_PR);
#if USE_LV_ANIMATION && LV_BTN_INK_EFFECT
ink_bg_state = LV_BTN_STATE_TGL_REL;
ink_top_state = LV_BTN_STATE_TGL_PR;
#endif
}
ext->long_pr_action_executed = 0;
#if USE_LV_ANIMATION && LV_BTN_INK_EFFECT
/*Forget the old inked button*/
if(ink_obj != NULL && ink_obj != btn) {
lv_anim_del(ink_obj, (lv_anim_fp_t)lv_btn_ink_effect_anim);
lv_obj_invalidate(ink_obj);
ink_obj = NULL;
}
/*Save the new data for inking and start it's animation if enabled*/
if(ext->ink_in_time > 0) {
ink_obj = btn;
ink_playback = false;
ink_ready = false;
lv_indev_get_point(lv_indev_get_act(), &ink_point);
lv_anim_t a;
a.var = btn;
a.start = 0;
a.end = LV_BTN_INK_VALUE_MAX;
a.fp = (lv_anim_fp_t)lv_btn_ink_effect_anim;
a.path = lv_anim_path_linear;
a.end_cb = lv_btn_ink_effect_anim_ready;
a.act_time = 0;
a.time = ext->ink_in_time;
a.playback = 0;
a.playback_pause = 0;
a.repeat = 0;
a.repeat_pause = 0;
lv_anim_create(&a);
}
#endif
/*Call the press action, 'param' is the caller indev_proc*/
if(ext->actions[LV_BTN_ACTION_PR] && state != LV_BTN_STATE_INA) {
res = ext->actions[LV_BTN_ACTION_PR](btn);
}
} else if(sign == LV_SIGNAL_PRESS_LOST) {
/*Refresh the state*/
if(ext->state == LV_BTN_STATE_PR) lv_btn_set_state(btn, LV_BTN_STATE_REL);
else if(ext->state == LV_BTN_STATE_TGL_PR) lv_btn_set_state(btn, LV_BTN_STATE_TGL_REL);
} else if(sign == LV_SIGNAL_PRESSING) {
/*When the button begins to drag revert pressed states to released*/
if(lv_indev_is_dragging(param) != false) {
if(ext->state == LV_BTN_STATE_PR) lv_btn_set_state(btn, LV_BTN_STATE_REL);
else if(ext->state == LV_BTN_STATE_TGL_PR) lv_btn_set_state(btn, LV_BTN_STATE_TGL_REL);
}
} else if(sign == LV_SIGNAL_RELEASED) {
/*If not dragged and it was not long press action then
*change state and run the action*/
if(lv_indev_is_dragging(param) == false && ext->long_pr_action_executed == 0) {
if(ext->state == LV_BTN_STATE_PR && tgl == false) {
lv_btn_set_state(btn, LV_BTN_STATE_REL);
} else if(ext->state == LV_BTN_STATE_TGL_PR && tgl == false) {
lv_btn_set_state(btn, LV_BTN_STATE_TGL_REL);
} else if(ext->state == LV_BTN_STATE_PR && tgl == true) {
lv_btn_set_state(btn, LV_BTN_STATE_TGL_REL);
} else if(ext->state == LV_BTN_STATE_TGL_PR && tgl == true) {
lv_btn_set_state(btn, LV_BTN_STATE_REL);
}
if(ext->actions[LV_BTN_ACTION_CLICK] && state != LV_BTN_STATE_INA) {
res = ext->actions[LV_BTN_ACTION_CLICK](btn);
}
} else { /*If dragged change back the state*/
if(ext->state == LV_BTN_STATE_PR) {
lv_btn_set_state(btn, LV_BTN_STATE_REL);
} else if(ext->state == LV_BTN_STATE_TGL_PR) {
lv_btn_set_state(btn, LV_BTN_STATE_TGL_REL);
}
}
#if USE_LV_ANIMATION && LV_BTN_INK_EFFECT
/*Draw the toggled state in the inking instead*/
if(ext->toggle) {
ink_top_state = ext->state;
}
/*If not a toggle button and the "IN" inking is ready then start an "OUT" inking*/
else if(ink_ready && ext->ink_out_time > 0) {
ink_obj = btn;
ink_playback = true; /*It is the playback. If not set `lv_btn_ink_effect_anim_ready` will start its own playback*/
lv_indev_get_point(lv_indev_get_act(), &ink_point);
lv_anim_t a;
a.var = ink_obj;
a.start = LV_BTN_INK_VALUE_MAX;
a.end = 0;
a.fp = (lv_anim_fp_t)lv_btn_ink_effect_anim;
a.path = lv_anim_path_linear;
a.end_cb = lv_btn_ink_effect_anim_ready;
a.act_time = 0;
a.time = ext->ink_out_time;
a.playback = 0;
a.playback_pause = 0;
a.repeat = 0;
a.repeat_pause = 0;
lv_anim_create(&a);
}
#endif
} else if(sign == LV_SIGNAL_LONG_PRESS) {
if(ext->actions[LV_BTN_ACTION_LONG_PR] && state != LV_BTN_STATE_INA) {
ext->long_pr_action_executed = 1;
res = ext->actions[LV_BTN_ACTION_LONG_PR](btn);
}
} else if(sign == LV_SIGNAL_LONG_PRESS_REP) {
if(ext->actions[LV_BTN_ACTION_LONG_PR_REPEAT] && state != LV_BTN_STATE_INA) {
res = ext->actions[LV_BTN_ACTION_LONG_PR_REPEAT](btn);
}
} else if(sign == LV_SIGNAL_CONTROLL) {
char c = *((char *)param);
if(c == LV_GROUP_KEY_RIGHT || c == LV_GROUP_KEY_UP) {
if(lv_btn_get_toggle(btn) != false) lv_btn_set_state(btn, LV_BTN_STATE_TGL_REL);
if(ext->actions[LV_BTN_ACTION_CLICK] && lv_btn_get_state(btn) != LV_BTN_STATE_INA) {
res = ext->actions[LV_BTN_ACTION_CLICK](btn);
}
} else if(c == LV_GROUP_KEY_LEFT || c == LV_GROUP_KEY_DOWN) {
if(lv_btn_get_toggle(btn) != false) lv_btn_set_state(btn, LV_BTN_STATE_REL);
if(ext->actions[LV_BTN_ACTION_CLICK] && lv_btn_get_state(btn) != LV_BTN_STATE_INA) {
res = ext->actions[LV_BTN_ACTION_CLICK](btn);
}
} else if(c == LV_GROUP_KEY_ENTER) {
if(!ext->long_pr_action_executed) {
if(lv_btn_get_toggle(btn)) {
if(state == LV_BTN_STATE_REL || state == LV_BTN_STATE_PR) lv_btn_set_state(btn, LV_BTN_STATE_TGL_REL);
else if(state == LV_BTN_STATE_TGL_REL || state == LV_BTN_STATE_TGL_PR) lv_btn_set_state(btn, LV_BTN_STATE_REL);
} else {
if(state == LV_BTN_STATE_REL || state == LV_BTN_STATE_PR) lv_btn_set_state(btn, LV_BTN_STATE_REL);
else if(state == LV_BTN_STATE_TGL_REL || state == LV_BTN_STATE_TGL_PR) lv_btn_set_state(btn, LV_BTN_STATE_TGL_REL);
}
if(ext->actions[LV_BTN_ACTION_CLICK] && state != LV_BTN_STATE_INA) {
res = ext->actions[LV_BTN_ACTION_CLICK](btn);
}
}
if(res != LV_RES_INV) {
ext->long_pr_action_executed = 0;
}
}
} else if(sign == LV_SIGNAL_CLEANUP) {
#if USE_LV_ANIMATION && LV_BTN_INK_EFFECT
if(btn == ink_obj) {
lv_anim_del(ink_obj, (lv_anim_fp_t)lv_btn_ink_effect_anim);
ink_obj = NULL;
}
#endif
} else if(sign == LV_SIGNAL_GET_TYPE) {
lv_obj_type_t * buf = param;
uint8_t i;
for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/
if(buf->type[i] == NULL) break;
}
buf->type[i] = "lv_btn";
}
return res;
}
#if USE_LV_ANIMATION && LV_BTN_INK_EFFECT
/**
* The animator function of inking. CAlled to increase the radius of ink
* @param btn pointer to the animated button
* @param val the new radius
*/
static void lv_btn_ink_effect_anim(lv_obj_t * btn, int32_t val)
{
if(btn) {
ink_act_value = val;
lv_obj_invalidate(btn);
}
}
/**
* Called to clean up when the ink animation is ready
* @param p unused
*/
static void lv_btn_ink_effect_anim_ready(void * p)
{
(void) p; /*Unused*/
lv_btn_ext_t * ext = lv_obj_get_ext_attr(ink_obj);
lv_btn_state_t state = lv_btn_get_state(ink_obj);
lv_obj_invalidate(ink_obj);
ink_ready = true;
if((state == LV_BTN_STATE_REL || state == LV_BTN_STATE_TGL_REL) && ext->toggle == 0 && ink_playback == false) {
lv_anim_t a;
a.var = ink_obj;
a.start = LV_BTN_INK_VALUE_MAX;
a.end = 0;
a.fp = (lv_anim_fp_t)lv_btn_ink_effect_anim;
a.path = lv_anim_path_linear;
a.end_cb = lv_btn_ink_effect_anim_ready;
a.act_time = -ext->ink_wait_time;
a.time = ext->ink_out_time;
a.playback = 0;
a.playback_pause = 0;
a.repeat = 0;
a.repeat_pause = 0;
lv_anim_create(&a);
ink_playback = true;
} else {
ink_obj = NULL;
}
}
#endif /*USE_LV_ANIMATION*/
#endif

View File

@@ -0,0 +1,280 @@
/**
* @file lv_btn.h
*
*/
#ifndef LV_BTN_H
#define LV_BTN_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#ifdef LV_CONF_INCLUDE_SIMPLE
#include "lv_conf.h"
#else
#include "../../lv_conf.h"
#endif
#if USE_LV_BTN != 0
/*Testing of dependencies*/
#if USE_LV_CONT == 0
#error "lv_btn: lv_cont is required. Enable it in lv_conf.h (USE_LV_CONT 1) "
#endif
#include "lv_cont.h"
#include "../lv_core/lv_indev.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/* Button states
* It can be used not only by buttons but other button-like objects too*/
enum
{
LV_BTN_STATE_REL,
LV_BTN_STATE_PR,
LV_BTN_STATE_TGL_REL,
LV_BTN_STATE_TGL_PR,
LV_BTN_STATE_INA,
LV_BTN_STATE_NUM,
};
typedef uint8_t lv_btn_state_t;
enum
{
LV_BTN_ACTION_CLICK,
LV_BTN_ACTION_PR,
LV_BTN_ACTION_LONG_PR,
LV_BTN_ACTION_LONG_PR_REPEAT,
LV_BTN_ACTION_NUM,
};
typedef uint8_t lv_btn_action_t;
/*Data of button*/
typedef struct
{
lv_cont_ext_t cont; /*Ext. of ancestor*/
/*New data for this type */
lv_action_t actions[LV_BTN_ACTION_NUM];
lv_style_t * styles[LV_BTN_STATE_NUM]; /*Styles in each state*/
lv_btn_state_t state; /*Current state of the button from 'lv_btn_state_t' enum*/
int idx;
#if LV_BTN_INK_EFFECT
uint16_t ink_in_time; /*[ms] Time of ink fill effect (0: disable ink effect)*/
uint16_t ink_wait_time; /*[ms] Wait before the ink disappears */
uint16_t ink_out_time; /*[ms] Time of ink disappearing*/
#endif
uint8_t toggle :1; /*1: Toggle enabled*/
uint8_t long_pr_action_executed :1; /*1: Long press action executed (Handled by the library)*/
} lv_btn_ext_t;
/*Styles*/
enum {
LV_BTN_STYLE_REL,
LV_BTN_STYLE_PR,
LV_BTN_STYLE_TGL_REL,
LV_BTN_STYLE_TGL_PR,
LV_BTN_STYLE_INA,
};
typedef uint8_t lv_btn_style_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a button objects
* @param par pointer to an object, it will be the parent of the new button
* @param copy pointer to a button object, if not NULL then the new object will be copied from it
* @return pointer to the created button
*/
lv_obj_t * lv_btn_create(lv_obj_t * par, const lv_obj_t * copy);
/*=====================
* Setter functions
*====================*/
/**
* Enable the toggled states. On release the button will change from/to toggled state.
* @param btn pointer to a button object
* @param tgl true: enable toggled states, false: disable
*/
void lv_btn_set_toggle(lv_obj_t * btn, bool tgl);
/**
* Set the state of the button
* @param btn pointer to a button object
* @param state the new state of the button (from lv_btn_state_t enum)
*/
void lv_btn_set_state(lv_obj_t * btn, lv_btn_state_t state);
/**
* Toggle the state of the button (ON->OFF, OFF->ON)
* @param btn pointer to a button object
*/
void lv_btn_toggle(lv_obj_t * btn);
/**
* Set a function to call when a button event happens
* @param btn pointer to a button object
* @param action type of event form 'lv_action_t' (press, release, long press, long press repeat)
*/
void lv_btn_set_action(lv_obj_t * btn, lv_btn_action_t type, lv_action_t action);
/**
* Set the layout on a button
* @param btn pointer to a button object
* @param layout a layout from 'lv_cont_layout_t'
*/
static inline void lv_btn_set_layout(lv_obj_t * btn, lv_layout_t layout)
{
lv_cont_set_layout(btn, layout);
}
/**
* Enable the horizontal or vertical fit.
* The button size will be set to involve the children horizontally or vertically.
* @param btn pointer to a button object
* @param hor_en true: enable the horizontal fit
* @param ver_en true: enable the vertical fit
*/
static inline void lv_btn_set_fit(lv_obj_t * btn, bool hor_en, bool ver_en)
{
lv_cont_set_fit(btn, hor_en, ver_en);
}
/**
* Set time of the ink effect (draw a circle on click to animate in the new state)
* @param btn pointer to a button object
* @param time the time of the ink animation
*/
void lv_btn_set_ink_in_time(lv_obj_t * btn, uint16_t time);
/**
* Set the wait time before the ink disappears
* @param btn pointer to a button object
* @param time the time of the ink animation
*/
void lv_btn_set_ink_wait_time(lv_obj_t * btn, uint16_t time);
/**
* Set time of the ink out effect (animate to the released state)
* @param btn pointer to a button object
* @param time the time of the ink animation
*/
void lv_btn_set_ink_out_time(lv_obj_t * btn, uint16_t time);
/**
* Set a style of a button.
* @param btn pointer to button object
* @param type which style should be set
* @param style pointer to a style
* */
void lv_btn_set_style(lv_obj_t * btn, lv_btn_style_t type, lv_style_t *style);
/*=====================
* Getter functions
*====================*/
/**
* Get the current state of the button
* @param btn pointer to a button object
* @return the state of the button (from lv_btn_state_t enum)
*/
lv_btn_state_t lv_btn_get_state(const lv_obj_t * btn);
/**
* Get the toggle enable attribute of the button
* @param btn pointer to a button object
* @return ture: toggle enabled, false: disabled
*/
bool lv_btn_get_toggle(const lv_obj_t * btn);
/**
* Get the release action of a button
* @param btn pointer to a button object
* @return pointer to the release action function
*/
lv_action_t lv_btn_get_action(const lv_obj_t * btn, lv_btn_action_t type);
/**
* Get the layout of a button
* @param btn pointer to button object
* @return the layout from 'lv_cont_layout_t'
*/
static inline lv_layout_t lv_btn_get_layout(const lv_obj_t * btn)
{
return lv_cont_get_layout(btn);
}
/**
* Get horizontal fit enable attribute of a button
* @param btn pointer to a button object
* @return true: horizontal fit is enabled; false: disabled
*/
static inline bool lv_btn_get_hor_fit(const lv_obj_t * btn)
{
return lv_cont_get_hor_fit(btn);
}
/**
* Get vertical fit enable attribute of a container
* @param btn pointer to a button object
* @return true: vertical fit is enabled; false: disabled
*/
static inline bool lv_btn_get_ver_fit(const lv_obj_t * btn)
{
return lv_cont_get_ver_fit(btn);
}
/**
* Get time of the ink in effect (draw a circle on click to animate in the new state)
* @param btn pointer to a button object
* @return the time of the ink animation
*/
uint16_t lv_btn_get_ink_in_time(const lv_obj_t * btn);
/**
* Get the wait time before the ink disappears
* @param btn pointer to a button object
* @return the time of the ink animation
*/
uint16_t lv_btn_get_ink_wait_time(const lv_obj_t * btn);
/**
* Get time of the ink out effect (animate to the releases state)
* @param btn pointer to a button object
* @return the time of the ink animation
*/
uint16_t lv_btn_get_ink_out_time(const lv_obj_t * btn);
/**
* Get style of a button.
* @param btn pointer to button object
* @param type which style should be get
* @return style pointer to the style
* */
lv_style_t * lv_btn_get_style(const lv_obj_t * btn, lv_btn_style_t type);
/**********************
* MACROS
**********************/
#endif /*USE_LV_BUTTON*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_BTN_H*/

View File

@@ -0,0 +1,881 @@
/**
* @file lv_btnm.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_btnm.h"
#if USE_LV_BTNM != 0
#include "../lv_core/lv_group.h"
#include "../lv_draw/lv_draw.h"
#include "../lv_core/lv_refr.h"
#include "../lv_themes/lv_theme.h"
#include "../lv_misc/lv_txt.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static lv_res_t lv_btnm_signal(lv_obj_t * btnm, lv_signal_t sign, void * param);
static bool lv_btnm_design(lv_obj_t * btnm, const lv_area_t * mask, lv_design_mode_t mode);
static uint8_t get_button_width(const char * btn_str);
static bool button_is_hidden(const char * btn_str);
static bool button_is_repeat_disabled(const char * btn_str);
static bool button_is_inactive(const char * btn_str);
const char * cut_ctrl_byte(const char * btn_str);
static uint16_t get_button_from_point(lv_obj_t * btnm, lv_point_t * p);
static uint16_t get_button_text(lv_obj_t * btnm, uint16_t btn_id);
static void allocate_btn_areas(lv_obj_t * btnm, const char ** map);
/**********************
* STATIC VARIABLES
**********************/
static const char * lv_btnm_def_map[] = {"Btn1", "Btn2", "Btn3", "\n",
"\002Btn4", "Btn5", ""
};
static lv_design_func_t ancestor_design_f;
static lv_signal_func_t ancestor_signal;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Create a button matrix objects
* @param par pointer to an object, it will be the parent of the new button matrix
* @param copy pointer to a button matrix object, if not NULL then the new object will be copied from it
* @return pointer to the created button matrix
*/
lv_obj_t * lv_btnm_create(lv_obj_t * par, const lv_obj_t * copy)
{
LV_LOG_TRACE("button matrix create started");
/*Create the ancestor object*/
lv_obj_t * new_btnm = lv_obj_create(par, copy);
lv_mem_assert(new_btnm);
if(new_btnm == NULL) return NULL;
if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_btnm);
/*Allocate the object type specific extended data*/
lv_btnm_ext_t * ext = lv_obj_allocate_ext_attr(new_btnm, sizeof(lv_btnm_ext_t));
lv_mem_assert(ext);
if(ext == NULL) return NULL;
ext->btn_cnt = 0;
ext->btn_id_pr = LV_BTNM_PR_NONE;
ext->btn_id_tgl = LV_BTNM_PR_NONE;
ext->button_areas = NULL;
ext->action = NULL;
ext->map_p = NULL;
ext->toggle = 0;
ext->recolor = 0;
ext->styles_btn[LV_BTN_STATE_REL] = &lv_style_btn_rel;
ext->styles_btn[LV_BTN_STATE_PR] = &lv_style_btn_pr;
ext->styles_btn[LV_BTN_STATE_TGL_REL] = &lv_style_btn_tgl_rel;
ext->styles_btn[LV_BTN_STATE_TGL_PR] = &lv_style_btn_tgl_pr;
ext->styles_btn[LV_BTN_STATE_INA] = &lv_style_btn_ina;
if(ancestor_design_f == NULL) ancestor_design_f = lv_obj_get_design_func(new_btnm);
lv_obj_set_signal_func(new_btnm, lv_btnm_signal);
lv_obj_set_design_func(new_btnm, lv_btnm_design);
/*Init the new button matrix object*/
if(copy == NULL) {
lv_obj_set_size(new_btnm, LV_HOR_RES / 2, LV_VER_RES / 4);
lv_btnm_set_map(new_btnm, lv_btnm_def_map);
/*Set the default styles*/
lv_theme_t * th = lv_theme_get_current();
if(th) {
lv_btnm_set_style(new_btnm, LV_BTNM_STYLE_BG, th->btnm.bg);
lv_btnm_set_style(new_btnm, LV_BTNM_STYLE_BTN_REL, th->btnm.btn.rel);
lv_btnm_set_style(new_btnm, LV_BTNM_STYLE_BTN_PR, th->btnm.btn.pr);
lv_btnm_set_style(new_btnm, LV_BTNM_STYLE_BTN_TGL_REL, th->btnm.btn.tgl_rel);
lv_btnm_set_style(new_btnm, LV_BTNM_STYLE_BTN_TGL_PR, th->btnm.btn.tgl_pr);
lv_btnm_set_style(new_btnm, LV_BTNM_STYLE_BTN_INA, th->btnm.btn.ina);
} else {
lv_obj_set_style(new_btnm, &lv_style_pretty);
}
}
/*Copy an existing object*/
else {
lv_btnm_ext_t * copy_ext = lv_obj_get_ext_attr(copy);
memcpy(ext->styles_btn, copy_ext->styles_btn, sizeof(ext->styles_btn));
ext->action = copy_ext->action;
ext->toggle = copy_ext->toggle;
ext->btn_id_tgl = copy_ext->btn_id_tgl;
lv_btnm_set_map(new_btnm, lv_btnm_get_map(copy));
}
LV_LOG_INFO("button matrix created");
return new_btnm;
}
/*=====================
* Setter functions
*====================*/
/**
* Set a new map. Buttons will be created/deleted according to the map.
* @param btnm pointer to a button matrix object
* @param map pointer a string array. The last string has to be: "".
* Use "\n" to begin a new line.
* The first byte can be a control data:
* - bit 7: always 1
* - bit 6: always 0
* - bit 5: inactive (disabled) (\24x)
* - bit 4: no repeat (on long press) (\22x)
* - bit 3: hidden (\21x)
* - bit 2..0: button relative width
* Example (practically use octal numbers): "\224abc": "abc" text with 4 width and no long press
*/
void lv_btnm_set_map(lv_obj_t * btnm, const char ** map)
{
if(map == NULL) return;
lv_btnm_ext_t * ext = lv_obj_get_ext_attr(btnm);
ext->map_p = map;
/*Analyze the map and create the required number of buttons*/
allocate_btn_areas(btnm, map);
/*Set size and positions of the buttons*/
lv_style_t * style_bg = lv_btnm_get_style(btnm, LV_BTNM_STYLE_BG);
lv_coord_t max_w = lv_obj_get_width(btnm) - 2 * style_bg->body.padding.hor;
lv_coord_t max_h = lv_obj_get_height(btnm) - 2 * style_bg->body.padding.ver;
lv_coord_t act_y = style_bg->body.padding.ver;
/*Count the lines to calculate button height*/
uint8_t line_cnt = 1;
uint8_t li;
for(li = 0; strlen(map[li]) != 0; li++) {
if(strcmp(map[li], "\n") == 0) line_cnt ++;
}
lv_coord_t btn_h = max_h - ((line_cnt - 1) * style_bg->body.padding.inner);
btn_h = btn_h / line_cnt;
btn_h --; /*-1 because e.g. height = 100 means 101 pixels (0..100)*/
/* Count the units and the buttons in a line
* (A button can be 1,2,3... unit wide)*/
uint16_t unit_cnt; /*Number of units in a row*/
uint16_t unit_act_cnt; /*Number of units currently put in a row*/
uint16_t btn_cnt; /*Number of buttons in a row*/
uint16_t i_tot = 0; /*Act. index in the str map*/
uint16_t btn_i = 0; /*Act. index of button areas*/
const char ** map_p_tmp = map;
/*Count the units and the buttons in a line*/
while(1) {
unit_cnt = 0;
btn_cnt = 0;
/*Count the buttons in a line*/
while(strcmp(map_p_tmp[btn_cnt], "\n") != 0 &&
strlen(map_p_tmp[btn_cnt]) != 0) { /*Check a line*/
unit_cnt += get_button_width(map_p_tmp[btn_cnt]);
btn_cnt ++;
}
/*Make sure the last row is at the bottom of 'btnm'*/
if(map_p_tmp[btn_cnt][0] == '\0') { /*Last row?*/
btn_h = max_h - act_y + style_bg->body.padding.ver - 1;
}
/*Only deal with the non empty lines*/
if(btn_cnt != 0) {
/*Calculate the width of all units*/
lv_coord_t all_unit_w = max_w - ((btn_cnt - 1) * style_bg->body.padding.inner);
/*Set the button size and positions and set the texts*/
uint16_t i;
lv_coord_t act_x = style_bg->body.padding.hor;
lv_coord_t act_unit_w;
unit_act_cnt = 0;
for(i = 0; i < btn_cnt; i++) {
/* one_unit_w = all_unit_w / unit_cnt
* act_unit_w = one_unit_w * button_width
* do this two operations but the multiply first to divide a greater number */
act_unit_w = (all_unit_w * get_button_width(map_p_tmp[i])) / unit_cnt;
act_unit_w --; /*-1 because e.g. width = 100 means 101 pixels (0..100)*/
/*Always recalculate act_x because of rounding errors */
act_x = (unit_act_cnt * all_unit_w) / unit_cnt + i * style_bg->body.padding.inner + style_bg->body.padding.hor;
/* Set the button's area.
* If inner padding is zero then use the prev. button x2 as x1 to avoid rounding errors*/
if(style_bg->body.padding.inner == 0 && act_x != style_bg->body.padding.hor) {
lv_area_set(&ext->button_areas[btn_i], ext->button_areas[btn_i - 1].x2, act_y,
act_x + act_unit_w, act_y + btn_h);
} else {
lv_area_set(&ext->button_areas[btn_i], act_x, act_y,
act_x + act_unit_w, act_y + btn_h);
}
unit_act_cnt += get_button_width(map_p_tmp[i]);
i_tot ++;
btn_i ++;
}
}
act_y += btn_h + style_bg->body.padding.inner;
if(strlen(map_p_tmp[btn_cnt]) == 0) break; /*Break on end of map*/
map_p_tmp = &map_p_tmp[btn_cnt + 1]; /*Set the map to the next line*/
i_tot ++; /*Skip the '\n'*/
}
lv_obj_invalidate(btnm);
}
/**
* Set a new callback function for the buttons (It will be called when a button is released)
* @param btnm: pointer to button matrix object
* @param cb pointer to a callback function
*/
void lv_btnm_set_action(lv_obj_t * btnm, lv_btnm_action_t action)
{
lv_btnm_ext_t * ext = lv_obj_get_ext_attr(btnm);
ext->action = action;
}
/**
* Enable or disable button toggling
* @param btnm pointer to button matrix object
* @param en true: enable toggling; false: disable toggling
* @param id index of the currently toggled button (ignored if 'en' == false)
*/
void lv_btnm_set_toggle(lv_obj_t * btnm, bool en, uint16_t id)
{
lv_btnm_ext_t * ext = lv_obj_get_ext_attr(btnm);
ext->toggle = en == false ? 0 : 1;
if(ext->toggle != 0) {
if(id >= ext->btn_cnt) id = ext->btn_cnt - 1;
ext->btn_id_tgl = id;
} else {
ext->btn_id_tgl = LV_BTNM_PR_NONE;
}
lv_obj_invalidate(btnm);
}
/**
* Set a style of a button matrix
* @param btnm pointer to a button matrix object
* @param type which style should be set
* @param style pointer to a style
*/
void lv_btnm_set_style(lv_obj_t * btnm, lv_btnm_style_t type, lv_style_t * style)
{
lv_btnm_ext_t * ext = lv_obj_get_ext_attr(btnm);
switch(type) {
case LV_BTNM_STYLE_BG:
lv_obj_set_style(btnm, style);
break;
case LV_BTNM_STYLE_BTN_REL:
ext->styles_btn[LV_BTN_STATE_REL] = style;
lv_obj_invalidate(btnm);
break;
case LV_BTNM_STYLE_BTN_PR:
ext->styles_btn[LV_BTN_STATE_PR] = style;
lv_obj_invalidate(btnm);
break;
case LV_BTNM_STYLE_BTN_TGL_REL:
ext->styles_btn[LV_BTN_STATE_TGL_REL] = style;
lv_obj_invalidate(btnm);
break;
case LV_BTNM_STYLE_BTN_TGL_PR:
ext->styles_btn[LV_BTN_STATE_TGL_PR] = style;
lv_obj_invalidate(btnm);
break;
case LV_BTNM_STYLE_BTN_INA:
ext->styles_btn[LV_BTN_STATE_INA] = style;
lv_obj_invalidate(btnm);
break;
}
}
void lv_btnm_set_recolor(const lv_obj_t * btnm, bool en)
{
lv_btnm_ext_t * ext = lv_obj_get_ext_attr(btnm);
ext->recolor = en;
lv_obj_invalidate(btnm);
}
/*=====================
* Getter functions
*====================*/
/**
* Get the current map of a button matrix
* @param btnm pointer to a button matrix object
* @return the current map
*/
const char ** lv_btnm_get_map(const lv_obj_t * btnm)
{
lv_btnm_ext_t * ext = lv_obj_get_ext_attr(btnm);
return ext->map_p;
}
/**
* Get a the callback function of the buttons on a button matrix
* @param btnm: pointer to button matrix object
* @return pointer to the callback function
*/
lv_btnm_action_t lv_btnm_get_action(const lv_obj_t * btnm)
{
lv_btnm_ext_t * ext = lv_obj_get_ext_attr(btnm);
return ext->action;
}
/**
* Get the pressed button
* @param btnm pointer to button matrix object
* @return index of the currently pressed button (LV_BTNM_PR_NONE: if unset)
*/
uint16_t lv_btnm_get_pressed(const lv_obj_t * btnm)
{
lv_btnm_ext_t * ext = lv_obj_get_ext_attr(btnm);
return ext->btn_id_pr;
}
/**
* Get the toggled button
* @param btnm pointer to button matrix object
* @return index of the currently toggled button (LV_BTNM_PR_NONE: if unset)
*/
uint16_t lv_btnm_get_toggled(const lv_obj_t * btnm)
{
lv_btnm_ext_t * ext = lv_obj_get_ext_attr(btnm);
if(ext->toggle == 0) return LV_BTNM_PR_NONE;
else return ext->btn_id_tgl;
}
/**
* Get a style of a button matrix
* @param btnm pointer to a button matrix object
* @param type which style should be get
* @return style pointer to a style
*/
lv_style_t * lv_btnm_get_style(const lv_obj_t * btnm, lv_btnm_style_t type)
{
lv_style_t * style = NULL;
lv_btnm_ext_t * ext = lv_obj_get_ext_attr(btnm);
switch(type) {
case LV_BTNM_STYLE_BG:
style = lv_obj_get_style(btnm);
break;
case LV_BTNM_STYLE_BTN_REL:
style = ext->styles_btn[LV_BTN_STATE_REL];
break;
case LV_BTNM_STYLE_BTN_PR:
style = ext->styles_btn[LV_BTN_STATE_PR];
break;
case LV_BTNM_STYLE_BTN_TGL_REL:
style = ext->styles_btn[LV_BTN_STATE_TGL_REL];
break;
case LV_BTNM_STYLE_BTN_TGL_PR:
style = ext->styles_btn[LV_BTN_STATE_TGL_PR];
break;
case LV_BTNM_STYLE_BTN_INA:
style = ext->styles_btn[LV_BTN_STATE_INA];
break;
default:
style = NULL;
break;
}
return style;
}
bool lv_btnm_get_recolor(const lv_obj_t * btnm)
{
lv_btnm_ext_t * ext = lv_obj_get_ext_attr(btnm);
return ext->recolor;
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Handle the drawing related tasks of the button matrixs
* @param btnm pointer to a button matrix object
* @param mask the object will be drawn only in this area
* @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area
* (return 'true' if yes)
* LV_DESIGN_DRAW: draw the object (always return 'true')
* LV_DESIGN_DRAW_POST: drawing after every children are drawn
* @param return true/false, depends on 'mode'
*/
static bool lv_btnm_design(lv_obj_t * btnm, const lv_area_t * mask, lv_design_mode_t mode)
{
if(mode == LV_DESIGN_COVER_CHK) {
return ancestor_design_f(btnm, mask, mode);
/*Return false if the object is not covers the mask_p area*/
}
/*Draw the object*/
else if(mode == LV_DESIGN_DRAW_MAIN) {
ancestor_design_f(btnm, mask, mode);
lv_btnm_ext_t * ext = lv_obj_get_ext_attr(btnm);
lv_style_t * bg_style = lv_obj_get_style(btnm);
lv_style_t * btn_style;
lv_opa_t opa_scale = lv_obj_get_opa_scale(btnm);
lv_area_t area_btnm;
lv_obj_get_coords(btnm, &area_btnm);
lv_area_t area_tmp;
lv_coord_t btn_w;
lv_coord_t btn_h;
uint16_t btn_i = 0;
uint16_t txt_i = 0;
lv_style_t style_tmp;
lv_txt_flag_t txt_flag = LV_TXT_FLAG_NONE;
if(ext->recolor) txt_flag = LV_TXT_FLAG_RECOLOR;
for(btn_i = 0; btn_i < ext->btn_cnt; btn_i ++, txt_i ++) {
/*Search the next valid text in the map*/
while(strcmp(ext->map_p[txt_i], "\n") == 0) {
txt_i ++;
}
/*Skip hidden buttons*/
if(button_is_hidden(ext->map_p[txt_i])) continue;
lv_area_copy(&area_tmp, &ext->button_areas[btn_i]);
area_tmp.x1 += area_btnm.x1;
area_tmp.y1 += area_btnm.y1;
area_tmp.x2 += area_btnm.x1;
area_tmp.y2 += area_btnm.y1;
btn_w = lv_area_get_width(&area_tmp);
btn_h = lv_area_get_height(&area_tmp);
/*Load the style*/
if(button_is_inactive(ext->map_p[txt_i])) btn_style = lv_btnm_get_style(btnm, LV_BTNM_STYLE_BTN_INA);
else if(btn_i != ext->btn_id_pr && btn_i != ext->btn_id_tgl) btn_style = lv_btnm_get_style(btnm, LV_BTNM_STYLE_BTN_REL);
else if(btn_i == ext->btn_id_pr && btn_i != ext->btn_id_tgl) btn_style = lv_btnm_get_style(btnm, LV_BTNM_STYLE_BTN_PR);
else if(btn_i != ext->btn_id_pr && btn_i == ext->btn_id_tgl) btn_style = lv_btnm_get_style(btnm, LV_BTNM_STYLE_BTN_TGL_REL);
else if(btn_i == ext->btn_id_pr && btn_i == ext->btn_id_tgl) btn_style = lv_btnm_get_style(btnm, LV_BTNM_STYLE_BTN_TGL_PR);
else btn_style = lv_btnm_get_style(btnm, LV_BTNM_STYLE_BTN_REL); /*Not possible option, just to be sure*/
lv_style_copy(&style_tmp, btn_style);
/*Remove borders on the edges if `LV_BORDER_INTERNAL`*/
if(style_tmp.body.border.part & LV_BORDER_INTERNAL) {
if(area_tmp.y1 == btnm->coords.y1 + bg_style->body.padding.ver) {
style_tmp.body.border.part &= ~LV_BORDER_TOP;
}
if(area_tmp.y2 == btnm->coords.y2 - bg_style->body.padding.ver) {
style_tmp.body.border.part &= ~LV_BORDER_BOTTOM;
}
if(txt_i == 0) {
style_tmp.body.border.part &= ~LV_BORDER_LEFT;
}
else if(strcmp(ext->map_p[txt_i - 1],"\n") == 0) {
style_tmp.body.border.part &= ~LV_BORDER_LEFT;
}
if(ext->map_p[txt_i + 1][0] == '\0' || strcmp(ext->map_p[txt_i + 1], "\n") == 0) {
style_tmp.body.border.part &= ~LV_BORDER_RIGHT;
}
}
lv_draw_rect(&area_tmp, mask, &style_tmp, opa_scale);
/*Calculate the size of the text*/
if(btn_style->glass) btn_style = bg_style;
const lv_font_t * font = btn_style->text.font;
lv_point_t txt_size;
lv_txt_get_size(&txt_size, ext->map_p[txt_i], font,
btn_style->text.letter_space, btn_style->text.line_space,
lv_area_get_width(&area_btnm), txt_flag);
area_tmp.x1 += (btn_w - txt_size.x) / 2;
area_tmp.y1 += (btn_h - txt_size.y) / 2;
area_tmp.x2 = area_tmp.x1 + txt_size.x;
area_tmp.y2 = area_tmp.y1 + txt_size.y;
lv_draw_label(&area_tmp, mask, btn_style, opa_scale, ext->map_p[txt_i], txt_flag, NULL);
}
}
return true;
}
/**
* Signal function of the button matrix
* @param btnm pointer to a button matrix object
* @param sign a signal type from lv_signal_t enum
* @param param pointer to a signal specific variable
* @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
*/
static lv_res_t lv_btnm_signal(lv_obj_t * btnm, lv_signal_t sign, void * param)
{
lv_res_t res;
/* Include the ancient signal function */
res = ancestor_signal(btnm, sign, param);
if(res != LV_RES_OK) return res;
lv_btnm_ext_t * ext = lv_obj_get_ext_attr(btnm);
lv_area_t btnm_area;
lv_area_t btn_area;
lv_point_t p;
if(sign == LV_SIGNAL_CLEANUP) {
lv_mem_free(ext->button_areas);
} else if(sign == LV_SIGNAL_STYLE_CHG || sign == LV_SIGNAL_CORD_CHG) {
lv_btnm_set_map(btnm, ext->map_p);
} else if(sign == LV_SIGNAL_PRESSING) {
uint16_t btn_pr;
/*Search the pressed area*/
lv_indev_get_point(param, &p);
btn_pr = get_button_from_point(btnm, &p);
/*Invalidate to old and the new areas*/;
lv_obj_get_coords(btnm, &btnm_area);
if(btn_pr != ext->btn_id_pr) {
lv_indev_reset_lpr(param);
if(ext->btn_id_pr != LV_BTNM_PR_NONE) {
lv_area_copy(&btn_area, &ext->button_areas[ext->btn_id_pr]);
btn_area.x1 += btnm_area.x1;
btn_area.y1 += btnm_area.y1;
btn_area.x2 += btnm_area.x1;
btn_area.y2 += btnm_area.y1;
lv_inv_area(&btn_area);
}
if(btn_pr != LV_BTNM_PR_NONE) {
lv_area_copy(&btn_area, &ext->button_areas[btn_pr]);
btn_area.x1 += btnm_area.x1;
btn_area.y1 += btnm_area.y1;
btn_area.x2 += btnm_area.x1;
btn_area.y2 += btnm_area.y1;
lv_inv_area(&btn_area);
}
}
ext->btn_id_pr = btn_pr;
}
else if(sign == LV_SIGNAL_LONG_PRESS_REP) {
if(ext->action && ext->btn_id_pr != LV_BTNM_PR_NONE) {
uint16_t txt_i = get_button_text(btnm, ext->btn_id_pr);
if(txt_i != LV_BTNM_PR_NONE) {
if(button_is_repeat_disabled(ext->map_p[txt_i]) == false &&
button_is_inactive(ext->map_p[txt_i]) == false) {
res = ext->action(btnm, cut_ctrl_byte(ext->map_p[txt_i]));
}
}
}
} else if(sign == LV_SIGNAL_RELEASED) {
if(ext->btn_id_pr != LV_BTNM_PR_NONE) {
uint16_t txt_i = get_button_text(btnm, ext->btn_id_pr);
if(button_is_inactive(ext->map_p[txt_i]) == false && txt_i != LV_BTNM_PR_NONE) { /*Ignore the inactive buttons anf click between the buttons*/
if(ext->action) res = ext->action(btnm, cut_ctrl_byte(ext->map_p[txt_i]));
if(res == LV_RES_OK) {
/*Invalidate to old pressed area*/;
lv_obj_get_coords(btnm, &btnm_area);
lv_area_copy(&btn_area, &ext->button_areas[ext->btn_id_pr]);
btn_area.x1 += btnm_area.x1;
btn_area.y1 += btnm_area.y1;
btn_area.x2 += btnm_area.x1;
btn_area.y2 += btnm_area.y1;
lv_inv_area(&btn_area);
if(ext->toggle != 0) {
/*Invalidate to old toggled area*/;
lv_area_copy(&btn_area, &ext->button_areas[ext->btn_id_tgl]);
btn_area.x1 += btnm_area.x1;
btn_area.y1 += btnm_area.y1;
btn_area.x2 += btnm_area.x1;
btn_area.y2 += btnm_area.y1;
lv_inv_area(&btn_area);
ext->btn_id_tgl = ext->btn_id_pr;
}
#if USE_LV_GROUP
/*Leave the clicked button when releases if this not the focused object in a group*/
lv_group_t * g = lv_obj_get_group(btnm);
if(lv_group_get_focused(g) != btnm) {
ext->btn_id_pr = LV_BTNM_PR_NONE;
}
#else
ext->btn_id_pr = LV_BTNM_PR_NONE;
#endif
}
}
}
} else if(sign == LV_SIGNAL_PRESS_LOST || sign == LV_SIGNAL_DEFOCUS) {
ext->btn_id_pr = LV_BTNM_PR_NONE;
lv_obj_invalidate(btnm);
} else if(sign == LV_SIGNAL_FOCUS) {
#if USE_LV_GROUP
lv_indev_t * indev = lv_indev_get_act();
lv_hal_indev_type_t indev_type = lv_indev_get_type(indev);
if(indev_type == LV_INDEV_TYPE_POINTER) {
/*Select the clicked button*/
lv_point_t p1;
lv_indev_get_point(indev, &p1);
uint16_t btn_i = get_button_from_point(btnm, &p1);
ext->btn_id_pr = btn_i;
} else if(indev_type == LV_INDEV_TYPE_ENCODER) {
/*In navigation mode don't select any button but in edit mode select the fist*/
if(lv_group_get_editing(lv_obj_get_group(btnm))) ext->btn_id_pr = 0;
else ext->btn_id_pr = LV_BTNM_PR_NONE;
} else {
ext->btn_id_pr = 0;
}
#else
ext->btn_id_pr = 0;
#endif
lv_obj_invalidate(btnm);
} else if(sign == LV_SIGNAL_CONTROLL) {
char c = *((char *)param);
if(c == LV_GROUP_KEY_RIGHT) {
if(ext->btn_id_pr == LV_BTNM_PR_NONE) ext->btn_id_pr = 0;
else ext->btn_id_pr++;
if(ext->btn_id_pr >= ext->btn_cnt - 1) ext->btn_id_pr = ext->btn_cnt - 1;
lv_obj_invalidate(btnm);
} else if(c == LV_GROUP_KEY_LEFT) {
if(ext->btn_id_pr == LV_BTNM_PR_NONE) ext->btn_id_pr = 0;
if(ext->btn_id_pr > 0) ext->btn_id_pr--;
lv_obj_invalidate(btnm);
} else if(c == LV_GROUP_KEY_DOWN) {
lv_style_t * style = lv_btnm_get_style(btnm, LV_BTNM_STYLE_BG);
/*Find the area below the the current*/
if(ext->btn_id_pr == LV_BTNM_PR_NONE) {
ext->btn_id_pr = 0;
} else {
uint16_t area_below;
lv_coord_t pr_center = ext->button_areas[ext->btn_id_pr].x1 + (lv_area_get_width(&ext->button_areas[ext->btn_id_pr]) >> 1);
for(area_below = ext->btn_id_pr; area_below < ext->btn_cnt; area_below ++) {
if(ext->button_areas[area_below].y1 > ext->button_areas[ext->btn_id_pr].y1 &&
pr_center >= ext->button_areas[area_below].x1 &&
pr_center <= ext->button_areas[area_below].x2 + style->body.padding.hor) {
break;
}
}
if(area_below < ext->btn_cnt) ext->btn_id_pr = area_below;
}
lv_obj_invalidate(btnm);
} else if(c == LV_GROUP_KEY_UP) {
lv_style_t * style = lv_btnm_get_style(btnm, LV_BTNM_STYLE_BG);
/*Find the area below the the current*/
if(ext->btn_id_pr == LV_BTNM_PR_NONE) {
ext->btn_id_pr = 0;
} else {
int16_t area_above;
lv_coord_t pr_center = ext->button_areas[ext->btn_id_pr].x1 + (lv_area_get_width(&ext->button_areas[ext->btn_id_pr]) >> 1);
for(area_above = ext->btn_id_pr; area_above >= 0; area_above --) {
if(ext->button_areas[area_above].y1 < ext->button_areas[ext->btn_id_pr].y1 &&
pr_center >= ext->button_areas[area_above].x1 - style->body.padding.hor &&
pr_center <= ext->button_areas[area_above].x2) {
break;
}
}
if(area_above >= 0) ext->btn_id_pr = area_above;
}
lv_obj_invalidate(btnm);
} else if(c == LV_GROUP_KEY_ENTER) {
if(ext->action != NULL) {
uint16_t txt_i = get_button_text(btnm, ext->btn_id_pr);
if(txt_i != LV_BTNM_PR_NONE) {
res = ext->action(btnm, cut_ctrl_byte(ext->map_p[txt_i]));
}
}
}
} else if(sign == LV_SIGNAL_GET_EDITABLE) {
bool * editable = (bool *)param;
*editable = true;
} else if(sign == LV_SIGNAL_GET_TYPE) {
lv_obj_type_t * buf = param;
uint8_t i;
for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/
if(buf->type[i] == NULL) break;
}
buf->type[i] = "lv_btnm";
}
return res;
}
/**
* Create the required number of buttons according to a map
* @param btnm pointer to button matrix object
* @param map_p pointer to a string array
*/
static void allocate_btn_areas(lv_obj_t * btnm, const char ** map)
{
/*Count the buttons in the map*/
uint16_t btn_cnt = 0;
uint16_t i = 0;
while(strlen(map[i]) != 0) {
if(strcmp(map[i], "\n") != 0) { /*Do not count line breaks*/
btn_cnt ++;
}
i++;
}
lv_btnm_ext_t * ext = lv_obj_get_ext_attr(btnm);
if(ext->button_areas != NULL) {
lv_mem_free(ext->button_areas);
ext->button_areas = NULL;
}
ext->button_areas = lv_mem_alloc(sizeof(lv_area_t) * btn_cnt);
lv_mem_assert(ext->button_areas);
if(ext->button_areas == NULL) btn_cnt = 0;
ext->btn_cnt = btn_cnt;
}
/**
* Get the width of a button in units. It comes from the first "letter".
* @param btn_str The descriptor string of a button. E.g. "apple" or "\004banana"
* @return the width of the button in units
*/
static uint8_t get_button_width(const char * btn_str)
{
if((btn_str[0] & LV_BTNM_CTRL_MASK) == LV_BTNM_CTRL_CODE) {
return btn_str[0] & LV_BTNM_WIDTH_MASK;
}
return 1; /*Default width is 1*/
}
static bool button_is_hidden(const char * btn_str)
{
/*If control byte presents and hidden bit is '1' then the button is hidden*/
if(((btn_str[0] & LV_BTNM_CTRL_MASK) == LV_BTNM_CTRL_CODE) &&
(btn_str[0] & LV_BTNM_HIDE_MASK)) {
return true;
}
return false;
}
static bool button_is_repeat_disabled(const char * btn_str)
{
/*If control byte presents and hidden bit is '1' then the button is hidden*/
if(((btn_str[0] & LV_BTNM_CTRL_MASK) == LV_BTNM_CTRL_CODE) &&
(btn_str[0] & LV_BTNM_REPEAT_DISABLE_MASK)) {
return true;
}
return false;
}
static bool button_is_inactive(const char * btn_str)
{
/*If control byte presents and hidden bit is '1' then the button is hidden*/
if(((btn_str[0] & LV_BTNM_CTRL_MASK) == LV_BTNM_CTRL_CODE) &&
(btn_str[0] & LV_BTNM_INACTIVE_MASK)) {
return true;
}
return false;
}
const char * cut_ctrl_byte(const char * btn_str)
{
/*Cut the control byte if present*/
if((btn_str[0] & LV_BTNM_CTRL_MASK) == LV_BTNM_CTRL_CODE) return &btn_str[1];
else return btn_str;
}
/**
* Gives the button id of a button under a given point
* @param btnm pointer to a button matrix object
* @param p a point with absolute coordinates
* @return the id of the button or LV_BTNM_PR_NONE.
*/
static uint16_t get_button_from_point(lv_obj_t * btnm, lv_point_t * p)
{
lv_area_t btnm_cords;
lv_area_t btn_area;
lv_btnm_ext_t * ext = lv_obj_get_ext_attr(btnm);
uint16_t i;
lv_obj_get_coords(btnm, &btnm_cords);
for(i = 0; i < ext->btn_cnt; i++) {
lv_area_copy(&btn_area, &ext->button_areas[i]);
btn_area.x1 += btnm_cords.x1;
btn_area.y1 += btnm_cords.y1;
btn_area.x2 += btnm_cords.x1;
btn_area.y2 += btnm_cords.y1;
if(lv_area_is_point_on(&btn_area, p) != false) {
break;
}
}
if(i == ext->btn_cnt) i = LV_BTNM_PR_NONE;
return i;
}
/**
* Get the text of a button
* @param btnm pointer to a button matrix object
* @param btn_id button id
* @return text id in ext->map_p or LV_BTNM_PR_NONE if 'btn_id' was invalid
*/
static uint16_t get_button_text(lv_obj_t * btnm, uint16_t btn_id)
{
lv_btnm_ext_t * ext = lv_obj_get_ext_attr(btnm);
if(btn_id > ext->btn_cnt) return LV_BTNM_PR_NONE;
uint16_t txt_i = 0;
uint16_t btn_i = 0;
/* Search the text of ext->btn_pr the buttons text in the map
* Skip "\n"-s*/
while(btn_i != btn_id) {
btn_i ++;
txt_i ++;
if(strcmp(ext->map_p[txt_i], "\n") == 0) txt_i ++;
}
if(btn_i == ext->btn_cnt) return LV_BTNM_PR_NONE;
return txt_i;
}
#endif

View File

@@ -0,0 +1,197 @@
/**
* @file lv_btnm.h
*
*/
#ifndef LV_BTNM_H
#define LV_BTNM_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#ifdef LV_CONF_INCLUDE_SIMPLE
#include "lv_conf.h"
#else
#include "../../lv_conf.h"
#endif
#if USE_LV_BTNM != 0
#include "../lv_core/lv_obj.h"
#include "lv_label.h"
#include "lv_btn.h"
/*********************
* DEFINES
*********************/
/*Control byte*/
#define LV_BTNM_CTRL_CODE 0x80 /*The control byte has to begin (if present) with 0b10xxxxxx*/
#define LV_BTNM_CTRL_MASK 0xC0
#define LV_BTNM_WIDTH_MASK 0x07
#define LV_BTNM_HIDE_MASK 0x08
#define LV_BTNM_REPEAT_DISABLE_MASK 0x10
#define LV_BTNM_INACTIVE_MASK 0x20
#define LV_BTNM_PR_NONE 0xFFFF
/**********************
* TYPEDEFS
**********************/
/* Type of callback function which is called when a button is released or long pressed on the button matrix
* Parameters: button matrix, text of the released button
* return LV_ACTION_RES_INV if the button matrix is deleted else LV_ACTION_RES_OK*/
typedef lv_res_t (*lv_btnm_action_t) (lv_obj_t *, const char *txt);
/*Data of button matrix*/
typedef struct
{
/*No inherited ext.*/ /*Ext. of ancestor*/
/*New data for this type */
const char ** map_p; /*Pointer to the current map*/
lv_area_t *button_areas; /*Array of areas of buttons*/
lv_btnm_action_t action; /*A function to call when a button is releases*/
lv_style_t *styles_btn[LV_BTN_STATE_NUM]; /*Styles of buttons in each state*/
uint16_t btn_cnt; /*Number of button in 'map_p'(Handled by the library)*/
uint16_t btn_id_pr; /*Index of the currently pressed button (in `button_areas`) or LV_BTNM_PR_NONE*/
uint16_t btn_id_tgl; /*Index of the currently toggled button (in `button_areas`) or LV_BTNM_PR_NONE */
uint8_t toggle :1; /*Enable toggling*/
uint8_t recolor :1; /*Enable button recoloring*/
} lv_btnm_ext_t;
enum {
LV_BTNM_STYLE_BG,
LV_BTNM_STYLE_BTN_REL,
LV_BTNM_STYLE_BTN_PR,
LV_BTNM_STYLE_BTN_TGL_REL,
LV_BTNM_STYLE_BTN_TGL_PR,
LV_BTNM_STYLE_BTN_INA,
};
typedef uint8_t lv_btnm_style_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a button matrix objects
* @param par pointer to an object, it will be the parent of the new button matrix
* @param copy pointer to a button matrix object, if not NULL then the new object will be copied from it
* @return pointer to the created button matrix
*/
lv_obj_t * lv_btnm_create(lv_obj_t * par, const lv_obj_t * copy);
/*=====================
* Setter functions
*====================*/
/**
* Set a new map. Buttons will be created/deleted according to the map.
* @param btnm pointer to a button matrix object
* @param map pointer a string array. The last string has to be: "".
* Use "\n" to begin a new line.
* The first byte can be a control data:
* - bit 7: always 1
* - bit 6: always 0
* - bit 5: inactive (disabled)
* - bit 4: no repeat (on long press)
* - bit 3: hidden
* - bit 2..0: button relative width
* Example (practically use octal numbers): "\224abc": "abc" text with 4 width and no long press
*/
void lv_btnm_set_map(lv_obj_t * btnm, const char ** map);
/**
* Set a new callback function for the buttons (It will be called when a button is released)
* @param btnm: pointer to button matrix object
* @param action pointer to a callback function
*/
void lv_btnm_set_action(lv_obj_t * btnm, lv_btnm_action_t action);
/**
* Enable or disable button toggling
* @param btnm pointer to button matrix object
* @param en true: enable toggling; false: disable toggling
* @param id index of the currently toggled button (ignored if 'en' == false)
*/
void lv_btnm_set_toggle(lv_obj_t * btnm, bool en, uint16_t id);
/**
* Set a style of a button matrix
* @param btnm pointer to a button matrix object
* @param type which style should be set
* @param style pointer to a style
*/
void lv_btnm_set_style(lv_obj_t *btnm, lv_btnm_style_t type, lv_style_t *style);
/**
* Set whether recoloring is enabled
* @param btnm pointer to button matrix object
* @param en whether recoloring is enabled
*/
void lv_btnm_set_recolor(const lv_obj_t * btnm, bool en);
/*=====================
* Getter functions
*====================*/
/**
* Get the current map of a button matrix
* @param btnm pointer to a button matrix object
* @return the current map
*/
const char ** lv_btnm_get_map(const lv_obj_t * btnm);
/**
* Get a the callback function of the buttons on a button matrix
* @param btnm: pointer to button matrix object
* @return pointer to the callback function
*/
lv_btnm_action_t lv_btnm_get_action(const lv_obj_t * btnm);
/**
* Get the pressed button
* @param btnm pointer to button matrix object
* @return index of the currently pressed button (LV_BTNM_PR_NONE: if unset)
*/
uint16_t lv_btnm_get_pressed(const lv_obj_t * btnm);
/**
* Get the toggled button
* @param btnm pointer to button matrix object
* @return index of the currently toggled button (LV_BTNM_PR_NONE: if unset)
*/
uint16_t lv_btnm_get_toggled(const lv_obj_t * btnm);
/**
* Get a style of a button matrix
* @param btnm pointer to a button matrix object
* @param type which style should be get
* @return style pointer to a style
*/
lv_style_t * lv_btnm_get_style(const lv_obj_t *btnm, lv_btnm_style_t type);
/**
* Find whether recoloring is enabled
* @param btnm pointer to button matrix object
* @return whether recoloring is enabled
*/
bool lv_btnm_get_recolor(const lv_obj_t * btnm);
/**********************
* MACROS
**********************/
#endif /*USE_LV_BTNM*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_BTNM_H*/

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,246 @@
/**
* @file lv_calendar.h
*
*/
#ifndef LV_CALENDAR_H
#define LV_CALENDAR_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#ifdef LV_CONF_INCLUDE_SIMPLE
#include "lv_conf.h"
#else
#include "../../lv_conf.h"
#endif
#if USE_LV_CALENDAR != 0
#include "../lv_core/lv_obj.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct {
uint16_t year;
int8_t month;
int8_t day;
} lv_calendar_date_t;
enum
{
LV_CALENDAR_ACTION_CLICK,
LV_CALENDAR_ACTION_PR,
LV_CALENDAR_ACTION_LONG_PR,
LV_CALENDAR_ACTION_LONG_PR_REPEAT,
LV_CALENDAR_ACTION_NUM,
};
typedef uint8_t lv_calendar_action_t;
/*Data of calendar*/
typedef struct {
/*None*/ /*Ext. of ancestor*/
/*New data for this type */
lv_calendar_date_t today; /*Date of today*/
lv_calendar_date_t showed_date; /*Currently visible month (day is ignored)*/
lv_calendar_date_t * highlighted_dates; /*Apply different style on these days (pointer to an array defined by the user)*/
uint8_t highlighted_dates_num; /*Number of elements in `highlighted_days`*/
int8_t btn_pressing; /*-1: prev month pressing, +1 next month pressing on the header*/
lv_calendar_date_t pressed_date;
const char ** day_names; /*Pointer to an array with the name of the days (NULL: use default names)*/
const char ** month_names; /*Pointer to an array with the name of the month (NULL. use default names)*/
lv_action_t actions[LV_CALENDAR_ACTION_NUM];
/*Styles*/
lv_style_t * style_header;
lv_style_t * style_header_pr;
lv_style_t * style_day_names;
lv_style_t * style_highlighted_days;
lv_style_t * style_inactive_days;
lv_style_t * style_week_box;
lv_style_t * style_today_box;
} lv_calendar_ext_t;
/*Styles*/
enum {
LV_CALENDAR_STYLE_BG, /*Also the style of the "normal" date numbers*/
LV_CALENDAR_STYLE_HEADER,
LV_CALENDAR_STYLE_HEADER_PR,
LV_CALENDAR_STYLE_DAY_NAMES,
LV_CALENDAR_STYLE_HIGHLIGHTED_DAYS,
LV_CALENDAR_STYLE_INACTIVE_DAYS,
LV_CALENDAR_STYLE_WEEK_BOX,
LV_CALENDAR_STYLE_TODAY_BOX,
};
typedef uint8_t lv_calendar_style_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a calendar objects
* @param par pointer to an object, it will be the parent of the new calendar
* @param copy pointer to a calendar object, if not NULL then the new object will be copied from it
* @return pointer to the created calendar
*/
lv_obj_t * lv_calendar_create(lv_obj_t * par, const lv_obj_t * copy);
/*======================
* Add/remove functions
*=====================*/
/*=====================
* Setter functions
*====================*/
/**
* Set a function to call when a calendar event happens
* @param calendar pointer to a calendar object
* @param action type of event form 'lv_action_t' (press, release, long press, long press repeat)
*/
void lv_calendar_set_action(lv_obj_t * calendar, lv_calendar_action_t type, lv_action_t action);
/**
* Set the today's date
* @param calendar pointer to a calendar object
* @param today pointer to an `lv_calendar_date_t` variable containing the date of today. The value will be saved it can be local variable too.
*/
void lv_calendar_set_today_date(lv_obj_t * calendar, lv_calendar_date_t * today);
/**
* Set the currently showed
* @param calendar pointer to a calendar object
* @param showed pointer to an `lv_calendar_date_t` variable containing the date to show. The value will be saved it can be local variable too.
*/
void lv_calendar_set_showed_date(lv_obj_t * calendar, lv_calendar_date_t * showed);
/**
* Set the the highlighted dates
* @param calendar pointer to a calendar object
* @param highlighted pointer to an `lv_calendar_date_t` array containing the dates. ONLY A POINTER WILL BE SAVED! CAN'T BE LOCAL ARRAY.
* @param date_num number of dates in the array
*/
void lv_calendar_set_highlighted_dates(lv_obj_t * calendar, lv_calendar_date_t * highlighted, uint16_t date_num);
/**
* Set the name of the days
* @param calendar pointer to a calendar object
* @param day_names pointer to an array with the names. E.g. `const char * days[7] = {"Sun", "Mon", ...}`
* Only the pointer will be saved so this variable can't be local which will be destroyed later.
*/
void lv_calendar_set_day_names(lv_obj_t * calendar, const char ** day_names);
/**
* Set the name of the month
* @param calendar pointer to a calendar object
* @param day_names pointer to an array with the names. E.g. `const char * days[12] = {"Jan", "Feb", ...}`
* Only the pointer will be saved so this variable can't be local which will be destroyed later.
*/
void lv_calendar_set_month_names(lv_obj_t * calendar, const char ** day_names);
/**
* Set a style of a calendar.
* @param calendar pointer to calendar object
* @param type which style should be set
* @param style pointer to a style
* */
void lv_calendar_set_style(lv_obj_t * calendar, lv_calendar_style_t type, lv_style_t *style);
/*=====================
* Getter functions
*====================*/
/**
* Get the action of a calendar
* @param calendar pointer to a calendar object
* @return pointer to the action function
*/
lv_action_t lv_calendar_get_action(const lv_obj_t * calendar, lv_calendar_action_t type);
/**
* Get the today's date
* @param calendar pointer to a calendar object
* @return return pointer to an `lv_calendar_date_t` variable containing the date of today.
*/
lv_calendar_date_t * lv_calendar_get_today_date(const lv_obj_t * calendar);
/**
* Get the currently showed
* @param calendar pointer to a calendar object
* @return pointer to an `lv_calendar_date_t` variable containing the date is being shown.
*/
lv_calendar_date_t * lv_calendar_get_showed_date(const lv_obj_t * calendar);
/**
* Get the the pressed date.
* @param calendar pointer to a calendar object
* @return pointer to an `lv_calendar_date_t` variable containing the pressed date.
*/
lv_calendar_date_t * lv_calendar_get_pressed_date(const lv_obj_t * calendar);
/**
* Get the the highlighted dates
* @param calendar pointer to a calendar object
* @return pointer to an `lv_calendar_date_t` array containing the dates.
*/
lv_calendar_date_t * lv_calendar_get_highlighted_dates(const lv_obj_t * calendar);
/**
* Get the number of the highlighted dates
* @param calendar pointer to a calendar object
* @return number of highlighted days
*/
uint16_t lv_calendar_get_highlighted_dates_num(const lv_obj_t * calendar);
/**
* Get the name of the days
* @param calendar pointer to a calendar object
* @return pointer to the array of day names
*/
const char ** lv_calendar_get_day_names(const lv_obj_t * calendar);
/**
* Get the name of the month
* @param calendar pointer to a calendar object
* @return pointer to the array of month names
*/
const char ** lv_calendar_get_month_names(const lv_obj_t * calendar);
/**
* Get style of a calendar.
* @param calendar pointer to calendar object
* @param type which style should be get
* @return style pointer to the style
* */
lv_style_t * lv_calendar_get_style(const lv_obj_t * calendar, lv_calendar_style_t type);
/*=====================
* Other functions
*====================*/
/**********************
* MACROS
**********************/
#endif /*USE_LV_CALENDAR*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_CALENDAR_H*/

View File

@@ -0,0 +1,593 @@
/**
* @file lv_canvas.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_canvas.h"
#if USE_LV_CANVAS != 0
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static lv_res_t lv_canvas_signal(lv_obj_t * canvas, lv_signal_t sign, void * param);
/**********************
* STATIC VARIABLES
**********************/
static lv_signal_func_t ancestor_signal;
static lv_design_func_t ancestor_design;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Create a canvas object
* @param par pointer to an object, it will be the parent of the new canvas
* @param copy pointer to a canvas object, if not NULL then the new object will be copied from it
* @return pointer to the created canvas
*/
lv_obj_t * lv_canvas_create(lv_obj_t * par, const lv_obj_t * copy)
{
LV_LOG_TRACE("canvas create started");
/*Create the ancestor of canvas*/
lv_obj_t * new_canvas = lv_img_create(par, copy);
lv_mem_assert(new_canvas);
if(new_canvas == NULL) return NULL;
/*Allocate the canvas type specific extended data*/
lv_canvas_ext_t * ext = lv_obj_allocate_ext_attr(new_canvas, sizeof(lv_canvas_ext_t));
lv_mem_assert(ext);
if(ext == NULL) return NULL;
if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_canvas);
if(ancestor_design == NULL) ancestor_design = lv_obj_get_design_func(new_canvas);
/*Initialize the allocated 'ext' */
ext->dsc.header.always_zero = 0;
ext->dsc.header.cf = LV_IMG_CF_TRUE_COLOR;
ext->dsc.header.h = 0;
ext->dsc.header.w = 0;
ext->dsc.data_size = 0;
ext->dsc.data = NULL;
lv_img_set_src(new_canvas, &ext->dsc);
/*The signal and design functions are not copied so set them here*/
lv_obj_set_signal_func(new_canvas, lv_canvas_signal);
/*Init the new canvas canvas*/
if(copy == NULL) {
}
/*Copy an existing canvas*/
else {
//lv_canvas_ext_t * copy_ext = lv_obj_get_ext_attr(copy);
/*Refresh the style with new signal function*/
lv_obj_refresh_style(new_canvas);
}
LV_LOG_INFO("canvas created");
return new_canvas;
}
/*=====================
* 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 * h) / 8)
* 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. The following formats are supported:
* LV_IMG_CF_TRUE_COLOR, LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED, LV_IMG_CF_INDEXES_1/2/4/8BIT
*
*/
void lv_canvas_set_buffer(lv_obj_t * canvas, void * buf, lv_coord_t w, lv_coord_t h, lv_img_cf_t cf)
{
lv_canvas_ext_t * ext = lv_obj_get_ext_attr(canvas);
ext->dsc.header.cf = cf;
ext->dsc.header.w = w;
ext->dsc.header.h = h;
ext->dsc.data = buf;
ext->dsc.data_size = (lv_img_color_format_get_px_size(cf) * w * h) / 8;
lv_img_set_src(canvas, &ext->dsc);
}
/**
* 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 point
*/
void lv_canvas_set_px(lv_obj_t * canvas, lv_coord_t x, lv_coord_t y, lv_color_t c)
{
lv_canvas_ext_t * ext = lv_obj_get_ext_attr(canvas);
if(x >= ext->dsc.header.w || y >= ext->dsc.header.h) {
LV_LOG_WARN("lv_canvas_set_px: x or y out of the canvas");
return;
}
uint8_t * buf_u8 = (uint8_t *) ext->dsc.data;
if(ext->dsc.header.cf == LV_IMG_CF_TRUE_COLOR ||
ext->dsc.header.cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED)
{
uint32_t px = ext->dsc.header.w * y * sizeof(lv_color_t) + x * sizeof(lv_color_t);
memcpy(&buf_u8[px], &c, sizeof(lv_color_t));
}
else if(ext->dsc.header.cf == LV_IMG_CF_INDEXED_1BIT) {
buf_u8 += 4 * 2;
uint8_t bit = x & 0x7;
x = x >> 3;
uint32_t px = (ext->dsc.header.w >> 3) * y + x;
buf_u8[px] = buf_u8[px] & ~(1 << (7 - bit));
buf_u8[px] = buf_u8[px] | ((c.full & 0x1) << (7 - bit));
}
else if(ext->dsc.header.cf == LV_IMG_CF_INDEXED_2BIT) {
buf_u8 += 4 * 4;
uint8_t bit = (x & 0x3) * 2;
x = x >> 2;
uint32_t px = (ext->dsc.header.w >> 2) * y + x;
buf_u8[px] = buf_u8[px] & ~(3 << (6 - bit));
buf_u8[px] = buf_u8[px] | ((c.full & 0x3) << (6 - bit));
}
else if(ext->dsc.header.cf == LV_IMG_CF_INDEXED_4BIT) {
buf_u8 += 4 * 16;
uint8_t bit = (x & 0x1) * 4;
x = x >> 1;
uint32_t px = (ext->dsc.header.w >> 1) * y + x;
buf_u8[px] = buf_u8[px] & ~(0xF << (4 - bit));
buf_u8[px] = buf_u8[px] | ((c.full & 0xF) << (4 - bit));
}
else if(ext->dsc.header.cf == LV_IMG_CF_INDEXED_8BIT) {
buf_u8 += 4 * 256;
uint32_t px = ext->dsc.header.w * y + x;
buf_u8[px] = c.full;
}
}
/**
* Set a style of a canvas.
* @param canvas pointer to canvas object
* @param type which style should be set
* @param style pointer to a style
*/
void lv_canvas_set_style(lv_obj_t * canvas, lv_canvas_style_t type, lv_style_t * style)
{
switch(type) {
case LV_CANVAS_STYLE_MAIN:
lv_img_set_style(canvas, style);
break;
}
}
/*=====================
* 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)
{
lv_color_t p_color = LV_COLOR_BLACK;
lv_canvas_ext_t * ext = lv_obj_get_ext_attr(canvas);
if(x >= ext->dsc.header.w || y >= ext->dsc.header.h) {
LV_LOG_WARN("lv_canvas_get_px: x or y out of the canvas");
return p_color;
}
uint8_t * buf_u8 = (uint8_t *) ext->dsc.data;
if(ext->dsc.header.cf == LV_IMG_CF_TRUE_COLOR ||
ext->dsc.header.cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED)
{
uint32_t px = ext->dsc.header.w * y * sizeof(lv_color_t) + x * sizeof(lv_color_t);
memcpy(&p_color, &buf_u8[px], sizeof(lv_color_t));
}
else if(ext->dsc.header.cf == LV_IMG_CF_INDEXED_1BIT) {
buf_u8 += 4 * 2;
uint8_t bit = x & 0x7;
x = x >> 3;
uint32_t px = (ext->dsc.header.w >> 3) * y + x;
p_color.full = (buf_u8[px] & (1 << (7 - bit))) >> (7 - bit);
}
else if(ext->dsc.header.cf == LV_IMG_CF_INDEXED_2BIT) {
buf_u8 += 4 * 4;
uint8_t bit = (x & 0x3) * 2;
x = x >> 2;
uint32_t px = (ext->dsc.header.w >> 2) * y + x;
p_color.full = (buf_u8[px] & (3 << (6 - bit))) >> (6 - bit);
}
else if(ext->dsc.header.cf == LV_IMG_CF_INDEXED_4BIT) {
buf_u8 += 4 * 16;
uint8_t bit = (x & 0x1) * 4;
x = x >> 1;
uint32_t px = (ext->dsc.header.w >> 1) * y + x;
p_color.full = (buf_u8[px] & (0xF << (4 - bit))) >> (4 - bit);
}
else if(ext->dsc.header.cf == LV_IMG_CF_INDEXED_8BIT) {
buf_u8 += 4 * 256;
uint32_t px = ext->dsc.header.w * y + x;
p_color.full = buf_u8[px];
}
return p_color;
}
/**
* Get style of a canvas.
* @param canvas pointer to canvas object
* @param type which style should be get
* @return style pointer to the style
*/
lv_style_t * lv_canvas_get_style(const lv_obj_t * canvas, lv_canvas_style_t type)
{
// lv_canvas_ext_t * ext = lv_obj_get_ext_attr(canvas);
lv_style_t * style = NULL;
switch(type) {
case LV_CANVAS_STYLE_MAIN:
style = lv_img_get_style(canvas);
break;
default:
style = NULL;
}
return style;
}
/*=====================
* 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 w width of the buffer to copy
* @param h height of the buffer to copy
* @param x left side of the destination position
* @param y top side of the destination position
*/
void lv_canvas_copy_buf(lv_obj_t * canvas, const void * to_copy, lv_coord_t w, lv_coord_t h, lv_coord_t x, lv_coord_t y)
{
lv_canvas_ext_t * ext = lv_obj_get_ext_attr(canvas);
if(x + w >= ext->dsc.header.w || y + h >= ext->dsc.header.h) {
LV_LOG_WARN("lv_canvas_copy_buf: x or y out of the canvas");
return;
}
uint32_t px_size = lv_img_color_format_get_px_size(ext->dsc.header.cf) >> 3;
uint32_t px = ext->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++) {
memcpy((void*)&ext->dsc.data[px], to_copy8, w * px_size);
px += ext->dsc.header.w * px_size;
to_copy8 += w * px_size;
}
}
/**
* Multiply a buffer with the canvas
* @param canvas pointer to a canvas object
* @param to_copy buffer to copy (multiply). LV_IMG_CF_TRUE_COLOR_ALPHA is not supported
* @param w width of the buffer to copy
* @param h height of the buffer to copy
* @param x left side of the destination position
* @param y top side of the destination position
*/
void lv_canvas_mult_buf(lv_obj_t * canvas, void * to_copy, lv_coord_t w, lv_coord_t h, lv_coord_t x, lv_coord_t y)
{
lv_canvas_ext_t * ext = lv_obj_get_ext_attr(canvas);
if(x + w >= ext->dsc.header.w || y + h >= ext->dsc.header.h) {
LV_LOG_WARN("lv_canvas_mult_buf: x or y out of the canvas");
return;
}
if(ext->dsc.header.cf == LV_IMG_CF_TRUE_COLOR_ALPHA) {
LV_LOG_WARN("lv_canvas_mult_buf: LV_IMG_CF_TRUE_COLOR_ALPHA is not supported");
return;
}
uint32_t px_size = lv_img_color_format_get_px_size(ext->dsc.header.cf) >> 3;
uint32_t px = ext->dsc.header.w * y * px_size + x * px_size;
lv_color_t * copy_buf_color = (lv_color_t *) to_copy;
lv_color_t * canvas_buf_color = (lv_color_t *) &ext->dsc.data[px];
lv_coord_t i;
lv_coord_t j;
for(i = 0; i < h; i++) {
for(j = 0; j < w; j++) {
#if LV_COLOR_DEPTH == 32
canvas_buf_color[j].red = (uint16_t) ((uint16_t) canvas_buf_color[j].red * copy_buf_color[j].red) >> 8;
canvas_buf_color[j].green = (uint16_t) ((uint16_t) canvas_buf_color[j].green * copy_buf_color[j].green) >> 8;
canvas_buf_color[j].blue = (uint16_t) ((uint16_t) canvas_buf_color[j].blue * copy_buf_color[j].blue) >> 8;
#elif LV_COLOR_DEPTH == 16
canvas_buf_color[j].red = (uint16_t) ((uint16_t) canvas_buf_color[j].red * copy_buf_color[j].red) >> 5;
canvas_buf_color[j].blue = (uint16_t) ((uint16_t) canvas_buf_color[j].blue * copy_buf_color[j].blue) >> 5;
# if LV_COLOR_16_SWAP == 0
canvas_buf_color[j].green = (uint16_t) ((uint16_t) canvas_buf_color[j].green * copy_buf_color[j].green) >> 6;
# else
uint8_t green_canvas = (canvas_buf_color[j].green_h << 3) + (canvas_buf_color[j].green_l);
uint8_t green_buf = (copy_buf_color[j].green_h << 3) + (copy_buf_color[j].green_l);
uint8_t green_res = (uint16_t)((uint16_t)green_canvas * green_buf) >> 6;
canvas_buf_color[j].green_h = (green_res >> 3) & 0x07;
canvas_buf_color[j].green_l = green_res & 0x07;
# endif /*LV_COLOR_16_SWAP*/
#elif LV_COLOR_DEPTH == 8
canvas_buf_color[j].red = (uint16_t) ((uint16_t) canvas_buf_color[j].red * copy_buf_color[j].red) >> 3;
canvas_buf_color[j].green = (uint16_t) ((uint16_t) canvas_buf_color[j].green * copy_buf_color[j].green) >> 3;
canvas_buf_color[j].blue = (uint16_t) ((uint16_t) canvas_buf_color[j].blue * copy_buf_color[j].blue) >> 2;
#endif
}
copy_buf_color += w;
canvas_buf_color += ext->dsc.header.w;
}
}
/**
* Draw circle function of the canvas
* @param canvas pointer to a canvas object
* @param x0 x coordinate of the circle
* @param y0 y coordinate of the circle
* @param radius radius of the circle
* @param color border color of the circle
*/
void lv_canvas_draw_circle(lv_obj_t * canvas, lv_coord_t x0, lv_coord_t y0, lv_coord_t radius, lv_color_t color)
{
int x = radius;
int y = 0;
int err = 0;
while (x >= y)
{
lv_canvas_set_px(canvas, x0 + x, y0 + y, color);
lv_canvas_set_px(canvas, x0 + y, y0 + x, color);
lv_canvas_set_px(canvas, x0 - y, y0 + x, color);
lv_canvas_set_px(canvas, x0 - x, y0 + y, color);
lv_canvas_set_px(canvas, x0 - x, y0 - y, color);
lv_canvas_set_px(canvas, x0 - y, y0 - x, color);
lv_canvas_set_px(canvas, x0 + y, y0 - x, color);
lv_canvas_set_px(canvas, x0 + x, y0 - y, color);
if (err <= 0)
{
y += 1;
err += 2*y + 1;
}
if (err > 0)
{
x -= 1;
err -= 2*x + 1;
}
}
}
/**
* Draw line function of the canvas
* @param canvas pointer to a canvas object
* @param point1 start point of the line
* @param point2 end point of the line
* @param color color of the line
*
* NOTE: The lv_canvas_draw_line function originates from https://github.com/jb55/bresenham-line.c.
*/
/*
* NOTE: The lv_canvas_draw_line function originates from https://github.com/jb55/bresenham-line.c.
*/
void lv_canvas_draw_line(lv_obj_t * canvas, lv_point_t point1, lv_point_t point2, lv_color_t color)
{
lv_coord_t x0, y0, x1, y1;
x0 = point1.x;
y0 = point1.y;
x1 = point2.x;
y1 = point2.y;
int dx = abs(x1-x0), sx = x0<x1 ? 1 : -1;
int dy = abs(y1-y0), sy = y0<y1 ? 1 : -1;
int err = (dx>dy ? dx : -dy)/2, e2;
for(;;){
lv_canvas_set_px(canvas, x0, y0, color);
if (x0==x1 && y0==y1) break;
e2 = err;
if (e2 >-dx) { err -= dy; x0 += sx; }
if (e2 < dy) { err += dx; y0 += sy; }
}
}
/**
* Draw triangle function of the canvas
* @param canvas pointer to a canvas object
* @param points edge points of the triangle
* @param color line color of the triangle
*/
void lv_canvas_draw_triangle(lv_obj_t * canvas, lv_point_t * points, lv_color_t color)
{
lv_canvas_draw_polygon(canvas, points, 3, color);
}
/**
* Draw rectangle function of the canvas
* @param canvas pointer to a canvas object
* @param points edge points of the rectangle
* @param color line color of the rectangle
*/
void lv_canvas_draw_rect(lv_obj_t * canvas, lv_point_t * points, lv_color_t color)
{
lv_canvas_draw_polygon(canvas, points, 4, color);
}
/**
* Draw polygon function of the canvas
* @param canvas pointer to a canvas object
* @param points edge points of the polygon
* @param size edge count of the polygon
* @param color line color of the polygon
*/
void lv_canvas_draw_polygon(lv_obj_t * canvas, lv_point_t * points, size_t size, lv_color_t color)
{
uint8_t i;
for(i=0; i < (size - 1); i++) {
lv_canvas_draw_line(canvas, points[i], points[i + 1], color);
}
lv_canvas_draw_line(canvas, points[size - 1], points[0], color);
}
/**
* Fill polygon function of the canvas
* @param canvas pointer to a canvas object
* @param points edge points of the polygon
* @param size edge count of the polygon
* @param boundary_color line color of the polygon
* @param fill_color fill color of the polygon
*/
void lv_canvas_fill_polygon(lv_obj_t * canvas, lv_point_t * points, size_t size, lv_color_t boundary_color, lv_color_t fill_color)
{
uint32_t x = 0, y = 0;
uint8_t i;
for(i=0; i<size; i++) {
x += points[i].x;
y += points[i].y;
}
x = x / size;
y = y / size;
lv_canvas_boundary_fill4(canvas, (lv_coord_t) x, (lv_coord_t) y, boundary_color, fill_color);
}
/**
* Boundary fill function of the canvas
* @param canvas pointer to a canvas object
* @param x x coordinate of the start position (seed)
* @param y y coordinate of the start position (seed)
* @param boundary_color edge/boundary color of the area
* @param fill_color fill color of the area
*/
void lv_canvas_boundary_fill4(lv_obj_t * canvas, lv_coord_t x, lv_coord_t y, lv_color_t boundary_color, lv_color_t fill_color)
{
lv_color_t c;
c = lv_canvas_get_px(canvas, x, y);
if(c.full != boundary_color.full &&
c.full != fill_color.full)
{
lv_canvas_set_px(canvas, x, y, fill_color);
lv_canvas_boundary_fill4(canvas, x + 1, y, boundary_color, fill_color);
lv_canvas_boundary_fill4(canvas, x, y + 1, boundary_color, fill_color);
lv_canvas_boundary_fill4(canvas, x - 1, y, boundary_color, fill_color);
lv_canvas_boundary_fill4(canvas, x, y - 1, boundary_color, fill_color);
}
}
/**
* Flood fill function of the canvas
* @param canvas pointer to a canvas object
* @param x x coordinate of the start position (seed)
* @param y y coordinate of the start position (seed)
* @param fill_color fill color of the area
* @param bg_color background color of the area
*/
void lv_canvas_flood_fill(lv_obj_t * canvas, lv_coord_t x, lv_coord_t y, lv_color_t fill_color, lv_color_t bg_color)
{
lv_color_t c;
c = lv_canvas_get_px(canvas, x, y);
if(c.full == bg_color.full)
{
lv_canvas_set_px(canvas, x, y, fill_color);
lv_canvas_flood_fill(canvas, x+1, y, fill_color, bg_color);
lv_canvas_flood_fill(canvas, x, y+1, fill_color, bg_color);
lv_canvas_flood_fill(canvas, x-1, y, fill_color, bg_color);
lv_canvas_flood_fill(canvas, x, y-1, fill_color, bg_color);
}
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Signal function of the canvas
* @param canvas pointer to a canvas object
* @param sign a signal type from lv_signal_t enum
* @param param pointer to a signal specific variable
* @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
*/
static lv_res_t lv_canvas_signal(lv_obj_t * canvas, lv_signal_t sign, void * param)
{
lv_res_t res;
/* Include the ancient signal function */
res = ancestor_signal(canvas, sign, param);
if(res != LV_RES_OK) return res;
if(sign == LV_SIGNAL_CLEANUP) {
/*Nothing to cleanup. (No dynamically allocated memory in 'ext')*/
} else if(sign == LV_SIGNAL_GET_TYPE) {
lv_obj_type_t * buf = param;
uint8_t i;
for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/
if(buf->type[i] == NULL) break;
}
buf->type[i] = "lv_canvas";
}
return res;
}
#endif

View File

@@ -0,0 +1,229 @@
/**
* @file lv_canvas.h
*
*/
#ifndef LV_CANVAS_H
#define LV_CANVAS_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#ifdef LV_CONF_INCLUDE_SIMPLE
#include "lv_conf.h"
#else
#include "../../lv_conf.h"
#endif
#if USE_LV_CANVAS != 0
#include "../lv_core/lv_obj.h"
#include "../lv_objx/lv_img.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/*Data of canvas*/
typedef struct {
lv_img_ext_t img; /*Ext. of ancestor*/
/*New data for this type */
lv_img_dsc_t dsc;
} lv_canvas_ext_t;
/*Styles*/
enum {
LV_CANVAS_STYLE_MAIN,
};
typedef uint8_t lv_canvas_style_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a canvas object
* @param par pointer to an object, it will be the parent of the new canvas
* @param copy pointer to a canvas object, if not NULL then the new object will be copied from it
* @return pointer to the created canvas
*/
lv_obj_t * lv_canvas_create(lv_obj_t * par, const lv_obj_t * copy);
/*=====================
* 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 * h) / 8)
* 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. The following formats are supported:
* LV_IMG_CF_TRUE_COLOR, LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED, LV_IMG_CF_INDEXES_1/2/4/8BIT
*/
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 point
*/
void lv_canvas_set_px(lv_obj_t * canvas, lv_coord_t x, lv_coord_t y, lv_color_t c);
/**
* Set a style of a canvas.
* @param canvas pointer to canvas object
* @param type which style should be set
* @param style pointer to a style
*/
void lv_canvas_set_style(lv_obj_t * canvas, lv_canvas_style_t type, lv_style_t * style);
/*=====================
* 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 style of a canvas.
* @param canvas pointer to canvas object
* @param type which style should be get
* @return style pointer to the style
*/
lv_style_t * lv_canvas_get_style(const lv_obj_t * canvas, lv_canvas_style_t type);
/*=====================
* 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 w width of the buffer to copy
* @param h height of the buffer to copy
* @param x left side of the destination position
* @param y top side of the destination position
*/
void lv_canvas_copy_buf(lv_obj_t * canvas, const void * to_copy, lv_coord_t w, lv_coord_t h, lv_coord_t x, lv_coord_t y);
/**
* Multiply a buffer with the canvas
* @param canvas pointer to a canvas object
* @param to_copy buffer to copy (multiply). LV_IMG_CF_TRUE_COLOR_ALPHA is not supported
* @param w width of the buffer to copy
* @param h height of the buffer to copy
* @param x left side of the destination position
* @param y top side of the destination position
*/
void lv_canvas_mult_buf(lv_obj_t * canvas, void * to_copy, lv_coord_t w, lv_coord_t h, lv_coord_t x, lv_coord_t y);
/**
* Draw circle function of the canvas
* @param canvas pointer to a canvas object
* @param x0 x coordinate of the circle
* @param y0 y coordinate of the circle
* @param radius radius of the circle
* @param color border color of the circle
*/
void lv_canvas_draw_circle(lv_obj_t * canvas, lv_coord_t x0, lv_coord_t y0, lv_coord_t radius, lv_color_t color);
/**
* Draw line function of the canvas
* @param canvas pointer to a canvas object
* @param point1 start point of the line
* @param point2 end point of the line
* @param color color of the line
*
* NOTE: The lv_canvas_draw_line function originates from https://github.com/jb55/bresenham-line.c.
*/
void lv_canvas_draw_line(lv_obj_t * canvas, lv_point_t point1, lv_point_t point2, lv_color_t color);
/**
* Draw triangle function of the canvas
* @param canvas pointer to a canvas object
* @param points edge points of the triangle
* @param color line color of the triangle
*/
void lv_canvas_draw_triangle(lv_obj_t * canvas, lv_point_t * points, lv_color_t color);
/**
* Draw rectangle function of the canvas
* @param canvas pointer to a canvas object
* @param points edge points of the rectangle
* @param color line color of the rectangle
*/
void lv_canvas_draw_rect(lv_obj_t * canvas, lv_point_t * points, lv_color_t color);
/**
* Draw polygon function of the canvas
* @param canvas pointer to a canvas object
* @param points edge points of the polygon
* @param size edge count of the polygon
* @param color line color of the polygon
*/
void lv_canvas_draw_polygon(lv_obj_t * canvas, lv_point_t * points, size_t size, lv_color_t color);
/**
* Fill polygon function of the canvas
* @param canvas pointer to a canvas object
* @param points edge points of the polygon
* @param size edge count of the polygon
* @param boundary_color line color of the polygon
* @param fill_color fill color of the polygon
*/
void lv_canvas_fill_polygon(lv_obj_t * canvas, lv_point_t * points, size_t size, lv_color_t boundary_color, lv_color_t fill_color);
/**
* Boundary fill function of the canvas
* @param canvas pointer to a canvas object
* @param x x coordinate of the start position (seed)
* @param y y coordinate of the start position (seed)
* @param boundary_color edge/boundary color of the area
* @param fill_color fill color of the area
*/
void lv_canvas_boundary_fill4(lv_obj_t * canvas, lv_coord_t x, lv_coord_t y, lv_color_t boundary_color, lv_color_t fill_color);
/**
* Flood fill function of the canvas
* @param canvas pointer to a canvas object
* @param x x coordinate of the start position (seed)
* @param y y coordinate of the start position (seed)
* @param fill_color fill color of the area
* @param bg_color background color of the area
*/
void lv_canvas_flood_fill(lv_obj_t * canvas, lv_coord_t x, lv_coord_t y, lv_color_t fill_color, lv_color_t bg_color);
/**********************
* MACROS
**********************/
#endif /*USE_LV_CANVAS*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_CANVAS_H*/

View File

@@ -0,0 +1,347 @@
/**
* @file lv_cb.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_cb.h"
#if USE_LV_CB != 0
#include "../lv_core/lv_group.h"
#include "../lv_themes/lv_theme.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static bool lv_cb_design(lv_obj_t * cb, const lv_area_t * mask, lv_design_mode_t mode);
static bool lv_bullet_design(lv_obj_t * bullet, const lv_area_t * mask, lv_design_mode_t mode);
static lv_res_t lv_cb_signal(lv_obj_t * cb, lv_signal_t sign, void * param);
/**********************
* STATIC VARIABLES
**********************/
static lv_design_func_t ancestor_bg_design;
static lv_design_func_t ancestor_bullet_design;
static lv_signal_func_t ancestor_signal;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Create a check box objects
* @param par pointer to an object, it will be the parent of the new check box
* @param copy pointer to a check box object, if not NULL then the new object will be copied from it
* @return pointer to the created check box
*/
lv_obj_t * lv_cb_create(lv_obj_t * par, const lv_obj_t * copy)
{
LV_LOG_TRACE("check box create started");
/*Create the ancestor basic object*/
lv_obj_t * new_cb = lv_btn_create(par, copy);
lv_mem_assert(new_cb);
if(new_cb == NULL) return NULL;
if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_cb);
if(ancestor_bg_design == NULL) ancestor_bg_design = lv_obj_get_design_func(new_cb);
lv_cb_ext_t * ext = lv_obj_allocate_ext_attr(new_cb, sizeof(lv_cb_ext_t));
lv_mem_assert(ext);
if(ext == NULL) return NULL;
ext->bullet = NULL;
ext->label = NULL;
lv_obj_set_signal_func(new_cb, lv_cb_signal);
lv_obj_set_design_func(new_cb, lv_cb_design);
/*Init the new checkbox object*/
if(copy == NULL) {
ext->bullet = lv_btn_create(new_cb, NULL);
if(ancestor_bullet_design == NULL) ancestor_bullet_design = lv_obj_get_design_func(ext->bullet);
lv_obj_set_click(ext->bullet, false);
ext->label = lv_label_create(new_cb, NULL);
lv_cb_set_text(new_cb, "Check box");
lv_btn_set_layout(new_cb, LV_LAYOUT_ROW_M);
lv_btn_set_fit(new_cb, true, true);
lv_btn_set_toggle(new_cb, true);
lv_obj_set_protect(new_cb, LV_PROTECT_PRESS_LOST);
/*Set the default styles*/
lv_theme_t * th = lv_theme_get_current();
if(th) {
lv_cb_set_style(new_cb, LV_CB_STYLE_BG, th->cb.bg);
lv_cb_set_style(new_cb, LV_CB_STYLE_BOX_REL, th->cb.box.rel);
lv_cb_set_style(new_cb, LV_CB_STYLE_BOX_PR, th->cb.box.pr);
lv_cb_set_style(new_cb, LV_CB_STYLE_BOX_TGL_REL, th->cb.box.tgl_rel);
lv_cb_set_style(new_cb, LV_CB_STYLE_BOX_TGL_PR, th->cb.box.tgl_pr);
lv_cb_set_style(new_cb, LV_CB_STYLE_BOX_INA, th->cb.box.ina);
} else {
lv_cb_set_style(new_cb, LV_CB_STYLE_BG, &lv_style_transp);
lv_cb_set_style(new_cb, LV_CB_STYLE_BOX_REL, &lv_style_pretty);
}
} else {
lv_cb_ext_t * copy_ext = lv_obj_get_ext_attr(copy);
ext->bullet = lv_btn_create(new_cb, copy_ext->bullet);
ext->label = lv_label_create(new_cb, copy_ext->label);
/*Refresh the style with new signal function*/
lv_obj_refresh_style(new_cb);
}
lv_obj_set_design_func(ext->bullet, lv_bullet_design);
LV_LOG_INFO("check box created");
return new_cb;
}
/*=====================
* Setter functions
*====================*/
/**
* Set the text of a check box
* @param cb pointer to a check box
* @param txt the text of the check box
*/
void lv_cb_set_text(lv_obj_t * cb, const char * txt)
{
lv_cb_ext_t * ext = lv_obj_get_ext_attr(cb);
lv_label_set_text(ext->label, txt);
}
/**
* Set a style of a check box
* @param cb pointer to check box object
* @param type which style should be set
* @param style pointer to a style
* */
void lv_cb_set_style(lv_obj_t * cb, lv_cb_style_t type, lv_style_t * style)
{
lv_cb_ext_t * ext = lv_obj_get_ext_attr(cb);
switch(type) {
case LV_CB_STYLE_BG:
lv_btn_set_style(cb, LV_BTN_STYLE_REL, style);
lv_btn_set_style(cb, LV_BTN_STYLE_PR, style);
lv_btn_set_style(cb, LV_BTN_STYLE_TGL_REL, style);
lv_btn_set_style(cb, LV_BTN_STYLE_TGL_PR, style);
lv_btn_set_style(cb, LV_BTN_STYLE_INA, style);
break;
case LV_CB_STYLE_BOX_REL:
lv_btn_set_style(ext->bullet, LV_BTN_STYLE_REL, style);
break;
case LV_CB_STYLE_BOX_PR:
lv_btn_set_style(ext->bullet, LV_BTN_STYLE_PR, style);
break;
case LV_CB_STYLE_BOX_TGL_REL:
lv_btn_set_style(ext->bullet, LV_BTN_STYLE_TGL_REL, style);
break;
case LV_CB_STYLE_BOX_TGL_PR:
lv_btn_set_style(ext->bullet, LV_BTN_STYLE_TGL_PR, style);
break;
case LV_CB_STYLE_BOX_INA:
lv_btn_set_style(ext->bullet, LV_BTN_STYLE_INA, style);
break;
}
}
/*=====================
* 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_cb_get_text(const lv_obj_t * cb)
{
lv_cb_ext_t * ext = lv_obj_get_ext_attr(cb);
return lv_label_get_text(ext->label);
}
/**
* Get a style of a button
* @param cb pointer to check box object
* @param type which style should be get
* @return style pointer to the style
* */
lv_style_t * lv_cb_get_style(const lv_obj_t * cb, lv_cb_style_t type)
{
lv_style_t * style = NULL;
lv_cb_ext_t * ext = lv_obj_get_ext_attr(cb);
switch(type) {
case LV_CB_STYLE_BOX_REL:
style = lv_btn_get_style(ext->bullet, LV_BTN_STYLE_REL);
break;
case LV_CB_STYLE_BOX_PR:
style = lv_btn_get_style(ext->bullet, LV_BTN_STYLE_PR);
break;
case LV_CB_STYLE_BOX_TGL_REL:
style = lv_btn_get_style(ext->bullet, LV_BTN_STYLE_TGL_REL);
break;
case LV_CB_STYLE_BOX_TGL_PR:
style = lv_btn_get_style(ext->bullet, LV_BTN_STYLE_TGL_PR);
break;
case LV_CB_STYLE_BOX_INA:
style = lv_btn_get_style(ext->bullet, LV_BTN_STYLE_INA);
break;
default:
style = NULL;
break;
}
return style;
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Handle the drawing related tasks of the check boxes
* @param cb pointer to an object
* @param mask the object will be drawn only in this area
* @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area
* (return 'true' if yes)
* LV_DESIGN_DRAW: draw the object (always return 'true')
* LV_DESIGN_DRAW_POST: drawing after every children are drawn
* @param return true/false, depends on 'mode'
*/
static bool lv_cb_design(lv_obj_t * cb, const lv_area_t * mask, lv_design_mode_t mode)
{
bool result = true;
if(mode == LV_DESIGN_COVER_CHK) {
/*Return false if the object is not covers the mask_p area*/
result = ancestor_bg_design(cb, mask, mode);
} else if(mode == LV_DESIGN_DRAW_MAIN || mode == LV_DESIGN_DRAW_POST) {
lv_cb_ext_t * cb_ext = lv_obj_get_ext_attr(cb);
lv_btn_ext_t * bullet_ext = lv_obj_get_ext_attr(cb_ext->bullet);
/*Be sure the state of the bullet is the same as the parent button*/
bullet_ext->state = cb_ext->bg_btn.state;
result = ancestor_bg_design(cb, mask, mode);
} else {
result = ancestor_bg_design(cb, mask, mode);
}
return result;
}
/**
* Handle the drawing related tasks of the check boxes
* @param bullet pointer to an object
* @param mask the object will be drawn only in this area
* @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area
* (return 'true' if yes)
* LV_DESIGN_DRAW: draw the object (always return 'true')
* LV_DESIGN_DRAW_POST: drawing after every children are drawn
* @param return true/false, depends on 'mode'
*/
static bool lv_bullet_design(lv_obj_t * bullet, const lv_area_t * mask, lv_design_mode_t mode)
{
if(mode == LV_DESIGN_COVER_CHK) {
return ancestor_bullet_design(bullet, mask, mode);
} else if(mode == LV_DESIGN_DRAW_MAIN) {
#if USE_LV_GROUP
/* If the check box is the active in a group and
* the background is not visible (transparent or empty)
* then activate the style of the bullet*/
lv_style_t * style_ori = lv_obj_get_style(bullet);
lv_obj_t * bg = lv_obj_get_parent(bullet);
lv_style_t * style_page = lv_obj_get_style(bg);
lv_group_t * g = lv_obj_get_group(bg);
if(style_page->body.empty != 0 || style_page->body.opa == LV_OPA_TRANSP) { /*Background is visible?*/
if(lv_group_get_focused(g) == bg) {
lv_style_t * style_mod;
style_mod = lv_group_mod_style(g, style_ori);
bullet->style_p = style_mod; /*Temporally change the style to the activated */
}
}
#endif
ancestor_bullet_design(bullet, mask, mode);
#if USE_LV_GROUP
bullet->style_p = style_ori; /*Revert the style*/
#endif
} else if(mode == LV_DESIGN_DRAW_POST) {
ancestor_bullet_design(bullet, mask, mode);
}
return true;
}
/**
* Signal function of the check box
* @param cb pointer to a check box object
* @param sign a signal type from lv_signal_t enum
* @param param pointer to a signal specific variable
* @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
*/
static lv_res_t lv_cb_signal(lv_obj_t * cb, lv_signal_t sign, void * param)
{
lv_res_t res;
/* Include the ancient signal function */
res = ancestor_signal(cb, sign, param);
if(res != LV_RES_OK) return res;
lv_cb_ext_t * ext = lv_obj_get_ext_attr(cb);
if(sign == LV_SIGNAL_STYLE_CHG) {
lv_style_t * label_style = lv_label_get_style(ext->label);
lv_obj_set_size(ext->bullet, lv_font_get_height(label_style->text.font), lv_font_get_height(label_style->text.font));
lv_btn_set_state(ext->bullet, lv_btn_get_state(cb));
} else if(sign == LV_SIGNAL_PRESSED ||
sign == LV_SIGNAL_RELEASED ||
sign == LV_SIGNAL_PRESS_LOST) {
lv_btn_set_state(ext->bullet, lv_btn_get_state(cb));
} else if(sign == LV_SIGNAL_CONTROLL) {
char c = *((char *)param);
if(c == LV_GROUP_KEY_RIGHT || c == LV_GROUP_KEY_DOWN ||
c == LV_GROUP_KEY_LEFT || c == LV_GROUP_KEY_UP ||
c == LV_GROUP_KEY_ENTER) {
lv_btn_set_state(ext->bullet, lv_btn_get_state(cb));
}
} else if(sign == LV_SIGNAL_GET_TYPE) {
lv_obj_type_t * buf = param;
uint8_t i;
for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/
if(buf->type[i] == NULL) break;
}
buf->type[i] = "lv_cb";
}
return res;
}
#endif

View File

@@ -0,0 +1,174 @@
/**
* @file lv_cb.h
*
*/
#ifndef LV_CB_H
#define LV_CB_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#ifdef LV_CONF_INCLUDE_SIMPLE
#include "lv_conf.h"
#else
#include "../../lv_conf.h"
#endif
#if USE_LV_CB != 0
/*Testing of dependencies*/
#if USE_LV_BTN == 0
#error "lv_cb: lv_btn is required. Enable it in lv_conf.h (USE_LV_BTN 1) "
#endif
#if USE_LV_LABEL == 0
#error "lv_cb: lv_label is required. Enable it in lv_conf.h (USE_LV_LABEL 1) "
#endif
#include "../lv_core/lv_obj.h"
#include "lv_btn.h"
#include "lv_label.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/*Data of check box*/
typedef struct
{
lv_btn_ext_t bg_btn; /*Ext. of ancestor*/
/*New data for this type */
lv_obj_t * bullet; /*Pointer to button*/
lv_obj_t * label; /*Pointer to label*/
} lv_cb_ext_t;
enum {
LV_CB_STYLE_BG,
LV_CB_STYLE_BOX_REL,
LV_CB_STYLE_BOX_PR,
LV_CB_STYLE_BOX_TGL_REL,
LV_CB_STYLE_BOX_TGL_PR,
LV_CB_STYLE_BOX_INA,
};
typedef uint8_t lv_cb_style_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a check box objects
* @param par pointer to an object, it will be the parent of the new check box
* @param copy pointer to a check box object, if not NULL then the new object will be copied from it
* @return pointer to the created check box
*/
lv_obj_t * lv_cb_create(lv_obj_t * par, const lv_obj_t * copy);
/*=====================
* Setter functions
*====================*/
/**
* Set the text of a check box
* @param cb pointer to a check box
* @param txt the text of the check box
*/
void lv_cb_set_text(lv_obj_t * cb, const char * txt);
/**
* Set the state of the check box
* @param cb pointer to a check box object
* @param checked true: make the check box checked; false: make it unchecked
*/
static inline void lv_cb_set_checked(lv_obj_t * cb, bool checked)
{
lv_btn_set_state(cb, checked ? LV_BTN_STATE_TGL_REL : LV_BTN_STATE_REL);
}
/**
* Make the check box inactive (disabled)
* @param cb pointer to a check box object
*/
static inline void lv_cb_set_inactive(lv_obj_t * cb)
{
lv_btn_set_state(cb, LV_BTN_STATE_INA);
}
/**
* Set a function to call when the check box is clicked
* @param cb pointer to a check box object
*/
static inline void lv_cb_set_action(lv_obj_t * cb, lv_action_t action)
{
lv_btn_set_action(cb, LV_BTN_ACTION_CLICK, action);
}
/**
* Set a style of a check box
* @param cb pointer to check box object
* @param type which style should be set
* @param style pointer to a style
* */
void lv_cb_set_style(lv_obj_t * cb, lv_cb_style_t type, lv_style_t *style);
/*=====================
* 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_cb_get_text(const lv_obj_t * cb);
/**
* Get the current state of the check box
* @param cb pointer to a check box object
* @return true: checked; false: not checked
*/
static inline bool lv_cb_is_checked(const lv_obj_t * cb)
{
return lv_btn_get_state(cb) == LV_BTN_STATE_REL ? false : true;
}
/**
* Get the action of a check box
* @param cb pointer to a button object
* @return pointer to the action function
*/
static inline lv_action_t lv_cb_get_action(const lv_obj_t * cb)
{
return lv_btn_get_action(cb, LV_BTN_ACTION_CLICK);
}
/**
* Get a style of a button
* @param cb pointer to check box object
* @param type which style should be get
* @return style pointer to the style
* */
lv_style_t * lv_cb_get_style(const lv_obj_t * cb, lv_cb_style_t type);
/**********************
* MACROS
**********************/
#endif /*USE_LV_CB*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_CB_H*/

View File

@@ -0,0 +1,824 @@
/**
* @file lv_chart.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_chart.h"
#if USE_LV_CHART != 0
#include "../lv_draw/lv_draw.h"
#include "../lv_themes/lv_theme.h"
/*********************
* DEFINES
*********************/
#define LV_CHART_YMIN_DEF 0
#define LV_CHART_YMAX_DEF 100
#define LV_CHART_HDIV_DEF 3
#define LV_CHART_VDIV_DEF 5
#define LV_CHART_PNUM_DEF 10
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static bool lv_chart_design(lv_obj_t * chart, const lv_area_t * mask, lv_design_mode_t mode);
static lv_res_t lv_chart_signal(lv_obj_t * chart, lv_signal_t sign, void * param);
static void lv_chart_draw_div(lv_obj_t * chart, const lv_area_t * mask);
static void lv_chart_draw_lines(lv_obj_t * chart, const lv_area_t * mask);
static void lv_chart_draw_points(lv_obj_t * chart, const lv_area_t * mask);
static void lv_chart_draw_cols(lv_obj_t * chart, const lv_area_t * mask);
static void lv_chart_draw_vertical_lines(lv_obj_t * chart, const lv_area_t * mask);
/**********************
* STATIC VARIABLES
**********************/
static lv_design_func_t ancestor_design_f;
static lv_signal_func_t ancestor_signal;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Create a chart background objects
* @param par pointer to an object, it will be the parent of the new chart background
* @param copy pointer to a chart background object, if not NULL then the new object will be copied from it
* @return pointer to the created chart background
*/
lv_obj_t * lv_chart_create(lv_obj_t * par, const lv_obj_t * copy)
{
LV_LOG_TRACE("chart create started");
/*Create the ancestor basic object*/
lv_obj_t * new_chart = lv_obj_create(par, copy);
lv_mem_assert(new_chart);
if(new_chart == NULL) return NULL;
/*Allocate the object type specific extended data*/
lv_chart_ext_t * ext = lv_obj_allocate_ext_attr(new_chart, sizeof(lv_chart_ext_t));
lv_mem_assert(ext);
if(ext == NULL) return NULL;
lv_ll_init(&ext->series_ll, sizeof(lv_chart_series_t));
ext->series.num = 0;
ext->ymin = LV_CHART_YMIN_DEF;
ext->ymax = LV_CHART_YMAX_DEF;
ext->hdiv_cnt = LV_CHART_HDIV_DEF;
ext->vdiv_cnt = LV_CHART_VDIV_DEF;
ext->point_cnt = LV_CHART_PNUM_DEF;
ext->type = LV_CHART_TYPE_LINE;
ext->series.opa = LV_OPA_COVER;
ext->series.dark = LV_OPA_50;
ext->series.width = 2;
if(ancestor_design_f == NULL) ancestor_design_f = lv_obj_get_design_func(new_chart);
if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_chart);
lv_obj_set_signal_func(new_chart, lv_chart_signal);
lv_obj_set_design_func(new_chart, lv_chart_design);
/*Init the new chart background object*/
if(copy == NULL) {
lv_obj_set_size(new_chart, LV_HOR_RES / 3, LV_VER_RES / 3);
/*Set the default styles*/
lv_theme_t * th = lv_theme_get_current();
if(th) {
lv_chart_set_style(new_chart, th->chart);
} else {
lv_chart_set_style(new_chart, &lv_style_pretty);
}
} else {
lv_chart_ext_t * ext_copy = lv_obj_get_ext_attr(copy);
ext->type = ext_copy->type;
ext->ymin = ext_copy->ymin;
ext->ymax = ext_copy->ymax;
ext->hdiv_cnt = ext_copy->hdiv_cnt;
ext->vdiv_cnt = ext_copy->vdiv_cnt;
ext->point_cnt = ext_copy->point_cnt;
ext->series.opa = ext_copy->series.opa;
/*Refresh the style with new signal function*/
lv_obj_refresh_style(new_chart);
}
LV_LOG_INFO("chart created");
return new_chart;
}
/*======================
* Add/remove functions
*=====================*/
/**
* Allocate and add a data series to the chart
* @param chart pointer to a chart object
* @param color color of the data series
* @return pointer to the allocated data series
*/
lv_chart_series_t * lv_chart_add_series(lv_obj_t * chart, lv_color_t color)
{
lv_chart_ext_t * ext = lv_obj_get_ext_attr(chart);
lv_chart_series_t * ser = lv_ll_ins_head(&ext->series_ll);
lv_mem_assert(ser);
if(ser == NULL) return NULL;
lv_coord_t def = LV_CHART_POINT_DEF;
if(ser == NULL) return NULL;
ser->color = color;
ser->points = lv_mem_alloc(sizeof(lv_coord_t) * ext->point_cnt);
lv_mem_assert(ser->points);
if(ser->points == NULL) {
lv_ll_rem(&ext->series_ll, ser);
lv_mem_free(ser);
return NULL;
}
ser->start_point = 0;
uint16_t i;
lv_coord_t * p_tmp = ser->points;
for(i = 0; i < ext->point_cnt; i++) {
*p_tmp = def;
p_tmp++;
}
ext->series.num++;
return ser;
}
/**
* Clear the point of a serie
* @param chart pointer to a chart object
* @param serie pointer to the chart's serie to clear
*/
void lv_chart_clear_serie(lv_obj_t * chart, lv_chart_series_t * serie)
{
if(chart == NULL || serie == NULL)
return;
lv_chart_ext_t * ext = lv_obj_get_ext_attr(chart);
if(ext == NULL) return;
uint32_t i;
for(i = 0; i < ext->point_cnt; i++)
{
serie->points[i] = LV_CHART_POINT_DEF;
}
serie->start_point = 0;
}
/*=====================
* Setter functions
*====================*/
/**
* Set the number of horizontal and vertical division lines
* @param chart pointer to a graph background object
* @param hdiv number of horizontal division lines
* @param vdiv number of vertical division lines
*/
void lv_chart_set_div_line_count(lv_obj_t * chart, uint8_t hdiv, uint8_t vdiv)
{
lv_chart_ext_t * ext = lv_obj_get_ext_attr(chart);
if(ext->hdiv_cnt == hdiv && ext->vdiv_cnt == vdiv) return;
ext->hdiv_cnt = hdiv;
ext->vdiv_cnt = vdiv;
lv_obj_invalidate(chart);
}
/**
* Set the minimal and maximal y values
* @param chart pointer to a graph background object
* @param ymin y minimum value
* @param ymax y maximum value
*/
void lv_chart_set_range(lv_obj_t * chart, lv_coord_t ymin, lv_coord_t ymax)
{
lv_chart_ext_t * ext = lv_obj_get_ext_attr(chart);
if(ext->ymin == ymin && ext->ymax == ymax) return;
ext->ymin = ymin;
ext->ymax = ymax;
lv_chart_refresh(chart);
}
/**
* Set a new type for a chart
* @param chart pointer to a chart object
* @param type new type of the chart (from 'lv_chart_type_t' enum)
*/
void lv_chart_set_type(lv_obj_t * chart, lv_chart_type_t type)
{
lv_chart_ext_t * ext = lv_obj_get_ext_attr(chart);
if(ext->type == type) return;
ext->type = type;
lv_chart_refresh(chart);
}
/**
* Set the number of points on a data line on a chart
* @param chart pointer r to chart object
* @param point_cnt new number of points on the data lines
*/
void lv_chart_set_point_count(lv_obj_t * chart, uint16_t point_cnt)
{
lv_chart_ext_t * ext = lv_obj_get_ext_attr(chart);
if(ext->point_cnt == point_cnt) return;
lv_chart_series_t * ser;
uint16_t point_cnt_old = ext->point_cnt;
uint16_t i;
lv_coord_t def = LV_CHART_POINT_DEF;
if(point_cnt < 1) point_cnt = 1;
LL_READ_BACK(ext->series_ll, ser) {
if(ser->start_point != 0) {
lv_coord_t * new_points = lv_mem_alloc(sizeof(lv_coord_t) * point_cnt);
lv_mem_assert(new_points);
if(new_points == NULL) return;
if(point_cnt >= point_cnt_old) {
for(i = 0; i < point_cnt_old; i++) {
new_points[i] = ser->points[(i + ser->start_point) % point_cnt_old]; /*Copy old contents to new array*/
}
for(i = point_cnt_old; i < point_cnt; i++) {
new_points[i] = def; /*Fill up the rest with default value*/
}
} else {
for(i = 0; i < point_cnt; i++) {
new_points[i] = ser->points[(i + ser->start_point) % point_cnt_old]; /*Copy old contents to new array*/
}
}
/*Switch over pointer from old to new*/
lv_mem_free(ser->points);
ser->points = new_points;
} else {
ser->points = lv_mem_realloc(ser->points, sizeof(lv_coord_t) * point_cnt);
lv_mem_assert(ser->points);
if(ser->points == NULL) return;
/*Initialize the new points*/
if(point_cnt > point_cnt_old) {
for(i = point_cnt_old - 1; i < point_cnt; i++) {
ser->points[i] = def;
}
}
}
ser->start_point = 0;
}
ext->point_cnt = point_cnt;
lv_chart_refresh(chart);
}
/**
* Set the opacity of the data series
* @param chart pointer to a chart object
* @param opa opacity of the data series
*/
void lv_chart_set_series_opa(lv_obj_t * chart, lv_opa_t opa)
{
lv_chart_ext_t * ext = lv_obj_get_ext_attr(chart);
if(ext->series.opa == opa) return;
ext->series.opa = opa;
lv_obj_invalidate(chart);
}
/**
* Set the line width or point radius of the data series
* @param chart pointer to a chart object
* @param width the new width
*/
void lv_chart_set_series_width(lv_obj_t * chart, lv_coord_t width)
{
lv_chart_ext_t * ext = lv_obj_get_ext_attr(chart);
if(ext->series.width == width) return;
ext->series.width = width;
lv_obj_invalidate(chart);
}
/**
* Set the dark effect on the bottom of the points or columns
* @param chart pointer to a chart object
* @param dark_eff dark effect level (LV_OPA_TRANSP to turn off)
*/
void lv_chart_set_series_darking(lv_obj_t * chart, lv_opa_t dark_eff)
{
lv_chart_ext_t * ext = lv_obj_get_ext_attr(chart);
if(ext->series.dark == dark_eff) return;
ext->series.dark = dark_eff;
lv_obj_invalidate(chart);
}
/**
* Initialize all data points with a value
* @param chart pointer to chart object
* @param ser pointer to a data series on 'chart'
* @param y the new value for all points
*/
void lv_chart_init_points(lv_obj_t * chart, lv_chart_series_t * ser, lv_coord_t y)
{
lv_chart_ext_t * ext = lv_obj_get_ext_attr(chart);
uint16_t i;
for(i = 0; i < ext->point_cnt; i++) {
ser->points[i] = y;
}
ser->start_point = 0;
lv_chart_refresh(chart);
}
/**
* Set the value s of points from an array
* @param chart pointer to chart object
* @param ser pointer to a data series on 'chart'
* @param y_array array of 'lv_coord_t' points (with 'points count' elements )
*/
void lv_chart_set_points(lv_obj_t * chart, lv_chart_series_t * ser, lv_coord_t * y_array)
{
lv_chart_ext_t * ext = lv_obj_get_ext_attr(chart);
memcpy(ser->points, y_array, ext->point_cnt * (sizeof(lv_coord_t)));
ser->start_point = 0;
lv_chart_refresh(chart);
}
/**
* Shift all data left and set the rightmost data on a data line
* @param chart pointer to chart object
* @param ser pointer to a data series on 'chart'
* @param y the new value of the rightmost data
*/
void lv_chart_set_next(lv_obj_t * chart, lv_chart_series_t * ser, lv_coord_t y)
{
lv_chart_ext_t * ext = lv_obj_get_ext_attr(chart);
ser->points[ser->start_point] = y; /*This was the place of the former left most value, after shifting it is the rightmost*/
ser->start_point = (ser->start_point + 1) % ext->point_cnt;
lv_chart_refresh(chart);
}
/*=====================
* Getter functions
*====================*/
/**
* Get the type of a chart
* @param chart pointer to chart object
* @return type of the chart (from 'lv_chart_t' enum)
*/
lv_chart_type_t lv_chart_get_type(const lv_obj_t * chart)
{
lv_chart_ext_t * ext = lv_obj_get_ext_attr(chart);
return ext->type;
}
/**
* Get the data point number per data line on chart
* @param chart pointer to chart object
* @return point number on each data line
*/
uint16_t lv_chart_get_point_cnt(const lv_obj_t * chart)
{
lv_chart_ext_t * ext = lv_obj_get_ext_attr(chart);
return ext->point_cnt;
}
/**
* Get the opacity of the data series
* @param chart pointer to chart object
* @return the opacity of the data series
*/
lv_opa_t lv_chart_get_series_opa(const lv_obj_t * chart)
{
lv_chart_ext_t * ext = lv_obj_get_ext_attr(chart);
return ext->series.opa;
}
/**
* Get the data series width
* @param chart pointer to chart object
* @return the width the data series (lines or points)
*/
lv_coord_t lv_chart_get_series_width(const lv_obj_t * chart)
{
lv_chart_ext_t * ext = lv_obj_get_ext_attr(chart);
return ext->series.width;
}
/**
* Get the dark effect level on the bottom of the points or columns
* @param chart pointer to chart object
* @return dark effect level (LV_OPA_TRANSP to turn off)
*/
lv_opa_t lv_chart_get_series_darking(const lv_obj_t * chart)
{
lv_chart_ext_t * ext = lv_obj_get_ext_attr(chart);
return ext->series.dark;
}
/*=====================
* Other functions
*====================*/
/**
* Refresh a chart if its data line has changed
* @param chart pointer to chart object
*/
void lv_chart_refresh(lv_obj_t * chart)
{
lv_obj_invalidate(chart);
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Handle the drawing related tasks of the chart backgrounds
* @param chart pointer to an object
* @param mask the object will be drawn only in this area
* @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area
* (return 'true' if yes)
* LV_DESIGN_DRAW: draw the object (always return 'true')
* LV_DESIGN_DRAW_POST: drawing after every children are drawn
* @param return true/false, depends on 'mode'
*/
static bool lv_chart_design(lv_obj_t * chart, const lv_area_t * mask, lv_design_mode_t mode)
{
if(mode == LV_DESIGN_COVER_CHK) {
/*Return false if the object is not covers the mask_p area*/
return ancestor_design_f(chart, mask, mode);
} else if(mode == LV_DESIGN_DRAW_MAIN) {
/*Draw the background*/
lv_draw_rect(&chart->coords, mask, lv_obj_get_style(chart), lv_obj_get_opa_scale(chart));
lv_chart_ext_t * ext = lv_obj_get_ext_attr(chart);
lv_chart_draw_div(chart, mask);
if(ext->type & LV_CHART_TYPE_LINE) lv_chart_draw_lines(chart, mask);
if(ext->type & LV_CHART_TYPE_COLUMN) lv_chart_draw_cols(chart, mask);
if(ext->type & LV_CHART_TYPE_POINT) lv_chart_draw_points(chart, mask);
if(ext->type & LV_CHART_TYPE_VERTICAL_LINE) lv_chart_draw_vertical_lines(chart, mask);
}
return true;
}
/**
* Signal function of the chart background
* @param chart pointer to a chart background object
* @param sign a signal type from lv_signal_t enum
* @param param pointer to a signal specific variable
*/
static lv_res_t lv_chart_signal(lv_obj_t * chart, lv_signal_t sign, void * param)
{
lv_res_t res;
/* Include the ancient signal function */
res = ancestor_signal(chart, sign, param);
if(res != LV_RES_OK) return res;
if(sign == LV_SIGNAL_CLEANUP) {
lv_coord_t ** datal;
lv_chart_ext_t * ext = lv_obj_get_ext_attr(chart);
LL_READ(ext->series_ll, datal) {
lv_mem_free(*datal);
}
lv_ll_clear(&ext->series_ll);
} else if(sign == LV_SIGNAL_GET_TYPE) {
lv_obj_type_t * buf = param;
uint8_t i;
for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/
if(buf->type[i] == NULL) break;
}
buf->type[i] = "lv_chart";
}
return res;
}
/**
* Draw the division lines on chart background
* @param chart pointer to chart object
* @param mask mask, inherited from the design function
*/
static void lv_chart_draw_div(lv_obj_t * chart, const lv_area_t * mask)
{
lv_chart_ext_t * ext = lv_obj_get_ext_attr(chart);
lv_style_t * style = lv_obj_get_style(chart);
lv_opa_t opa_scale = lv_obj_get_opa_scale(chart);
uint8_t div_i;
uint8_t div_i_end;
uint8_t div_i_start;
lv_point_t p1;
lv_point_t p2;
lv_coord_t w = lv_obj_get_width(chart);
lv_coord_t h = lv_obj_get_height(chart);
lv_coord_t x_ofs = chart->coords.x1;
lv_coord_t y_ofs = chart->coords.y1;
if(ext->hdiv_cnt != 0) {
/*Draw slide lines if no border*/
if(style->body.border.width != 0) {
div_i_start = 1;
div_i_end = ext->hdiv_cnt;
} else {
div_i_start = 0;
div_i_end = ext->hdiv_cnt + 1;
}
p1.x = 0 + x_ofs;
p2.x = w + x_ofs;
for(div_i = div_i_start; div_i <= div_i_end; div_i++) {
p1.y = (int32_t)((int32_t)h * div_i) / (ext->hdiv_cnt + 1);
p1.y += y_ofs;
if(div_i == div_i_start) p1.y += (style->line.width >> 1) + 1; /*The first line might not be visible*/
if(div_i == div_i_end) p1.y -= (style->line.width >> 1) + 1; /*The last line might not be visible*/
p2.y = p1.y;
lv_draw_line(&p1, &p2, mask, style, opa_scale);
}
}
if(ext->vdiv_cnt != 0) {
/*Draw slide lines if no border*/
if(style->body.border.width != 0) {
div_i_start = 1;
div_i_end = ext->vdiv_cnt;
} else {
div_i_start = 0;
div_i_end = ext->vdiv_cnt + 1;
}
p1.y = 0 + y_ofs;
p2.y = h + y_ofs;
for(div_i = div_i_start; div_i <= div_i_end; div_i ++) {
p1.x = (int32_t)((int32_t)w * div_i) / (ext->vdiv_cnt + 1);
p1.x += x_ofs;
if(div_i == div_i_start) p1.x += (style->line.width >> 1) + 1; /*The first line might not be visible*/
if(div_i == div_i_end) p1.x -= (style->line.width >> 1) + 1; /*The last line might not be visible*/
p2.x = p1.x;
lv_draw_line(&p1, &p2, mask, style, opa_scale);
}
}
}
/**
* Draw the data lines as lines on a chart
* @param obj pointer to chart object
*/
static void lv_chart_draw_lines(lv_obj_t * chart, const lv_area_t * mask)
{
lv_chart_ext_t * ext = lv_obj_get_ext_attr(chart);
uint16_t i;
lv_point_t p1;
lv_point_t p2;
lv_coord_t w = lv_obj_get_width(chart);
lv_coord_t h = lv_obj_get_height(chart);
lv_coord_t x_ofs = chart->coords.x1;
lv_coord_t y_ofs = chart->coords.y1;
int32_t y_tmp;
lv_coord_t p_prev;
lv_coord_t p_act;
lv_chart_series_t * ser;
lv_opa_t opa_scale = lv_obj_get_opa_scale(chart);
lv_style_t style;
lv_style_copy(&style, &lv_style_plain);
style.line.opa = ext->series.opa;
style.line.width = ext->series.width;
/*Go through all data lines*/
LL_READ_BACK(ext->series_ll, ser) {
style.line.color = ser->color;
p1.x = 0 + x_ofs;
p2.x = 0 + x_ofs;
p_prev = ser->start_point;
y_tmp = (int32_t)((int32_t) ser->points[p_prev] - ext->ymin) * h;
y_tmp = y_tmp / (ext->ymax - ext->ymin);
p2.y = h - y_tmp + y_ofs;
for(i = 1; i < ext->point_cnt; i ++) {
p1.x = p2.x;
p1.y = p2.y;
p2.x = ((w * i) / (ext->point_cnt - 1)) + x_ofs;
p_act = (ser->start_point + i) % ext->point_cnt;
y_tmp = (int32_t)((int32_t) ser->points[p_act] - ext->ymin) * h;
y_tmp = y_tmp / (ext->ymax - ext->ymin);
p2.y = h - y_tmp + y_ofs;
if(ser->points[p_prev] != LV_CHART_POINT_DEF && ser->points[p_act] != LV_CHART_POINT_DEF)
lv_draw_line(&p1, &p2, mask, &style, opa_scale);
p_prev = p_act;
}
}
}
/**
* Draw the data lines as points on a chart
* @param chart pointer to chart object
* @param mask mask, inherited from the design function
*/
static void lv_chart_draw_points(lv_obj_t * chart, const lv_area_t * mask)
{
lv_chart_ext_t * ext = lv_obj_get_ext_attr(chart);
uint16_t i;
lv_area_t cir_a;
lv_coord_t w = lv_obj_get_width(chart);
lv_coord_t h = lv_obj_get_height(chart);
lv_coord_t x_ofs = chart->coords.x1;
lv_coord_t y_ofs = chart->coords.y1;
int32_t y_tmp;
lv_coord_t p_act;
lv_chart_series_t * ser;
uint8_t series_cnt = 0;
lv_style_t style_point;
lv_style_copy(&style_point, &lv_style_plain);
style_point.body.border.width = 0;
style_point.body.empty = 0;
style_point.body.radius = LV_RADIUS_CIRCLE;
style_point.body.opa = ext->series.opa;
style_point.body.radius = ext->series.width;
/*Go through all data lines*/
LL_READ_BACK(ext->series_ll, ser) {
style_point.body.main_color = ser->color;
style_point.body.grad_color = lv_color_mix(LV_COLOR_BLACK, ser->color, ext->series.dark);
for(i = 0; i < ext->point_cnt; i ++) {
cir_a.x1 = ((w * i) / (ext->point_cnt - 1)) + x_ofs;
cir_a.x2 = cir_a.x1 + style_point.body.radius;
cir_a.x1 -= style_point.body.radius;
p_act = (ser->start_point + i) % ext->point_cnt;
y_tmp = (int32_t)((int32_t) ser->points[p_act] - ext->ymin) * h;
y_tmp = y_tmp / (ext->ymax - ext->ymin);
cir_a.y1 = h - y_tmp + y_ofs;
cir_a.y2 = cir_a.y1 + style_point.body.radius;
cir_a.y1 -= style_point.body.radius;
if(ser->points[p_act] != LV_CHART_POINT_DEF)
lv_draw_rect(&cir_a, mask, &style_point, lv_obj_get_opa_scale(chart));
}
series_cnt++;
}
}
/**
* Draw the data lines as columns on a chart
* @param chart pointer to chart object
* @param mask mask, inherited from the design function
*/
static void lv_chart_draw_cols(lv_obj_t * chart, const lv_area_t * mask)
{
lv_chart_ext_t * ext = lv_obj_get_ext_attr(chart);
uint16_t i;
lv_area_t col_a;
lv_area_t col_mask;
bool mask_ret;
lv_coord_t w = lv_obj_get_width(chart);
lv_coord_t h = lv_obj_get_height(chart);
int32_t y_tmp;
lv_chart_series_t * ser;
lv_style_t rects;
lv_coord_t col_w = w / ((ext->series.num + 1) * ext->point_cnt); /* Suppose + 1 series as separator*/
lv_coord_t x_ofs = col_w / 2; /*Shift with a half col.*/
lv_style_copy(&rects, &lv_style_plain);
rects.body.border.width = 0;
rects.body.empty = 0;
rects.body.radius = 0;
rects.body.opa = ext->series.opa;
col_a.y2 = chart->coords.y2;
lv_coord_t x_act;
/*Go through all points*/
for(i = 0; i < ext->point_cnt; i ++) {
x_act = (int32_t)((int32_t) w * i) / ext->point_cnt;
x_act += chart->coords.x1 + x_ofs;
/*Draw the current point of all data line*/
LL_READ_BACK(ext->series_ll, ser) {
rects.body.main_color = ser->color;
rects.body.grad_color = lv_color_mix(LV_COLOR_BLACK, ser->color, ext->series.dark);
col_a.x1 = x_act;
col_a.x2 = col_a.x1 + col_w;
x_act += col_w;
lv_coord_t p_act = (ser->start_point + i) % ext->point_cnt;
y_tmp = (int32_t)((int32_t) ser->points[p_act] - ext->ymin) * h;
y_tmp = y_tmp / (ext->ymax - ext->ymin);
col_a.y1 = h - y_tmp + chart->coords.y1;
mask_ret = lv_area_intersect(&col_mask, mask, &col_a);
if(mask_ret != false && ser->points[p_act] != LV_CHART_POINT_DEF) {
lv_draw_rect(&chart->coords, &col_mask, &rects, lv_obj_get_opa_scale(chart));
}
}
}
}
/**
* Draw the data lines as vertical lines on a chart if there is only 1px between point
* @param obj pointer to chart object
*/
static void lv_chart_draw_vertical_lines(lv_obj_t * chart, const lv_area_t * mask)
{
lv_chart_ext_t * ext = lv_obj_get_ext_attr(chart);
lv_coord_t w = lv_obj_get_width(chart);
/*Vertical lines works only if the width == point count. Else use the normal line type*/
if(ext->point_cnt != w) {
lv_chart_draw_lines(chart, mask);
return;
}
uint16_t i;
lv_point_t p1;
lv_point_t p2;
lv_coord_t h = lv_obj_get_height(chart);
lv_coord_t x_ofs = chart->coords.x1;
lv_coord_t y_ofs = chart->coords.y1;
int32_t y_tmp;
lv_chart_series_t * ser;
lv_opa_t opa_scale = lv_obj_get_opa_scale(chart);
lv_style_t style;
lv_style_copy(&style, &lv_style_plain);
style.line.opa = ext->series.opa;
style.line.width = ext->series.width;
/*Go through all data lines*/
LL_READ_BACK(ext->series_ll, ser) {
style.line.color = ser->color;
p1.x = 0 + x_ofs;
p2.x = 0 + x_ofs;
y_tmp = (int32_t)((int32_t) ser->points[0] - ext->ymin) * h;
y_tmp = y_tmp / (ext->ymax - ext->ymin);
p2.y = h - y_tmp + y_ofs;
p1.y = p2.y;
for(i = 0; i < ext->point_cnt; i++)
{
y_tmp = (int32_t)((int32_t) ser->points[i] - ext->ymin) * h;
y_tmp = y_tmp / (ext->ymax - ext->ymin);
p2.y = h - y_tmp + y_ofs;
if(p1.y == p2.y)
{
p2.x++;
}
if(ser->points[i] != LV_CHART_POINT_DEF) {
lv_draw_line(&p1, &p2, mask, &style, opa_scale);
}
p2.x = ((w * i) / (ext->point_cnt - 1)) + x_ofs;
p1.x = p2.x;
p1.y = p2.y;
}
}
}
#endif

View File

@@ -0,0 +1,262 @@
/**
* @file lv_chart.h
*
*/
#ifndef LV_CHART_H
#define LV_CHART_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#ifdef LV_CONF_INCLUDE_SIMPLE
#include "lv_conf.h"
#else
#include "../../lv_conf.h"
#endif
#if USE_LV_CHART != 0
#include "../lv_core/lv_obj.h"
#include "lv_line.h"
/*********************
* DEFINES
*********************/
#define LV_CHART_POINT_DEF (LV_COORD_MIN)
/**********************
* TYPEDEFS
**********************/
typedef struct
{
lv_coord_t * points;
lv_color_t color;
uint16_t start_point;
} lv_chart_series_t;
/*Data of chart */
typedef struct
{
/*No inherited ext*/ /*Ext. of ancestor*/
/*New data for this type */
lv_ll_t series_ll; /*Linked list for the data line pointers (stores lv_chart_dl_t)*/
lv_coord_t ymin; /*y min value (used to scale the data)*/
lv_coord_t ymax; /*y max value (used to scale the data)*/
uint8_t hdiv_cnt; /*Number of horizontal division lines*/
uint8_t vdiv_cnt; /*Number of vertical division lines*/
uint16_t point_cnt; /*Point number in a data line*/
uint8_t type :4; /*Line, column or point chart (from 'lv_chart_type_t')*/
struct {
lv_coord_t width; /*Line width or point radius*/
uint8_t num; /*Number of data lines in dl_ll*/
lv_opa_t opa; /*Opacity of data lines*/
lv_opa_t dark; /*Dark level of the point/column bottoms*/
} series;
} lv_chart_ext_t;
/*Chart types*/
enum
{
LV_CHART_TYPE_LINE = 0x01, /*Connect the points with lines*/
LV_CHART_TYPE_COLUMN = 0x02, /*Draw columns*/
LV_CHART_TYPE_POINT = 0x04, /*Draw circles on the points*/
LV_CHART_TYPE_VERTICAL_LINE = 0x08, /*Draw vertical lines on points (useful when chart width == point count)*/
};
typedef uint8_t lv_chart_type_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a chart background objects
* @param par pointer to an object, it will be the parent of the new chart background
* @param copy pointer to a chart background object, if not NULL then the new object will be copied from it
* @return pointer to the created chart background
*/
lv_obj_t * lv_chart_create(lv_obj_t * par, const lv_obj_t * copy);
/*======================
* Add/remove functions
*=====================*/
/**
* Allocate and add a data series to the chart
* @param chart pointer to a chart object
* @param color color of the data series
* @return pointer to the allocated data series
*/
lv_chart_series_t * lv_chart_add_series(lv_obj_t * chart, lv_color_t color);
/**
* Clear the point of a serie
* @param chart pointer to a chart object
* @param serie pointer to the chart's serie to clear
*/
void lv_chart_clear_serie(lv_obj_t * chart, lv_chart_series_t * serie);
/*=====================
* Setter functions
*====================*/
/**
* Set the number of horizontal and vertical division lines
* @param chart pointer to a graph background object
* @param hdiv number of horizontal division lines
* @param vdiv number of vertical division lines
*/
void lv_chart_set_div_line_count(lv_obj_t * chart, uint8_t hdiv, uint8_t vdiv);
/**
* Set the minimal and maximal y values
* @param chart pointer to a graph background object
* @param ymin y minimum value
* @param ymax y maximum value
*/
void lv_chart_set_range(lv_obj_t * chart, lv_coord_t ymin, lv_coord_t ymax);
/**
* Set a new type for a chart
* @param chart pointer to a chart object
* @param type new type of the chart (from 'lv_chart_type_t' enum)
*/
void lv_chart_set_type(lv_obj_t * chart, lv_chart_type_t type);
/**
* Set the number of points on a data line on a chart
* @param chart pointer r to chart object
* @param point_cnt new number of points on the data lines
*/
void lv_chart_set_point_count(lv_obj_t * chart, uint16_t point_cnt);
/**
* Set the opacity of the data series
* @param chart pointer to a chart object
* @param opa opacity of the data series
*/
void lv_chart_set_series_opa(lv_obj_t * chart, lv_opa_t opa);
/**
* Set the line width or point radius of the data series
* @param chart pointer to a chart object
* @param width the new width
*/
void lv_chart_set_series_width(lv_obj_t * chart, lv_coord_t width);
/**
* Set the dark effect on the bottom of the points or columns
* @param chart pointer to a chart object
* @param dark_eff dark effect level (LV_OPA_TRANSP to turn off)
*/
void lv_chart_set_series_darking(lv_obj_t * chart, lv_opa_t dark_eff);
/**
* Initialize all data points with a value
* @param chart pointer to chart object
* @param ser pointer to a data series on 'chart'
* @param y the new value for all points
*/
void lv_chart_init_points(lv_obj_t * chart, lv_chart_series_t * ser, lv_coord_t y);
/**
* Set the value s of points from an array
* @param chart pointer to chart object
* @param ser pointer to a data series on 'chart'
* @param y_array array of 'lv_coord_t' points (with 'points count' elements )
*/
void lv_chart_set_points(lv_obj_t * chart, lv_chart_series_t * ser, lv_coord_t * y_array);
/**
* Shift all data right and set the most right data on a data line
* @param chart pointer to chart object
* @param ser pointer to a data series on 'chart'
* @param y the new value of the most right data
*/
void lv_chart_set_next(lv_obj_t * chart, lv_chart_series_t * ser, lv_coord_t y);
/**
* Set the style of a chart
* @param chart pointer to a chart object
* @param style pointer to a style
*/
static inline void lv_chart_set_style(lv_obj_t *chart, lv_style_t *style)
{
lv_obj_set_style(chart, style);
}
/*=====================
* Getter functions
*====================*/
/**
* Get the type of a chart
* @param chart pointer to chart object
* @return type of the chart (from 'lv_chart_t' enum)
*/
lv_chart_type_t lv_chart_get_type(const lv_obj_t * chart);
/**
* Get the data point number per data line on chart
* @param chart pointer to chart object
* @return point number on each data line
*/
uint16_t lv_chart_get_point_cnt(const lv_obj_t * chart);
/**
* Get the opacity of the data series
* @param chart pointer to chart object
* @return the opacity of the data series
*/
lv_opa_t lv_chart_get_series_opa(const lv_obj_t * chart);
/**
* Get the data series width
* @param chart pointer to chart object
* @return the width the data series (lines or points)
*/
lv_coord_t lv_chart_get_series_width(const lv_obj_t * chart);
/**
* Get the dark effect level on the bottom of the points or columns
* @param chart pointer to chart object
* @return dark effect level (LV_OPA_TRANSP to turn off)
*/
lv_opa_t lv_chart_get_series_darking(const lv_obj_t * chart);
/**
* Get the style of an chart object
* @param chart pointer to an chart object
* @return pointer to the chart's style
*/
static inline lv_style_t* lv_chart_get_style(const lv_obj_t *chart)
{
return lv_obj_get_style(chart);
}
/*=====================
* Other functions
*====================*/
/**
* Refresh a chart if its data line has changed
* @param chart pointer to chart object
*/
void lv_chart_refresh(lv_obj_t * chart);
/**********************
* MACROS
**********************/
#endif /*USE_LV_CHART*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_CHART_H*/

View File

@@ -0,0 +1,642 @@
/**
* @file lv_cont.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_cont.h"
#if USE_LV_CONT != 0
#include <stdint.h>
#include <string.h>
#include "../lv_draw/lv_draw.h"
#include "../lv_draw/lv_draw_vbasic.h"
#include "../lv_themes/lv_theme.h"
#include "../lv_misc/lv_area.h"
#include "../lv_misc/lv_color.h"
#include "../lv_misc/lv_math.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static lv_res_t lv_cont_signal(lv_obj_t * cont, lv_signal_t sign, void * param);
static void lv_cont_refr_layout(lv_obj_t * cont);
static void lv_cont_layout_col(lv_obj_t * cont);
static void lv_cont_layout_row(lv_obj_t * cont);
static void lv_cont_layout_center(lv_obj_t * cont);
static void lv_cont_layout_pretty(lv_obj_t * cont);
static void lv_cont_layout_grid(lv_obj_t * cont);
static void lv_cont_refr_autofit(lv_obj_t * cont);
/**********************
* STATIC VARIABLES
**********************/
static lv_signal_func_t ancestor_signal;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Create a container objects
* @param par pointer to an object, it will be the parent of the new container
* @param copy pointer to a container object, if not NULL then the new object will be copied from it
* @return pointer to the created container
*/
lv_obj_t * lv_cont_create(lv_obj_t * par, const lv_obj_t * copy)
{
LV_LOG_TRACE("container create started");
/*Create a basic object*/
lv_obj_t * new_cont = lv_obj_create(par, copy);
lv_mem_assert(new_cont);
if(new_cont == NULL) return NULL;
if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_cont);
lv_obj_allocate_ext_attr(new_cont, sizeof(lv_cont_ext_t));
lv_cont_ext_t * ext = lv_obj_get_ext_attr(new_cont);
if(ext == NULL) return NULL;
lv_mem_assert(ext);
ext->hor_fit = 0;
ext->ver_fit = 0;
ext->layout = LV_LAYOUT_OFF;
lv_obj_set_signal_func(new_cont, lv_cont_signal);
/*Init the new container*/
if(copy == NULL) {
/*Set the default styles*/
lv_theme_t * th = lv_theme_get_current();
if(th) {
lv_cont_set_style(new_cont, th->cont);
} else {
lv_cont_set_style(new_cont, &lv_style_pretty);
}
}
/*Copy an existing object*/
else {
lv_cont_ext_t * copy_ext = lv_obj_get_ext_attr(copy);
ext->hor_fit = copy_ext->hor_fit;
ext->ver_fit = copy_ext->ver_fit;
ext->layout = copy_ext->layout;
/*Refresh the style with new signal function*/
lv_obj_refresh_style(new_cont);
}
LV_LOG_INFO("container created");
return new_cont;
}
/*=====================
* Setter functions
*====================*/
/**
* Set a layout on a container
* @param cont pointer to a container object
* @param layout a layout from 'lv_cont_layout_t'
*/
void lv_cont_set_layout(lv_obj_t * cont, lv_layout_t layout)
{
lv_cont_ext_t * ext = lv_obj_get_ext_attr(cont);
if(ext->layout == layout) return;
ext->layout = layout;
/*Send a signal to refresh the layout*/
cont->signal_func(cont, LV_SIGNAL_CHILD_CHG, NULL);
}
/**
* Enable the horizontal or vertical fit.
* The container size will be set to involve the children horizontally or vertically.
* @param cont pointer to a container object
* @param hor_en true: enable the horizontal fit
* @param ver_en true: enable the vertical fit
*/
void lv_cont_set_fit(lv_obj_t * cont, bool hor_en, bool ver_en)
{
lv_obj_invalidate(cont);
lv_cont_ext_t * ext = lv_obj_get_ext_attr(cont);
if(ext->hor_fit == hor_en && ext->ver_fit == ver_en) return;
ext->hor_fit = hor_en == false ? 0 : 1;
ext->ver_fit = ver_en == false ? 0 : 1;
/*Send a signal to refresh the layout*/
cont->signal_func(cont, LV_SIGNAL_CHILD_CHG, NULL);
}
/*=====================
* Getter functions
*====================*/
/**
* Get the layout of a container
* @param cont pointer to container object
* @return the layout from 'lv_cont_layout_t'
*/
lv_layout_t lv_cont_get_layout(const lv_obj_t * cont)
{
lv_cont_ext_t * ext = lv_obj_get_ext_attr(cont);
return ext->layout;
}
/**
* Get horizontal fit enable attribute of a container
* @param cont pointer to a container object
* @return true: horizontal fit is enabled; false: disabled
*/
bool lv_cont_get_hor_fit(const lv_obj_t * cont)
{
lv_cont_ext_t * ext = lv_obj_get_ext_attr(cont);
return ext->hor_fit == 0 ? false : true;
}
/**
* Get vertical fit enable attribute of a container
* @param cont pointer to a container object
* @return true: vertical fit is enabled; false: disabled
*/
bool lv_cont_get_ver_fit(const lv_obj_t * cont)
{
lv_cont_ext_t * ext = lv_obj_get_ext_attr(cont);
return ext->ver_fit == 0 ? false : true;
}
/**
* Get that width reduced by the horizontal padding. Useful if a layout is used.
* @param cont pointer to a container object
* @return the width which still fits into the container
*/
lv_coord_t lv_cont_get_fit_width(lv_obj_t * cont)
{
lv_style_t * style = lv_cont_get_style(cont);
return lv_obj_get_width(cont) - 2 * style->body.padding.hor;
}
/**
* Get that height reduced by the vertical padding. Useful if a layout is used.
* @param cont pointer to a container object
* @return the height which still fits into the container
*/
lv_coord_t lv_cont_get_fit_height(lv_obj_t * cont)
{
lv_style_t * style = lv_cont_get_style(cont);
return lv_obj_get_height(cont) - 2 * style->body.padding.ver;
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Signal function of the container
* @param cont pointer to a container object
* @param sign a signal type from lv_signal_t enum
* @param param pointer to a signal specific variable
* @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
*/
static lv_res_t lv_cont_signal(lv_obj_t * cont, lv_signal_t sign, void * param)
{
lv_res_t res;
/* Include the ancient signal function */
res = ancestor_signal(cont, sign, param);
if(res != LV_RES_OK) return res;
if(sign == LV_SIGNAL_STYLE_CHG) { /*Recalculate the padding if the style changed*/
lv_cont_refr_layout(cont);
lv_cont_refr_autofit(cont);
} else if(sign == LV_SIGNAL_CHILD_CHG) {
lv_cont_refr_layout(cont);
lv_cont_refr_autofit(cont);
} else if(sign == LV_SIGNAL_CORD_CHG) {
if(lv_obj_get_width(cont) != lv_area_get_width(param) ||
lv_obj_get_height(cont) != lv_area_get_height(param)) {
lv_cont_refr_layout(cont);
lv_cont_refr_autofit(cont);
}
} else if(sign == LV_SIGNAL_GET_TYPE) {
lv_obj_type_t * buf = param;
uint8_t i;
for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/
if(buf->type[i] == NULL) break;
}
buf->type[i] = "lv_cont";
}
return res;
}
/**
* Refresh the layout of a container
* @param cont pointer to an object which layout should be refreshed
*/
static void lv_cont_refr_layout(lv_obj_t * cont)
{
lv_layout_t type = lv_cont_get_layout(cont);
/*'cont' has to be at least 1 child*/
if(lv_obj_get_child(cont, NULL) == NULL) return;
if(type == LV_LAYOUT_OFF) return;
if(type == LV_LAYOUT_CENTER) {
lv_cont_layout_center(cont);
} else if(type == LV_LAYOUT_COL_L || type == LV_LAYOUT_COL_M || type == LV_LAYOUT_COL_R) {
lv_cont_layout_col(cont);
} else if(type == LV_LAYOUT_ROW_T || type == LV_LAYOUT_ROW_M || type == LV_LAYOUT_ROW_B) {
lv_cont_layout_row(cont);
} else if(type == LV_LAYOUT_PRETTY) {
lv_cont_layout_pretty(cont);
} else if(type == LV_LAYOUT_GRID) {
lv_cont_layout_grid(cont);
}
}
/**
* Handle column type layouts
* @param cont pointer to an object which layout should be handled
*/
static void lv_cont_layout_col(lv_obj_t * cont)
{
lv_layout_t type = lv_cont_get_layout(cont);
lv_obj_t * child;
/*Adjust margin and get the alignment type*/
lv_align_t align;
lv_style_t * style = lv_obj_get_style(cont);
lv_coord_t hpad_corr;
switch(type) {
case LV_LAYOUT_COL_L:
hpad_corr = style->body.padding.hor;
align = LV_ALIGN_IN_TOP_LEFT;
break;
case LV_LAYOUT_COL_M:
hpad_corr = 0;
align = LV_ALIGN_IN_TOP_MID;
break;
case LV_LAYOUT_COL_R:
hpad_corr = -style->body.padding.hor;
align = LV_ALIGN_IN_TOP_RIGHT;
break;
default:
hpad_corr = 0;
align = LV_ALIGN_IN_TOP_LEFT;
break;
}
/* Disable child change action because the children will be moved a lot
* an unnecessary child change signals could be sent*/
lv_obj_set_protect(cont, LV_PROTECT_CHILD_CHG);
/* Align the children */
lv_coord_t last_cord = style->body.padding.ver;
LL_READ_BACK(cont->child_ll, child) {
if(lv_obj_get_hidden(child) != false ||
lv_obj_is_protected(child, LV_PROTECT_POS) != false) continue;
lv_obj_align(child, cont, align, hpad_corr, last_cord);
last_cord += lv_obj_get_height(child) + style->body.padding.inner;
}
lv_obj_clear_protect(cont, LV_PROTECT_CHILD_CHG);
}
/**
* Handle row type layouts
* @param cont pointer to an object which layout should be handled
*/
static void lv_cont_layout_row(lv_obj_t * cont)
{
lv_layout_t type = lv_cont_get_layout(cont);
lv_obj_t * child;
/*Adjust margin and get the alignment type*/
lv_align_t align;
lv_style_t * style = lv_obj_get_style(cont);
lv_coord_t vpad_corr = style->body.padding.ver;
switch(type) {
case LV_LAYOUT_ROW_T:
vpad_corr = style->body.padding.ver;
align = LV_ALIGN_IN_TOP_LEFT;
break;
case LV_LAYOUT_ROW_M:
vpad_corr = 0;
align = LV_ALIGN_IN_LEFT_MID;
break;
case LV_LAYOUT_ROW_B:
vpad_corr = -style->body.padding.ver;
align = LV_ALIGN_IN_BOTTOM_LEFT;
break;
default:
vpad_corr = 0;
align = LV_ALIGN_IN_TOP_LEFT;
break;
}
/* Disable child change action because the children will be moved a lot
* an unnecessary child change signals could be sent*/
lv_obj_set_protect(cont, LV_PROTECT_CHILD_CHG);
/* Align the children */
lv_coord_t last_cord = style->body.padding.hor;
LL_READ_BACK(cont->child_ll, child) {
if(lv_obj_get_hidden(child) != false ||
lv_obj_is_protected(child, LV_PROTECT_POS) != false) continue;
lv_obj_align(child, cont, align, last_cord, vpad_corr);
last_cord += lv_obj_get_width(child) + style->body.padding.inner;
}
lv_obj_clear_protect(cont, LV_PROTECT_CHILD_CHG);
}
/**
* Handle the center layout
* @param cont pointer to an object which layout should be handled
*/
static void lv_cont_layout_center(lv_obj_t * cont)
{
lv_obj_t * child;
lv_style_t * style = lv_obj_get_style(cont);
uint32_t obj_num = 0;
lv_coord_t h_tot = 0;
LL_READ(cont->child_ll, child) {
if(lv_obj_get_hidden(child) != false ||
lv_obj_is_protected(child, LV_PROTECT_POS) != false) continue;
h_tot += lv_obj_get_height(child) + style->body.padding.inner;
obj_num ++;
}
if(obj_num == 0) return;
h_tot -= style->body.padding.inner;
/* Disable child change action because the children will be moved a lot
* an unnecessary child change signals could be sent*/
lv_obj_set_protect(cont, LV_PROTECT_CHILD_CHG);
/* Align the children */
lv_coord_t last_cord = - (h_tot / 2);
LL_READ_BACK(cont->child_ll, child) {
if(lv_obj_get_hidden(child) != false ||
lv_obj_is_protected(child, LV_PROTECT_POS) != false) continue;
lv_obj_align(child, cont, LV_ALIGN_CENTER, 0, last_cord + lv_obj_get_height(child) / 2);
last_cord += lv_obj_get_height(child) + style->body.padding.inner;
}
lv_obj_clear_protect(cont, LV_PROTECT_CHILD_CHG);
}
/**
* Handle the pretty layout. Put as many object as possible in row
* then begin a new row
* @param cont pointer to an object which layout should be handled
*/
static void lv_cont_layout_pretty(lv_obj_t * cont)
{
lv_obj_t * child_rs; /* Row starter child */
lv_obj_t * child_rc; /* Row closer child */
lv_obj_t * child_tmp; /* Temporary child */
lv_style_t * style = lv_obj_get_style(cont);
lv_coord_t w_obj = lv_obj_get_width(cont);
lv_coord_t act_y = style->body.padding.ver;
/* Disable child change action because the children will be moved a lot
* an unnecessary child change signals could be sent*/
child_rs = lv_ll_get_tail(&cont->child_ll); /*Set the row starter child*/
if(child_rs == NULL) return; /*Return if no child*/
lv_obj_set_protect(cont, LV_PROTECT_CHILD_CHG);
child_rc = child_rs; /*Initially the the row starter and closer is the same*/
while(child_rs != NULL) {
lv_coord_t h_row = 0;
lv_coord_t w_row = style->body.padding.hor * 2; /*The width is at least the left+right hpad*/
uint32_t obj_num = 0;
/*Find the row closer object and collect some data*/
do {
if(lv_obj_get_hidden(child_rc) == false &&
lv_obj_is_protected(child_rc, LV_PROTECT_POS) == false) {
/*If this object is already not fit then break*/
if(w_row + lv_obj_get_width(child_rc) > w_obj) {
/*Step back one child because the last already not fit, so the previous is the closer*/
if(child_rc != NULL && obj_num != 0) {
child_rc = lv_ll_get_next(&cont->child_ll, child_rc);
}
break;
}
w_row += lv_obj_get_width(child_rc) + style->body.padding.inner; /*Add the object width + opad*/
h_row = LV_MATH_MAX(h_row, lv_obj_get_height(child_rc)); /*Search the highest object*/
obj_num ++;
if(lv_obj_is_protected(child_rc, LV_PROTECT_FOLLOW)) break; /*If can not be followed by an other object then break here*/
}
child_rc = lv_ll_get_prev(&cont->child_ll, child_rc); /*Load the next object*/
if(obj_num == 0) child_rs = child_rc; /*If the first object was hidden (or too long) then set the next as first */
} while(child_rc != NULL);
/*If the object is too long then align it to the middle*/
if(obj_num == 0) {
if(child_rc != NULL) {
lv_obj_align(child_rc, cont, LV_ALIGN_IN_TOP_MID, 0, act_y);
h_row = lv_obj_get_height(child_rc); /*Not set previously because of the early break*/
}
}
/*If there is only one object in the row then align it to the middle*/
else if(obj_num == 1) {
lv_obj_align(child_rs, cont, LV_ALIGN_IN_TOP_MID, 0, act_y);
}
/*If there are two object in the row then align them proportionally*/
else if(obj_num == 2) {
lv_obj_t * obj1 = child_rs;
lv_obj_t * obj2 = lv_ll_get_prev(&cont->child_ll, child_rs);
w_row = lv_obj_get_width(obj1) + lv_obj_get_width(obj2);
lv_coord_t pad = (w_obj - w_row) / 3;
lv_obj_align(obj1, cont, LV_ALIGN_IN_TOP_LEFT, pad, act_y + (h_row - lv_obj_get_height(obj1)) / 2);
lv_obj_align(obj2, cont, LV_ALIGN_IN_TOP_RIGHT, -pad, act_y + (h_row - lv_obj_get_height(obj2)) / 2);
}
/* Align the children (from child_rs to child_rc)*/
else {
w_row -= style->body.padding.inner * obj_num;
lv_coord_t new_opad = (w_obj - w_row) / (obj_num - 1);
lv_coord_t act_x = style->body.padding.hor; /*x init*/
child_tmp = child_rs;
while(child_tmp != NULL) {
if(lv_obj_get_hidden(child_tmp) == false &&
lv_obj_is_protected(child_tmp, LV_PROTECT_POS) == false) {
lv_obj_align(child_tmp, cont, LV_ALIGN_IN_TOP_LEFT, act_x, act_y + (h_row - lv_obj_get_height(child_tmp)) / 2);
act_x += lv_obj_get_width(child_tmp) + new_opad;
}
if(child_tmp == child_rc) break;
child_tmp = lv_ll_get_prev(&cont->child_ll, child_tmp);
}
}
if(child_rc == NULL) break;
act_y += style->body.padding.inner + h_row; /*y increment*/
child_rs = lv_ll_get_prev(&cont->child_ll, child_rc); /*Go to the next object*/
child_rc = child_rs;
}
lv_obj_clear_protect(cont, LV_PROTECT_CHILD_CHG);
}
/**
* Handle the grid layout. Align same-sized objects in a grid
* @param cont pointer to an object which layout should be handled
*/
static void lv_cont_layout_grid(lv_obj_t * cont)
{
lv_obj_t * child;
lv_style_t * style = lv_obj_get_style(cont);
lv_coord_t w_tot = lv_obj_get_width(cont);
lv_coord_t w_obj = lv_obj_get_width(lv_obj_get_child(cont, NULL));
lv_coord_t h_obj = lv_obj_get_height(lv_obj_get_child(cont, NULL));
uint16_t obj_row = (w_tot - (2 * style->body.padding.hor)) / (w_obj + style->body.padding.inner); /*Obj. num. in a row*/
lv_coord_t x_ofs;
if(obj_row > 1) {
x_ofs = w_obj + (w_tot - (2 * style->body.padding.hor) - (obj_row * w_obj)) / (obj_row - 1);
} else {
x_ofs = w_tot / 2 - w_obj / 2;
}
lv_coord_t y_ofs = h_obj + style->body.padding.inner;
/* Disable child change action because the children will be moved a lot
* an unnecessary child change signals could be sent*/
lv_obj_set_protect(cont, LV_PROTECT_CHILD_CHG);
/* Align the children */
lv_coord_t act_x = style->body.padding.hor;
lv_coord_t act_y = style->body.padding.ver;
uint16_t obj_cnt = 0;
LL_READ_BACK(cont->child_ll, child) {
if(lv_obj_get_hidden(child) != false ||
lv_obj_is_protected(child, LV_PROTECT_POS) != false) continue;
if(obj_row > 1) {
lv_obj_set_pos(child, act_x, act_y);
act_x += x_ofs;
} else {
lv_obj_set_pos(child, x_ofs, act_y);
}
obj_cnt ++;
if(obj_cnt >= obj_row) {
obj_cnt = 0;
act_x = style->body.padding.hor;
act_y += y_ofs;
}
}
lv_obj_clear_protect(cont, LV_PROTECT_CHILD_CHG);
}
/**
* Handle auto fit. Set the size of the object to involve all children.
* @param cont pointer to an object which size will be modified
*/
static void lv_cont_refr_autofit(lv_obj_t * cont)
{
lv_cont_ext_t * ext = lv_obj_get_ext_attr(cont);
if(ext->hor_fit == 0 &&
ext->ver_fit == 0) {
return;
}
lv_area_t new_cords;
lv_area_t ori;
lv_style_t * style = lv_obj_get_style(cont);
lv_obj_t * i;
lv_coord_t hpad = style->body.padding.hor;
lv_coord_t vpad = style->body.padding.ver;
/*Search the side coordinates of the children*/
lv_obj_get_coords(cont, &ori);
lv_obj_get_coords(cont, &new_cords);
new_cords.x1 = LV_COORD_MAX;
new_cords.y1 = LV_COORD_MAX;
new_cords.x2 = LV_COORD_MIN;
new_cords.y2 = LV_COORD_MIN;
LL_READ(cont->child_ll, i) {
if(lv_obj_get_hidden(i) != false) continue;
new_cords.x1 = LV_MATH_MIN(new_cords.x1, i->coords.x1);
new_cords.y1 = LV_MATH_MIN(new_cords.y1, i->coords.y1);
new_cords.x2 = LV_MATH_MAX(new_cords.x2, i->coords.x2);
new_cords.y2 = LV_MATH_MAX(new_cords.y2, i->coords.y2);
}
/*If the value is not the init value then the page has >=1 child.*/
if(new_cords.x1 != LV_COORD_MAX) {
if(ext->hor_fit != 0) {
new_cords.x1 -= hpad;
new_cords.x2 += hpad;
} else {
new_cords.x1 = cont->coords.x1;
new_cords.x2 = cont->coords.x2;
}
if(ext->ver_fit != 0) {
new_cords.y1 -= vpad;
new_cords.y2 += vpad;
} else {
new_cords.y1 = cont->coords.y1;
new_cords.y2 = cont->coords.y2;
}
/*Do nothing if the coordinates are not changed*/
if(cont->coords.x1 != new_cords.x1 ||
cont->coords.y1 != new_cords.y1 ||
cont->coords.x2 != new_cords.x2 ||
cont->coords.y2 != new_cords.y2) {
lv_obj_invalidate(cont);
lv_area_copy(&cont->coords, &new_cords);
lv_obj_invalidate(cont);
/*Notify the object about its new coordinates*/
cont->signal_func(cont, LV_SIGNAL_CORD_CHG, &ori);
/*Inform the parent about the new coordinates*/
lv_obj_t * par = lv_obj_get_parent(cont);
par->signal_func(par, LV_SIGNAL_CHILD_CHG, cont);
}
}
}
#endif

View File

@@ -0,0 +1,163 @@
/**
* @file lv_cont.h
*
*/
#ifndef LV_CONT_H
#define LV_CONT_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#ifdef LV_CONF_INCLUDE_SIMPLE
#include "lv_conf.h"
#else
#include "../../lv_conf.h"
#endif
#if USE_LV_CONT != 0
#include "../lv_core/lv_obj.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/*Layout options*/
enum
{
LV_LAYOUT_OFF = 0,
LV_LAYOUT_CENTER,
LV_LAYOUT_COL_L, /*Column left align*/
LV_LAYOUT_COL_M, /*Column middle align*/
LV_LAYOUT_COL_R, /*Column right align*/
LV_LAYOUT_ROW_T, /*Row top align*/
LV_LAYOUT_ROW_M, /*Row middle align*/
LV_LAYOUT_ROW_B, /*Row bottom align*/
LV_LAYOUT_PRETTY, /*Put as many object as possible in row and begin a new row*/
LV_LAYOUT_GRID, /*Align same-sized object into a grid*/
};
typedef uint8_t lv_layout_t;
typedef struct
{
/*Inherited from 'base_obj' so no inherited ext. */ /*Ext. of ancestor*/
/*New data for this type */
uint8_t layout :4; /*A layout from 'lv_cont_layout_t' enum*/
uint8_t hor_fit :1; /*1: Enable horizontal fit to involve all children*/
uint8_t ver_fit :1; /*1: Enable horizontal fit to involve all children*/
} lv_cont_ext_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a container objects
* @param par pointer to an object, it will be the parent of the new container
* @param copy pointer to a container object, if not NULL then the new object will be copied from it
* @return pointer to the created container
*/
lv_obj_t * lv_cont_create(lv_obj_t * par, const lv_obj_t * copy);
/*=====================
* Setter functions
*====================*/
/**
* Set a layout on a container
* @param cont pointer to a container object
* @param layout a layout from 'lv_cont_layout_t'
*/
void lv_cont_set_layout(lv_obj_t * cont, lv_layout_t layout);
/**
* Enable the horizontal or vertical fit.
* The container size will be set to involve the children horizontally or vertically.
* @param cont pointer to a container object
* @param hor_en true: enable the horizontal fit
* @param ver_en true: enable the vertical fit
*/
void lv_cont_set_fit(lv_obj_t * cont, bool hor_en, bool ver_en);
/**
* Set the style of a container
* @param cont pointer to a container object
* @param style pointer to the new style
*/
static inline void lv_cont_set_style(lv_obj_t *cont, lv_style_t * style)
{
lv_obj_set_style(cont, style);
}
/*=====================
* Getter functions
*====================*/
/**
* Get the layout of a container
* @param cont pointer to container object
* @return the layout from 'lv_cont_layout_t'
*/
lv_layout_t lv_cont_get_layout(const lv_obj_t * cont);
/**
* Get horizontal fit enable attribute of a container
* @param cont pointer to a container object
* @return true: horizontal fit is enabled; false: disabled
*/
bool lv_cont_get_hor_fit(const lv_obj_t * cont);
/**
* Get vertical fit enable attribute of a container
* @param cont pointer to a container object
* @return true: vertical fit is enabled; false: disabled
*/
bool lv_cont_get_ver_fit(const lv_obj_t * cont);
/**
* Get that width reduced by the horizontal padding. Useful if a layout is used.
* @param cont pointer to a container object
* @return the width which still fits into the container
*/
lv_coord_t lv_cont_get_fit_width(lv_obj_t * cont);
/**
* Get that height reduced by the vertical padding. Useful if a layout is used.
* @param cont pointer to a container object
* @return the height which still fits into the container
*/
lv_coord_t lv_cont_get_fit_height(lv_obj_t * cont);
/**
* Get the style of a container
* @param cont pointer to a container object
* @return pointer to the container's style
*/
static inline lv_style_t * lv_cont_get_style(const lv_obj_t *cont)
{
return lv_obj_get_style(cont);
}
/**********************
* MACROS
**********************/
#endif /*USE_LV_CONT*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_CONT_H*/

View File

@@ -0,0 +1,981 @@
/*
* Copyright (c) 2019 CTCaer
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file lv_ddlist.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_ddlist.h"
#if USE_LV_DDLIST != 0
#include "../lv_draw/lv_draw.h"
#include "../lv_core/lv_group.h"
#include "../lv_core/lv_indev.h"
#include "../lv_themes/lv_theme.h"
#include "../lv_misc/lv_symbol_def.h"
#include "../lv_misc/lv_anim.h"
//#include <strings.h>
/*********************
* DEFINES
*********************/
#if USE_LV_ANIMATION
# ifndef LV_DDLIST_ANIM_TIME
# define LV_DDLIST_ANIM_TIME 200 /*ms*/
# endif
#else
# undef LV_DDLIST_ANIM_TIME
# define LV_DDLIST_ANIM_TIME 0 /*No animation*/
#endif
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static bool lv_ddlist_design(lv_obj_t * ddlist, const lv_area_t * mask, lv_design_mode_t mode);
static lv_res_t lv_ddlist_signal(lv_obj_t * ddlist, lv_signal_t sign, void * param);
static lv_res_t lv_ddlist_scrl_signal(lv_obj_t * scrl, lv_signal_t sign, void * param);
static lv_res_t lv_ddlist_release_action(lv_obj_t * ddlist);
static lv_res_t lv_ddlist_press_action(lv_obj_t * ddlist);
static void lv_ddlist_refr_size(lv_obj_t * ddlist, bool anim_en);
static void lv_ddlist_pos_current_option(lv_obj_t * ddlist);
/**********************
* STATIC VARIABLES
**********************/
static lv_signal_func_t ancestor_signal;
static lv_signal_func_t ancestor_scrl_signal;
static lv_design_func_t ancestor_design;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Create a drop down list objects
* @param par pointer to an object, it will be the parent of the new drop down list
* @param copy pointer to a drop down list object, if not NULL then the new object will be copied from it
* @return pointer to the created drop down list
*/
lv_obj_t * lv_ddlist_create(lv_obj_t * par, const lv_obj_t * copy)
{
LV_LOG_TRACE("drop down list create started");
/*Create the ancestor drop down list*/
lv_obj_t * new_ddlist = lv_page_create(par, copy);
lv_mem_assert(new_ddlist);
if(new_ddlist == NULL) return NULL;
if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_ddlist);
if(ancestor_scrl_signal == NULL) ancestor_scrl_signal = lv_obj_get_signal_func(lv_page_get_scrl(new_ddlist));
if(ancestor_design == NULL) ancestor_design = lv_obj_get_design_func(new_ddlist);
/*Allocate the drop down list type specific extended data*/
lv_ddlist_ext_t * ext = lv_obj_allocate_ext_attr(new_ddlist, sizeof(lv_ddlist_ext_t));
lv_mem_assert(ext);
if(ext == NULL) return NULL;
/*Initialize the allocated 'ext' */
ext->label = NULL;
ext->action = NULL;
ext->opened = 0;
ext->fix_height = 0;
ext->sel_opt_id = 0;
ext->sel_opt_id_ori = 0;
ext->option_cnt = 0;
ext->anim_time = LV_DDLIST_ANIM_TIME;
ext->sel_style = &lv_style_plain_color;
ext->draw_arrow = 0; /*Do not draw arrow by default*/
ext->direction_up = 0;
/*The signal and design functions are not copied so set them here*/
lv_obj_set_signal_func(new_ddlist, lv_ddlist_signal);
lv_obj_set_signal_func(lv_page_get_scrl(new_ddlist), lv_ddlist_scrl_signal);
lv_obj_set_design_func(new_ddlist, lv_ddlist_design);
/*Init the new drop down list drop down list*/
if(copy == NULL) {
lv_obj_t * scrl = lv_page_get_scrl(new_ddlist);
lv_obj_set_drag(scrl, false);
lv_page_set_scrl_fit(new_ddlist, true, true);
ext->label = lv_label_create(new_ddlist, NULL);
lv_cont_set_fit(new_ddlist, true, false);
lv_page_set_rel_action(new_ddlist, lv_ddlist_release_action);
lv_page_set_pr_action(new_ddlist, lv_ddlist_press_action);
lv_page_set_sb_mode(new_ddlist, LV_SB_MODE_DRAG);
lv_page_set_sb_mode(new_ddlist, LV_SB_MODE_HIDE);
lv_page_set_style(new_ddlist, LV_PAGE_STYLE_SCRL, &lv_style_transp_tight);
lv_ddlist_set_options(new_ddlist, "Option 1\nOption 2\nOption 3");
/*Set the default styles*/
lv_theme_t * th = lv_theme_get_current();
if(th) {
lv_ddlist_set_style(new_ddlist, LV_DDLIST_STYLE_BG, th->ddlist.bg);
lv_ddlist_set_style(new_ddlist, LV_DDLIST_STYLE_BGO, th->ddlist.bgo);
lv_ddlist_set_style(new_ddlist, LV_DDLIST_STYLE_PR, th->ddlist.pr);
lv_ddlist_set_style(new_ddlist, LV_DDLIST_STYLE_SEL, th->ddlist.sel);
lv_ddlist_set_style(new_ddlist, LV_DDLIST_STYLE_SB, th->ddlist.sb);
} else {
lv_ddlist_set_style(new_ddlist, LV_DDLIST_STYLE_BG, &lv_style_pretty);
lv_ddlist_set_style(new_ddlist, LV_DDLIST_STYLE_BGO, &lv_style_pretty);
lv_ddlist_set_style(new_ddlist, LV_DDLIST_STYLE_PR, &lv_style_pretty);
lv_ddlist_set_style(new_ddlist, LV_DDLIST_STYLE_SEL, &lv_style_plain_color);
lv_ddlist_set_style(new_ddlist, LV_DDLIST_STYLE_SB, &lv_style_pretty_color);
}
}
/*Copy an existing drop down list*/
else {
lv_ddlist_ext_t * copy_ext = lv_obj_get_ext_attr(copy);
ext->label = lv_label_create(new_ddlist, copy_ext->label);
lv_label_set_text(ext->label, lv_label_get_text(copy_ext->label));
ext->sel_opt_id = copy_ext->sel_opt_id;
ext->fix_height = copy_ext->fix_height;
ext->action = copy_ext->action;
ext->option_cnt = copy_ext->option_cnt;
ext->sel_style = copy_ext->sel_style;
ext->anim_time = copy_ext->anim_time;
ext->draw_arrow = copy_ext->draw_arrow;
/*Refresh the style with new signal function*/
lv_obj_refresh_style(new_ddlist);
}
LV_LOG_INFO("drop down list created");
return new_ddlist;
}
/*=====================
* Setter functions
*====================*/
/**
* Set arrow draw in a drop down list
* @param ddlist pointer to drop down list object
* @param en enable/disable a arrow draw. E.g. "true" for draw.
*/
void lv_ddlist_set_draw_arrow(lv_obj_t * ddlist, bool en)
{
lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist);
/*Set the flag*/
ext->draw_arrow = en;
}
/**
* Set the options in a drop down list from a string
* @param ddlist pointer to drop down list object
* @param options a string with '\n' separated options. E.g. "One\nTwo\nThree"
*/
void lv_ddlist_set_options(lv_obj_t * ddlist, const char * options)
{
lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist);
/*Count the '\n'-s to determine the number of options*/
ext->option_cnt = 0;
uint16_t i;
for(i = 0; options[i] != '\0'; i++) {
if(options[i] == '\n') ext->option_cnt++;
}
ext->option_cnt++; /*Last option in the at row*/
lv_label_set_text(ext->label, options);
lv_ddlist_refr_size(ddlist, false);
}
/**
* Set the selected option
* @param ddlist pointer to drop down list object
* @param sel_opt id of the selected option (0 ... number of option - 1);
*/
void lv_ddlist_set_selected(lv_obj_t * ddlist, uint16_t sel_opt)
{
lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist);
if(ext->sel_opt_id == sel_opt) return;
ext->sel_opt_id = sel_opt < ext->option_cnt ? sel_opt : ext->option_cnt - 1;
ext->sel_opt_id_ori = ext->sel_opt_id;
/*Move the list to show the current option*/
if(ext->opened == 0) {
lv_ddlist_pos_current_option(ddlist);
} else {
lv_obj_invalidate(ddlist);
}
}
/**
* Set a function to call when a new option is chosen
* @param ddlist pointer to a drop down list
* @param action pointer to a call back function
*/
void lv_ddlist_set_action(lv_obj_t * ddlist, lv_action_t action)
{
lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist);
ext->action = action;
}
/**
* Set the fix height for the drop down list
* If 0 then the opened ddlist will be auto. sized else the set height will be applied.
* @param ddlist pointer to a drop down list
* @param h the height when the list is opened (0: auto size)
*/
void lv_ddlist_set_fix_height(lv_obj_t * ddlist, lv_coord_t h)
{
lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist);
if(ext->fix_height == h) return;
ext->fix_height = h;
lv_ddlist_refr_size(ddlist, false);
}
/**
* Enable or disable the horizontal fit to the content
* @param ddlist pointer to a drop down list
* @param en true: enable auto fit; false: disable auto fit
*/
void lv_ddlist_set_hor_fit(lv_obj_t * ddlist, bool en)
{
lv_cont_set_fit(ddlist, en, lv_cont_get_ver_fit(ddlist));
lv_page_set_scrl_fit(ddlist, en, lv_page_get_scrl_fit_ver(ddlist));
lv_ddlist_refr_size(ddlist, false);
}
/**
* Set the open/close animation time.
* @param ddlist pointer to a drop down list
* @param anim_time: open/close animation time [ms]
*/
void lv_ddlist_set_anim_time(lv_obj_t * ddlist, uint16_t anim_time)
{
lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist);
#if USE_LV_ANIMATION == 0
anim_time = 0;
#endif
ext->anim_time = anim_time;
}
/**
* Set a style of a drop down list
* @param ddlist pointer to a drop down list object
* @param type which style should be set
* @param style pointer to a style
*/
void lv_ddlist_set_style(lv_obj_t * ddlist, lv_ddlist_style_t type, lv_style_t * style)
{
lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist);
switch(type) {
case LV_DDLIST_STYLE_BG:
lv_page_set_style(ddlist, LV_PAGE_STYLE_BG, style);
break;
case LV_DDLIST_STYLE_BGO:
lv_page_set_style(ddlist, LV_PAGE_STYLE_BGO, style);
break;
case LV_DDLIST_STYLE_PR:
lv_page_set_style(ddlist, LV_PAGE_STYLE_PR, style);
break;
case LV_DDLIST_STYLE_SB:
lv_page_set_style(ddlist, LV_PAGE_STYLE_SB, style);
break;
case LV_DDLIST_STYLE_SEL:
ext->sel_style = style;
lv_obj_t * scrl = lv_page_get_scrl(ddlist);
lv_obj_refresh_ext_size(scrl); /*Because of the wider selected rectangle*/
break;
}
}
void lv_ddlist_set_align(lv_obj_t *ddlist, lv_label_align_t align)
{
lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist);
lv_label_set_align(ext->label, align);
}
void lv_ddlist_set_direction_up(lv_obj_t *ddlist, bool enable)
{
lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist);
ext->direction_up = enable;
}
/*=====================
* Getter functions
*====================*/
/**
* Get arrow draw in a drop down list
* @param ddlist pointer to drop down list object
*/
bool lv_ddlist_get_draw_arrow(lv_obj_t * ddlist)
{
lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist);
return ext->draw_arrow;
}
/**
* Get the options of a drop down list
* @param ddlist pointer to drop down list object
* @return the options separated by '\n'-s (E.g. "Option1\nOption2\nOption3")
*/
const char * lv_ddlist_get_options(const lv_obj_t * ddlist)
{
lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist);
return lv_label_get_text(ext->label);
}
/**
* Get the selected option
* @param ddlist pointer to drop down list object
* @return id of the selected option (0 ... number of option - 1);
*/
uint16_t lv_ddlist_get_selected(const lv_obj_t * ddlist)
{
lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist);
return ext->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
*/
void lv_ddlist_get_selected_str(const lv_obj_t * ddlist, char * buf)
{
lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist);
uint16_t i;
uint16_t line = 0;
const char * opt_txt = lv_label_get_text(ext->label);
uint16_t txt_len = strlen(opt_txt);
for(i = 0; i < txt_len && line != ext->sel_opt_id; i++) {
if(opt_txt[i] == '\n') line ++;
}
uint16_t c;
for(c = 0; opt_txt[i] != '\n' && i < txt_len; c++, i++) buf[c] = opt_txt[i];
buf[c] = '\0';
}
/**
* Get the "option selected" callback function
* @param ddlist pointer to a drop down list
* @return pointer to the call back function
*/
lv_action_t lv_ddlist_get_action(const lv_obj_t * ddlist)
{
lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist);
return ext->action;
}
/**
* Get the fix height value.
* @param ddlist pointer to a drop down list object
* @return the height if the ddlist is opened (0: auto size)
*/
lv_coord_t lv_ddlist_get_fix_height(const lv_obj_t * ddlist)
{
lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist);
return ext->fix_height;
}
/**
* Get the open/close animation time.
* @param ddlist pointer to a drop down list
* @return open/close animation time [ms]
*/
uint16_t lv_ddlist_get_anim_time(const lv_obj_t * ddlist)
{
lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist);
return ext->anim_time;
}
/**
* Get a style of a drop down list
* @param ddlist pointer to a drop down list object
* @param type which style should be get
* @return style pointer to a style
*/
lv_style_t * lv_ddlist_get_style(const lv_obj_t * ddlist, lv_ddlist_style_t type)
{
lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist);
switch(type) {
case LV_DDLIST_STYLE_BG:
return lv_page_get_style(ddlist, LV_PAGE_STYLE_BG);
case LV_DDLIST_STYLE_BGO:
return lv_page_get_style(ddlist, LV_PAGE_STYLE_BGO);
case LV_DDLIST_STYLE_PR:
return lv_page_get_style(ddlist, LV_PAGE_STYLE_PR);
case LV_DDLIST_STYLE_SB:
return lv_page_get_style(ddlist, LV_PAGE_STYLE_SB);
case LV_DDLIST_STYLE_SEL:
return ext->sel_style;
default:
return NULL;
}
/*To avoid warning*/
return NULL;
}
lv_label_align_t lv_ddlist_get_align(const lv_obj_t *ddlist)
{
lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist);
return lv_label_get_align(ext->label);
}
/*=====================
* Other functions
*====================*/
/**
* Open the drop down list with or without animation
* @param ddlist pointer to drop down list object
* @param anim_en true: use animation; false: not use animations
*/
void lv_ddlist_open(lv_obj_t * ddlist, bool anim_en)
{
#if USE_LV_ANIMATION == 0
anim_en = false;
#endif
lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist);
ext->opened = 1;
lv_obj_set_drag(lv_page_get_scrl(ddlist), true);
lv_ddlist_refr_size(ddlist, anim_en);
}
/**
* Close (Collapse) the drop down list
* @param ddlist pointer to drop down list object
* @param anim_en true: use animation; false: not use animations
*/
void lv_ddlist_close(lv_obj_t * ddlist, bool anim_en)
{
#if USE_LV_ANIMATION == 0
anim_en = false;
#endif
lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist);
ext->opened = 0;
lv_obj_set_drag(lv_page_get_scrl(ddlist), false);
lv_ddlist_refr_size(ddlist, anim_en);
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Get the text alignment flag for a drop down list.
* @param ddlist drop down list
* @return text alignment flag
*/
static lv_txt_flag_t lv_ddlist_get_txt_flag(const lv_obj_t *ddlist)
{
lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist);
/*The label might be already deleted so just return with some value*/
if(!ext->label) return LV_TXT_FLAG_CENTER;
lv_label_align_t align = lv_label_get_align(ext->label);
switch(align)
{
default:
case LV_LABEL_ALIGN_LEFT:
return LV_TXT_FLAG_NONE;
case LV_LABEL_ALIGN_CENTER:
return LV_TXT_FLAG_CENTER;
case LV_LABEL_ALIGN_RIGHT:
return LV_TXT_FLAG_RIGHT;
}
}
/**
* Handle the drawing related tasks of the drop down lists
* @param ddlist pointer to an object
* @param mask the object will be drawn only in this area
* @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area
* (return 'true' if yes)
* LV_DESIGN_DRAW: draw the object (always return 'true')
* LV_DESIGN_DRAW_POST: drawing after every children are drawn
* @param return true/false, depends on 'mode'
*/
static bool lv_ddlist_design(lv_obj_t * ddlist, const lv_area_t * mask, lv_design_mode_t mode)
{
/*Return false if the object is not covers the mask_p area*/
if(mode == LV_DESIGN_COVER_CHK) {
return ancestor_design(ddlist, mask, mode);
}
/*Draw the object*/
else if(mode == LV_DESIGN_DRAW_MAIN) {
ancestor_design(ddlist, mask, mode);
lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist);
lv_opa_t opa_scale = lv_obj_get_opa_scale(ddlist);
/*If the list is opened draw a rectangle under the selected item*/
if(ext->opened != 0) {
lv_style_t * style = lv_ddlist_get_style(ddlist, LV_DDLIST_STYLE_BG);
const lv_font_t * font = style->text.font;
lv_coord_t font_h = lv_font_get_height(font);
/*Draw the selected*/
lv_area_t rect_area;
rect_area.y1 = ext->label->coords.y1;
rect_area.y1 += ext->sel_opt_id * (font_h + style->text.line_space);
rect_area.y1 -= style->text.line_space / 2;
rect_area.y2 = rect_area.y1 + font_h + style->text.line_space - 1;
rect_area.x1 = ddlist->coords.x1;
rect_area.x2 = ddlist->coords.x2;
lv_draw_rect(&rect_area, mask, ext->sel_style, opa_scale);
}
}
/*Post draw when the children are drawn*/
else if(mode == LV_DESIGN_DRAW_POST) {
/*Redraw the text on the selected area with a different color*/
lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist);
lv_opa_t opa_scale = lv_obj_get_opa_scale(ddlist);
/*Redraw only in opened state*/
if(ext->opened) {
lv_style_t * style = lv_ddlist_get_style(ddlist, LV_DDLIST_STYLE_BG);
const lv_font_t * font = style->text.font;
lv_coord_t font_h = lv_font_get_height(font);
lv_area_t area_sel;
area_sel.y1 = ext->label->coords.y1;
area_sel.y1 += ext->sel_opt_id * (font_h + style->text.line_space);
area_sel.y1 -= style->text.line_space / 2;
area_sel.y2 = area_sel.y1 + font_h + style->text.line_space - 1;
area_sel.x1 = ddlist->coords.x1;
area_sel.x2 = ddlist->coords.x2;
lv_area_t mask_sel;
bool area_ok;
area_ok = lv_area_intersect(&mask_sel, mask, &area_sel);
if(area_ok) {
lv_style_t * sel_style = lv_ddlist_get_style(ddlist, LV_DDLIST_STYLE_SEL);
lv_style_t new_style;
lv_style_copy(&new_style, style);
new_style.text.color = sel_style->text.color;
new_style.text.opa = sel_style->text.opa;
lv_txt_flag_t flag = lv_ddlist_get_txt_flag(ddlist);
lv_draw_label(&ext->label->coords, &mask_sel, &new_style, opa_scale,
lv_label_get_text(ext->label), flag, NULL);
}
}
/*Add a down symbol in ddlist when closed*/
else
{
/*Draw a arrow in ddlist if enabled*/
if(ext->draw_arrow)
{
lv_style_t * style = lv_ddlist_get_style(ddlist, LV_DDLIST_STYLE_BG);
const lv_font_t * font = style->text.font;
lv_style_t * sel_style = lv_ddlist_get_style(ddlist, LV_DDLIST_STYLE_BG);
lv_coord_t font_h = lv_font_get_height(font);
lv_style_t new_style;
lv_style_copy(&new_style, style);
new_style.text.color = sel_style->text.color;
new_style.text.opa = sel_style->text.opa;
lv_area_t area_arrow;
area_arrow.x2 = ddlist->coords.x2 - style->body.padding.hor;
if (!ext->direction_up)
area_arrow.x1 = area_arrow.x2 - lv_txt_get_width(SYMBOL_DOWN, strlen(SYMBOL_DOWN), sel_style->text.font, 0, 0);
else
area_arrow.x1 = area_arrow.x2 - lv_txt_get_width(SYMBOL_UP, strlen(SYMBOL_UP), sel_style->text.font, 0, 0);
area_arrow.y1 = ddlist->coords.y1 + style->text.line_space;
area_arrow.y2 = area_arrow.y1 + font_h;
lv_area_t mask_arrow;
bool area_ok;
area_ok = lv_area_intersect(&mask_arrow, mask, &area_arrow);
if (area_ok)
{
if (!ext->direction_up)
lv_draw_label(&area_arrow, &mask_arrow, &new_style, opa_scale,
SYMBOL_DOWN, LV_TXT_FLAG_NONE, NULL); /*Use a down arrow in ddlist, you can replace it with your custom symbol*/
else
lv_draw_label(&area_arrow, &mask_arrow, &new_style, opa_scale,
SYMBOL_UP, LV_TXT_FLAG_NONE, NULL); /*Use a down arrow in ddlist, you can replace it with your custom symbol*/
}
}
}
/*Draw the scrollbar in the ancestor page design function*/
ancestor_design(ddlist, mask, mode);
}
return true;
}
/**
* Signal function of the drop down list
* @param ddlist pointer to a drop down list object
* @param sign a signal type from lv_signal_t enum
* @param param pointer to a signal specific variable
* @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
*/
static lv_res_t lv_ddlist_signal(lv_obj_t * ddlist, lv_signal_t sign, void * param)
{
lv_res_t res;
/* Include the ancient signal function */
res = ancestor_signal(ddlist, sign, param);
if(res != LV_RES_OK) return res;
lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist);
if(sign == LV_SIGNAL_STYLE_CHG) {
//! lv_ddlist_refr_size(ddlist, 0); // uncommented in OG
} else if(sign == LV_SIGNAL_CLEANUP) {
ext->label = NULL;
} else if(sign == LV_SIGNAL_FOCUS) {
#if USE_LV_GROUP
lv_group_t * g = lv_obj_get_group(ddlist);
bool editing = lv_group_get_editing(g);
lv_hal_indev_type_t indev_type = lv_indev_get_type(lv_indev_get_act());
/*Encoders need special handling*/
if(indev_type == LV_INDEV_TYPE_ENCODER) {
/*Open the list if editing*/
if(editing) {
ext->opened = true;
ext->sel_opt_id_ori = ext->sel_opt_id;
lv_ddlist_refr_size(ddlist, true);
}
/*Close the lift if navigating*/
else {
ext->opened = false;
ext->sel_opt_id = ext->sel_opt_id_ori;
lv_ddlist_refr_size(ddlist, true);
}
} else {
/*Open the list if closed*/
if(!ext->opened) {
ext->opened = true;
ext->sel_opt_id_ori = ext->sel_opt_id; /*Save the current value. Used to revert this state if ENER wont't be pressed*/
lv_ddlist_refr_size(ddlist, true);
}
}
#endif
} else if(sign == LV_SIGNAL_DEFOCUS) {
if(ext->opened) {
ext->opened = false;
ext->sel_opt_id = ext->sel_opt_id_ori;
lv_ddlist_refr_size(ddlist, true);
}
} else if(sign == LV_SIGNAL_CONTROLL) {
char c = *((char *)param);
if(c == LV_GROUP_KEY_RIGHT || c == LV_GROUP_KEY_DOWN) {
if(!ext->opened) {
ext->opened = 1;
lv_ddlist_refr_size(ddlist, true);
}
if(ext->sel_opt_id + 1 < ext->option_cnt) {
ext->sel_opt_id ++;
lv_ddlist_pos_current_option(ddlist);
lv_obj_invalidate(ddlist);
}
} else if(c == LV_GROUP_KEY_LEFT || c == LV_GROUP_KEY_UP) {
if(!ext->opened) {
ext->opened = 1;
lv_ddlist_refr_size(ddlist, true);
}
if(ext->sel_opt_id > 0) {
ext->sel_opt_id --;
lv_ddlist_pos_current_option(ddlist);
lv_obj_invalidate(ddlist);
}
} else if(c == LV_GROUP_KEY_ENTER) {
if(ext->opened) {
ext->sel_opt_id_ori = ext->sel_opt_id;
ext->opened = 0;
if(ext->action) ext->action(ddlist);
#if USE_LV_GROUP
lv_group_t * g = lv_obj_get_group(ddlist);
bool editing = lv_group_get_editing(g);
if(editing) lv_group_set_editing(g, false); /*In edit mode go to navigate mode if an option is selected*/
#endif
} else {
ext->opened = 1;
}
lv_ddlist_refr_size(ddlist, true);
} else if(c == LV_GROUP_KEY_ESC) {
if(ext->opened) {
ext->opened = 0;
ext->sel_opt_id = ext->sel_opt_id_ori;
lv_ddlist_refr_size(ddlist, true);
}
}
} else if(sign == LV_SIGNAL_GET_EDITABLE) {
bool * editable = (bool *)param;
*editable = true;
} else if(sign == LV_SIGNAL_GET_TYPE) {
lv_obj_type_t * buf = param;
uint8_t i;
for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/
if(buf->type[i] == NULL) break;
}
buf->type[i] = "lv_ddlist";
}
return res;
}
/**
* Signal function of the drop down list's scrollable part
* @param scrl pointer to a drop down list's scrollable part
* @param sign a signal type from lv_signal_t enum
* @param param pointer to a signal specific variable
* @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
*/
static lv_res_t lv_ddlist_scrl_signal(lv_obj_t * scrl, lv_signal_t sign, void * param)
{
lv_res_t res;
/* Include the ancient signal function */
res = ancestor_scrl_signal(scrl, sign, param);
if(res != LV_RES_OK) return res;
lv_obj_t * ddlist = lv_obj_get_parent(scrl);
if(sign == LV_SIGNAL_REFR_EXT_SIZE) {
/* Because of the wider selected rectangle ext. size
* In this way by dragging the scrollable part the wider rectangle area can be redrawn too*/
lv_style_t * style = lv_ddlist_get_style(ddlist, LV_DDLIST_STYLE_BG);
if(scrl->ext_size < style->body.padding.hor) scrl->ext_size = style->body.padding.hor;
} else if(sign == LV_SIGNAL_CLEANUP) {
lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist);
ext->label = NULL; /*The label is already deleted*/
}
return res;
}
/**
* Called when a drop down list is released to open it or set new option
* @param ddlist pointer to a drop down list object
* @return LV_ACTION_RES_INV if the ddlist it deleted in the user callback else LV_ACTION_RES_OK
*/
static lv_res_t lv_ddlist_release_action(lv_obj_t * ddlist)
{
lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist);
if (!lv_obj_get_click(ddlist)) return LV_RES_OK;
if(ext->opened == 0) { /*Open the list*/
ext->opened = 1;
lv_ddlist_set_style(ddlist, LV_DDLIST_STYLE_BG, lv_ddlist_get_style(ddlist, LV_DDLIST_STYLE_BGO));
lv_obj_set_drag(lv_page_get_scrl(ddlist), true);
} else {
ext->opened = 0;
//lv_ddlist_set_style(ddlist, LV_DDLIST_STYLE_BG, lv_ddlist_get_style(ddlist, lv_ddlist_get_style(ddlist, LV_DDLIST_STYLE_PR)));
lv_obj_set_drag(lv_page_get_scrl(ddlist), false);
/*Search the clicked option*/
lv_indev_t * indev = lv_indev_get_act();
lv_point_t p;
lv_indev_get_point(indev, &p);
p.x -= ext->label->coords.x1;
p.y -= ext->label->coords.y1;
uint16_t letter_i;
letter_i = lv_label_get_letter_on(ext->label, &p);
uint16_t new_opt = 0;
const char * txt = lv_label_get_text(ext->label);
uint32_t i = 0;
uint32_t line_cnt = 0;
uint32_t letter;
for(line_cnt = 0; line_cnt < letter_i; line_cnt++) {
letter = lv_txt_encoded_next(txt, &i);
if(letter == '\n') new_opt ++;
}
ext->sel_opt_id = new_opt;
if(ext->action != NULL) {
ext->action(ddlist);
}
}
lv_ddlist_refr_size(ddlist, true);
return LV_RES_OK;
}
static lv_res_t lv_ddlist_press_action(lv_obj_t * ddlist)
{
lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist);
if (!lv_obj_get_click(ddlist)) return LV_RES_OK;
if (ext->opened == 0)
{ /*Open the list*/
lv_ddlist_set_style(ddlist, LV_DDLIST_STYLE_BG, lv_ddlist_get_style(ddlist, LV_DDLIST_STYLE_PR));
}
else
{
//lv_ddlist_set_style(ddlist, LV_DDLIST_STYLE_BG, lv_ddlist_get_style(ddlist, LV_DDLIST_STYLE_BGO));
//lv_obj_set_drag(lv_page_get_scrl(ddlist), false);
///*Search the clicked option*/
//lv_indev_t * indev = lv_indev_get_act();
//lv_point_t p;
//lv_indev_get_point(indev, &p);
//p.x -= ext->label->coords.x1;
//p.y -= ext->label->coords.y1;
//uint16_t letter_i;
//letter_i = lv_label_get_letter_on(ext->label, &p);
//uint16_t new_opt = 0;
//const char * txt = lv_label_get_text(ext->label);
//uint32_t i = 0;
//uint32_t line_cnt = 0;
//uint32_t letter;
//for (line_cnt = 0; line_cnt < letter_i; line_cnt++)
//{
// letter = lv_txt_encoded_next(txt, &i);
// if (letter == '\n') new_opt++;
//}
//ext->sel_opt_id = new_opt;
//if (ext->action != NULL)
//{
// ext->action(ddlist);
//}
}
return LV_RES_OK;
}
/**
* Refresh the size of drop down list according to its status (open or closed)
* @param ddlist pointer to a drop down list object
* @param anim_en Change the size (open/close) with or without animation (true/false)
*/
static void lv_ddlist_refr_size(lv_obj_t * ddlist, bool anim_en)
{
#if USE_LV_ANIMATION == 0
anim_en = false;
#endif
lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist);
lv_style_t * style = lv_obj_get_style(ddlist);
lv_coord_t new_height, full_height;
bool current_state = 0;
if(ext->opened) { /*Open the list*/
if(ext->fix_height == 0) new_height = lv_obj_get_height(lv_page_get_scrl(ddlist)) + 2 * style->body.padding.ver;
else new_height = ext->fix_height;
current_state = 1;
lv_page_set_sb_mode(ddlist, LV_SB_MODE_UNHIDE);
} else { /*Close the list*/
const lv_font_t * font = style->text.font;
lv_style_t * label_style = lv_obj_get_style(ext->label);
lv_coord_t font_h = lv_font_get_height(font);
new_height = font_h + 2 * label_style->text.line_space;
//full_height = lv_obj_get_height(lv_page_get_scrl(ddlist)) + 2 * style->body.padding.ver;
current_state = 0;
lv_page_set_sb_mode(ddlist, LV_SB_MODE_HIDE);
}
if(anim_en == 0 || ext->direction_up) {
lv_obj_set_height(ddlist, new_height);
if (ext->direction_up)
{
full_height = lv_obj_get_height(lv_page_get_scrl(ddlist)) - lv_font_get_height(style->text.font);
if (current_state)
lv_obj_set_y(ddlist, lv_obj_get_y(ddlist) - full_height);
else
lv_obj_set_y(ddlist, lv_obj_get_y(ddlist) + full_height);
}
lv_ddlist_pos_current_option(ddlist);
#if USE_LV_ANIMATION
lv_anim_del(ddlist, (lv_anim_fp_t)lv_obj_set_height); /*If an animation is in progress then it will overwrite this changes*/
} else {
lv_anim_t a;
a.var = ddlist;
a.start = lv_obj_get_height(ddlist);
a.end = new_height;
a.fp = (lv_anim_fp_t)lv_obj_set_height;
a.path = lv_anim_path_linear;
a.end_cb = (lv_anim_cb_t)lv_ddlist_pos_current_option;
a.act_time = 0;
a.time = ext->anim_time;
a.playback = 0;
a.playback_pause = 0;
a.repeat = 0;
a.repeat_pause = 0;
lv_anim_create(&a);
#endif
}
}
/**
* Set the position of list when it is closed to show the selected item
* @param ddlist pointer to a drop down list
*/
static void lv_ddlist_pos_current_option(lv_obj_t * ddlist)
{
lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist);
lv_style_t * style = lv_obj_get_style(ddlist);
const lv_font_t * font = style->text.font;
lv_coord_t font_h = lv_font_get_height(font);
lv_style_t * label_style = lv_obj_get_style(ext->label);
lv_obj_t * scrl = lv_page_get_scrl(ddlist);
lv_coord_t h = lv_obj_get_height(ddlist);
lv_coord_t line_y1 = ext->sel_opt_id * (font_h + label_style->text.line_space) + ext->label->coords.y1 - scrl->coords.y1;
lv_obj_set_y(scrl, - line_y1 + (h - font_h) / 2);
}
#endif

View File

@@ -0,0 +1,286 @@
/*
* Copyright (c) 2019 CTCaer
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file lv_ddlist.h
*
*/
#ifndef LV_DDLIST_H
#define LV_DDLIST_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#ifdef LV_CONF_INCLUDE_SIMPLE
#include "lv_conf.h"
#else
#include "../../lv_conf.h"
#endif
#if USE_LV_DDLIST != 0
/*Testing of dependencies*/
#if USE_LV_PAGE == 0
#error "lv_ddlist: lv_page is required. Enable it in lv_conf.h (USE_LV_PAGE 1) "
#endif
#if USE_LV_LABEL == 0
#error "lv_ddlist: lv_label is required. Enable it in lv_conf.h (USE_LV_LABEL 1) "
#endif
#include "../lv_core/lv_obj.h"
#include "../lv_objx/lv_page.h"
#include "../lv_objx/lv_label.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/*Data of drop down list*/
typedef struct
{
lv_page_ext_t page; /*Ext. of ancestor*/
/*New data for this type */
lv_obj_t *label; /*Label for the options*/
lv_style_t * sel_style; /*Style of the selected option*/
lv_action_t action; /*Pointer to function to call when an option is selected*/
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*/
uint16_t anim_time; /*Open/Close animation time [ms]*/
uint8_t opened :1; /*1: The list is opened (handled by the library)*/
uint8_t draw_arrow :1; /*1: Draw arrow*/
uint8_t direction_up : 1; /*1: Open direction*/
lv_coord_t fix_height; /*Height of the ddlist when opened. (0: auto-size)*/
} lv_ddlist_ext_t;
enum {
LV_DDLIST_STYLE_BG,
LV_DDLIST_STYLE_BGO,
LV_DDLIST_STYLE_PR,
LV_DDLIST_STYLE_SEL,
LV_DDLIST_STYLE_SB,
};
typedef uint8_t lv_ddlist_style_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a drop down list objects
* @param par pointer to an object, it will be the parent of the new drop down list
* @param copy pointer to a drop down list object, if not NULL then the new object will be copied from it
* @return pointer to the created drop down list
*/
lv_obj_t * lv_ddlist_create(lv_obj_t * par, const lv_obj_t * copy);
/*=====================
* Setter functions
*====================*/
/**
* Set arrow draw in a drop down list
* @param ddlist pointer to drop down list object
* @param en enable/disable a arrow draw. E.g. "true" for draw.
*/
void lv_ddlist_set_draw_arrow(lv_obj_t * ddlist, bool en);
/**
* Set the options in a drop down list from a string
* @param ddlist pointer to drop down list object
* @param options a string with '\n' separated options. E.g. "One\nTwo\nThree"
*/
void lv_ddlist_set_options(lv_obj_t * ddlist, const char * options);
/**
* Set the selected option
* @param ddlist pointer to drop down list object
* @param sel_opt id of the selected option (0 ... number of option - 1);
*/
void lv_ddlist_set_selected(lv_obj_t * ddlist, uint16_t sel_opt);
/**
* Set a function to call when a new option is chosen
* @param ddlist pointer to a drop down list
* @param action pointer to a call back function
*/
void lv_ddlist_set_action(lv_obj_t * ddlist, lv_action_t action);
/**
* Set the fix height for the drop down list
* If 0 then the opened ddlist will be auto. sized else the set height will be applied.
* @param ddlist pointer to a drop down list
* @param h the height when the list is opened (0: auto size)
*/
void lv_ddlist_set_fix_height(lv_obj_t * ddlist, lv_coord_t h);
/**
* Enable or disable the horizontal fit to the content
* @param ddlist pointer to a drop down list
* @param en true: enable auto fit; false: disable auto fit
*/
void lv_ddlist_set_hor_fit(lv_obj_t * ddlist, bool en);
/**
* Set the scroll bar mode of a drop down list
* @param ddlist pointer to a drop down list object
* @param sb_mode the new mode from 'lv_page_sb_mode_t' enum
*/
static inline void lv_ddlist_set_sb_mode(lv_obj_t * ddlist, lv_sb_mode_t mode)
{
lv_page_set_sb_mode(ddlist, mode);
}
/**
* Set the open/close animation time.
* @param ddlist pointer to a drop down list
* @param anim_time: open/close animation time [ms]
*/
void lv_ddlist_set_anim_time(lv_obj_t * ddlist, uint16_t anim_time);
/**
* Set a style of a drop down list
* @param ddlist pointer to a drop down list object
* @param type which style should be set
* @param style pointer to a style
* */
void lv_ddlist_set_style(lv_obj_t *ddlist, lv_ddlist_style_t type, lv_style_t *style);
/**
* Set the alignment of the labels in a drop down list
* @param ddlist pointer to a drop down list object
* @param align alignment of labels
*/
void lv_ddlist_set_align(lv_obj_t *ddlist, lv_label_align_t align);
void lv_ddlist_set_direction_up(lv_obj_t *ddlist, bool enable);
/*=====================
* Getter functions
*====================*/
/**
* Get arrow draw in a drop down list
* @param ddlist pointer to drop down list object
*/
bool lv_ddlist_get_draw_arrow(lv_obj_t * ddlist);
/**
* Get the options of a drop down list
* @param ddlist pointer to drop down list object
* @return the options separated by '\n'-s (E.g. "Option1\nOption2\nOption3")
*/
const char * lv_ddlist_get_options(const lv_obj_t * ddlist);
/**
* Get the selected option
* @param ddlist pointer to drop down list object
* @return id of the selected option (0 ... number of option - 1);
*/
uint16_t lv_ddlist_get_selected(const lv_obj_t * ddlist);
/**
* Get the current selected option as a string
* @param ddlist pointer to ddlist object
* @param buf pointer to an array to store the string
*/
void lv_ddlist_get_selected_str(const lv_obj_t * ddlist, char * buf);
/**
* Get the "option selected" callback function
* @param ddlist pointer to a drop down list
* @return pointer to the call back function
*/
lv_action_t lv_ddlist_get_action(const lv_obj_t * ddlist);
/**
* Get the fix height value.
* @param ddlist pointer to a drop down list object
* @return the height if the ddlist is opened (0: auto size)
*/
lv_coord_t lv_ddlist_get_fix_height(const lv_obj_t * ddlist);
/**
* Get the scroll bar mode of a drop down list
* @param ddlist pointer to a drop down list object
* @return scrollbar mode from 'lv_page_sb_mode_t' enum
*/
static inline lv_sb_mode_t lv_ddlist_get_sb_mode(const lv_obj_t * ddlist)
{
return lv_page_get_sb_mode(ddlist);
}
/**
* Get the open/close animation time.
* @param ddlist pointer to a drop down list
* @return open/close animation time [ms]
*/
uint16_t lv_ddlist_get_anim_time(const lv_obj_t * ddlist);
/**
* Get a style of a drop down list
* @param ddlist pointer to a drop down list object
* @param type which style should be get
* @return style pointer to a style
*/
lv_style_t * lv_ddlist_get_style(const lv_obj_t *ddlist, lv_ddlist_style_t type);
/**
* Get the alignment of the labels in a drop down list
* @param ddlist pointer to a drop down list object
* @return alignment of labels
*/
lv_label_align_t lv_ddlist_get_align(const lv_obj_t *ddlist);
/*=====================
* Other functions
*====================*/
/**
* Open the drop down list with or without animation
* @param ddlist pointer to drop down list object
* @param anim_en true: use animation; false: not use animations
*/
void lv_ddlist_open(lv_obj_t * ddlist, bool anim_en);
/**
* Close (Collapse) the drop down list
* @param ddlist pointer to drop down list object
* @param anim_en true: use animation; false: not use animations
*/
void lv_ddlist_close(lv_obj_t * ddlist, bool anim_en);
/**********************
* MACROS
**********************/
#endif /*USE_LV_DDLIST*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_DDLIST_H*/

View File

@@ -0,0 +1,466 @@
/**
* @file lv_gauge.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_gauge.h"
#if USE_LV_GAUGE != 0
#include "../lv_draw/lv_draw.h"
#include "../lv_themes/lv_theme.h"
#include "../lv_misc/lv_txt.h"
#include "../lv_misc/lv_math.h"
#include <stdio.h>
#include <string.h>
/*********************
* DEFINES
*********************/
#define LV_GAUGE_DEF_NEEDLE_COLOR LV_COLOR_RED
#define LV_GAUGE_DEF_LABEL_COUNT 6
#define LV_GAUGE_DEF_LINE_COUNT 21 /*Should be: ((label_cnt - 1) * internal_lines) + 1*/
#define LV_GAUGE_DEF_ANGLE 220
#define LV_GAUGE_INTERPOLATE_SHIFT 5 /*Interpolate the needle drawing between to degrees*/
#define LV_GAUGE_INTERPOLATE_MASK 0x1F
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static bool lv_gauge_design(lv_obj_t * gauge, const lv_area_t * mask, lv_design_mode_t mode);
static lv_res_t lv_gauge_signal(lv_obj_t * gauge, lv_signal_t sign, void * param);
static void lv_gauge_draw_scale(lv_obj_t * gauge, const lv_area_t * mask);
static void lv_gauge_draw_needle(lv_obj_t * gauge, const lv_area_t * mask);
/**********************
* STATIC VARIABLES
**********************/
static lv_design_func_t ancestor_design;
static lv_signal_func_t ancestor_signal;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Create a gauge objects
* @param par pointer to an object, it will be the parent of the new gauge
* @param copy pointer to a gauge object, if not NULL then the new object will be copied from it
* @return pointer to the created gauge
*/
lv_obj_t * lv_gauge_create(lv_obj_t * par, const lv_obj_t * copy)
{
LV_LOG_TRACE("gauge create started");
/*Create the ancestor gauge*/
lv_obj_t * new_gauge = lv_lmeter_create(par, copy);
lv_mem_assert(new_gauge);
if(new_gauge == NULL) return NULL;
/*Allocate the gauge type specific extended data*/
lv_gauge_ext_t * ext = lv_obj_allocate_ext_attr(new_gauge, sizeof(lv_gauge_ext_t));
lv_mem_assert(ext);
if(ext == NULL) return NULL;
/*Initialize the allocated 'ext' */
ext->needle_count = 0;
ext->values = NULL;
ext->needle_colors = NULL;
ext->label_count = LV_GAUGE_DEF_LABEL_COUNT;
if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_gauge);
if(ancestor_design == NULL) ancestor_design = lv_obj_get_design_func(new_gauge);
/*The signal and design functions are not copied so set them here*/
lv_obj_set_signal_func(new_gauge, lv_gauge_signal);
lv_obj_set_design_func(new_gauge, lv_gauge_design);
/*Init the new gauge gauge*/
if(copy == NULL) {
lv_gauge_set_scale(new_gauge, LV_GAUGE_DEF_ANGLE, LV_GAUGE_DEF_LINE_COUNT, LV_GAUGE_DEF_LABEL_COUNT);
lv_gauge_set_needle_count(new_gauge, 1, NULL);
lv_gauge_set_critical_value(new_gauge, 80);
lv_obj_set_size(new_gauge, 2 * LV_DPI, 2 * LV_DPI);
/*Set the default styles*/
lv_theme_t * th = lv_theme_get_current();
if(th) {
lv_gauge_set_style(new_gauge, th->gauge);
} else {
lv_gauge_set_style(new_gauge, &lv_style_pretty_color);
}
}
/*Copy an existing gauge*/
else {
lv_gauge_ext_t * copy_ext = lv_obj_get_ext_attr(copy);
lv_gauge_set_needle_count(new_gauge, copy_ext->needle_count, copy_ext->needle_colors);
uint8_t i;
for(i = 0; i < ext->needle_count; i++) {
ext->values[i] = copy_ext->values[i];
}
ext->label_count = copy_ext->label_count;
/*Refresh the style with new signal function*/
lv_obj_refresh_style(new_gauge);
}
LV_LOG_INFO("gauge created");
return new_gauge;
}
/*=====================
* Setter functions
*====================*/
/**
* Set the number of needles
* @param gauge pointer to gauge object
* @param needle_cnt new count of needles
* @param colors an array of colors for needles (with 'num' elements)
*/
void lv_gauge_set_needle_count(lv_obj_t * gauge, uint8_t needle_cnt, const lv_color_t * colors)
{
lv_gauge_ext_t * ext = lv_obj_get_ext_attr(gauge);
if(ext->needle_count != needle_cnt) {
if(ext->values != NULL) {
lv_mem_free(ext->values);
ext->values = NULL;
}
ext->values = lv_mem_realloc(ext->values, needle_cnt * sizeof(int16_t));
lv_mem_assert(ext->values);
if(ext->values == NULL) return;
int16_t min = lv_gauge_get_min_value(gauge);
uint8_t n;
for(n = ext->needle_count; n < needle_cnt; n++) {
ext->values[n] = min;
}
ext->needle_count = needle_cnt;
}
ext->needle_colors = colors;
lv_obj_invalidate(gauge);
}
/**
* Set the value of a needle
* @param gauge pointer to a gauge
* @param needle_id the id of the needle
* @param value the new value
*/
void lv_gauge_set_value(lv_obj_t * gauge, uint8_t needle_id, int16_t value)
{
lv_gauge_ext_t * ext = lv_obj_get_ext_attr(gauge);
if(needle_id >= ext->needle_count) return;
if(ext->values[needle_id] == value) return;
int16_t min = lv_gauge_get_min_value(gauge);
int16_t max = lv_gauge_get_max_value(gauge);
if(value > max) value = max;
else if(value < min) value = min;
ext->values[needle_id] = value;
lv_obj_invalidate(gauge);
}
/**
* Set the scale settings of a gauge
* @param gauge pointer to a gauge object
* @param angle angle of the scale (0..360)
* @param line_cnt count of scale lines.
* The get a given "subdivision" lines between label, `line_cnt` = (sub_div + 1) * (label_cnt - 1) + 1
* @param label_cnt count of scale labels.
*/
void lv_gauge_set_scale(lv_obj_t * gauge, uint16_t angle, uint8_t line_cnt, uint8_t label_cnt)
{
/*TODO v6.0: change `line_cnt` to `subdiv_cnt`*/
lv_lmeter_set_scale(gauge, angle, line_cnt);
lv_gauge_ext_t * ext = lv_obj_get_ext_attr(gauge);
ext->label_count = label_cnt;
lv_obj_invalidate(gauge);
}
/*=====================
* Getter functions
*====================*/
/**
* Get the value of a needle
* @param gauge pointer to gauge object
* @param needle the id of the needle
* @return the value of the needle [min,max]
*/
int16_t lv_gauge_get_value(const lv_obj_t * gauge, uint8_t needle)
{
lv_gauge_ext_t * ext = lv_obj_get_ext_attr(gauge);
int16_t min = lv_gauge_get_min_value(gauge);
if(needle >= ext->needle_count) return min;
return ext->values[needle];
}
/**
* Get the count of needles on a gauge
* @param gauge pointer to gauge
* @return count of needles
*/
uint8_t lv_gauge_get_needle_count(const lv_obj_t * gauge)
{
lv_gauge_ext_t * ext = lv_obj_get_ext_attr(gauge);
return ext->needle_count;
}
/**
* Set the number of labels (and the thicker lines too)
* @param gauge pointer to a gauge object
* @return count of labels
*/
uint8_t lv_gauge_get_label_count(const lv_obj_t * gauge)
{
lv_gauge_ext_t * ext = lv_obj_get_ext_attr(gauge);
return ext->label_count;
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Handle the drawing related tasks of the gauges
* @param gauge pointer to an object
* @param mask the object will be drawn only in this area
* @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area
* (return 'true' if yes)
* LV_DESIGN_DRAW: draw the object (always return 'true')
* LV_DESIGN_DRAW_POST: drawing after every children are drawn
* @param return true/false, depends on 'mode'
*/
static bool lv_gauge_design(lv_obj_t * gauge, const lv_area_t * mask, lv_design_mode_t mode)
{
/*Return false if the object is not covers the mask_p area*/
if(mode == LV_DESIGN_COVER_CHK) {
return false;
}
/*Draw the object*/
else if(mode == LV_DESIGN_DRAW_MAIN) {
/* Store the real pointer because of 'lv_group'
* If the object is in focus 'lv_obj_get_style()' will give a pointer to tmp style
* and to the real object style. It is important because of style change tricks below*/
lv_style_t * style_ori_p = gauge->style_p;
lv_style_t * style = lv_obj_get_style(gauge);
lv_gauge_ext_t * ext = lv_obj_get_ext_attr(gauge);
lv_gauge_draw_scale(gauge, mask);
/*Draw the ancestor line meter with max value to show the rainbow like line colors*/
uint16_t line_cnt_tmp = ext->lmeter.line_cnt;
ancestor_design(gauge, mask, mode); /*To draw lines*/
/*Temporally modify the line meter to draw thicker and longer lines where labels are*/
lv_style_t style_tmp;
lv_style_copy(&style_tmp, style);
ext->lmeter.line_cnt = ext->label_count; /*Only to labels*/
style_tmp.line.width = style_tmp.line.width * 2; /*Ticker lines*/
style_tmp.body.padding.hor = style_tmp.body.padding.hor * 2; /*Longer lines*/
gauge->style_p = &style_tmp;
ancestor_design(gauge, mask, mode); /*To draw lines*/
ext->lmeter.line_cnt = line_cnt_tmp; /*Restore the parameters*/
gauge->style_p = style_ori_p; /*Restore the ORIGINAL style pointer*/
lv_gauge_draw_needle(gauge, mask);
}
/*Post draw when the children are drawn*/
else if(mode == LV_DESIGN_DRAW_POST) {
ancestor_design(gauge, mask, mode);
}
return true;
}
/**
* Signal function of the gauge
* @param gauge pointer to a gauge object
* @param sign a signal type from lv_signal_t enum
* @param param pointer to a signal specific variable
* @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
*/
static lv_res_t lv_gauge_signal(lv_obj_t * gauge, lv_signal_t sign, void * param)
{
lv_res_t res;
/* Include the ancient signal function */
res = ancestor_signal(gauge, sign, param);
if(res != LV_RES_OK) return res;
lv_gauge_ext_t * ext = lv_obj_get_ext_attr(gauge);
if(sign == LV_SIGNAL_CLEANUP) {
lv_mem_free(ext->values);
ext->values = NULL;
} else if(sign == LV_SIGNAL_GET_TYPE) {
lv_obj_type_t * buf = param;
uint8_t i;
for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/
if(buf->type[i] == NULL) break;
}
buf->type[i] = "lv_gauge";
}
return res;
}
/**
* Draw the scale on a gauge
* @param gauge pointer to gauge object
* @param mask mask of drawing
*/
static void lv_gauge_draw_scale(lv_obj_t * gauge, const lv_area_t * mask)
{
char scale_txt[16];
lv_gauge_ext_t * ext = lv_obj_get_ext_attr(gauge);
lv_style_t * style = lv_obj_get_style(gauge);
lv_opa_t opa_scale = lv_obj_get_opa_scale(gauge);
lv_coord_t r = lv_obj_get_width(gauge) / 2 - (3 * style->body.padding.hor) - style->body.padding.inner;
lv_coord_t x_ofs = lv_obj_get_width(gauge) / 2 + gauge->coords.x1;
lv_coord_t y_ofs = lv_obj_get_height(gauge) / 2 + gauge->coords.y1;
int16_t scale_angle = lv_lmeter_get_scale_angle(gauge);
uint16_t label_num = ext->label_count;
int16_t angle_ofs = 90 + (360 - scale_angle) / 2;
int16_t min = lv_gauge_get_min_value(gauge);
int16_t max = lv_gauge_get_max_value(gauge);
uint8_t i;
for(i = 0; i < label_num; i++) {
/*Calculate the position a scale label*/
int16_t angle = (i * scale_angle) / (label_num - 1) + angle_ofs;
lv_coord_t y = (int32_t)((int32_t)lv_trigo_sin(angle) * r) / LV_TRIGO_SIN_MAX;
y += y_ofs;
lv_coord_t x = (int32_t)((int32_t)lv_trigo_sin(angle + 90) * r) / LV_TRIGO_SIN_MAX;
x += x_ofs;
int16_t scale_act = (int32_t)((int32_t)(max - min) * i) / (label_num - 1);
scale_act += min;
lv_math_num_to_str(scale_act, scale_txt);
lv_area_t label_cord;
lv_point_t label_size;
lv_txt_get_size(&label_size, scale_txt, style->text.font,
style->text.letter_space, style->text.line_space, LV_COORD_MAX, LV_TXT_FLAG_NONE);
/*Draw the label*/
label_cord.x1 = x - label_size.x / 2;
label_cord.y1 = y - label_size.y / 2;
label_cord.x2 = label_cord.x1 + label_size.x;
label_cord.y2 = label_cord.y1 + label_size.y;
lv_draw_label(&label_cord, mask, style, opa_scale, scale_txt, LV_TXT_FLAG_NONE, NULL);
}
}
/**
* Draw the needles of a gauge
* @param gauge pointer to gauge object
* @param mask mask of drawing
*/
static void lv_gauge_draw_needle(lv_obj_t * gauge, const lv_area_t * mask)
{
lv_style_t style_needle;
lv_gauge_ext_t * ext = lv_obj_get_ext_attr(gauge);
lv_style_t * style = lv_gauge_get_style(gauge);
lv_opa_t opa_scale = lv_obj_get_opa_scale(gauge);
lv_coord_t r = lv_obj_get_width(gauge) / 2 - style->body.padding.hor;
lv_coord_t x_ofs = lv_obj_get_width(gauge) / 2 + gauge->coords.x1;
lv_coord_t y_ofs = lv_obj_get_height(gauge) / 2 + gauge->coords.y1;
uint16_t angle = lv_lmeter_get_scale_angle(gauge);
int16_t angle_ofs = 90 + (360 - angle) / 2;
int16_t min = lv_gauge_get_min_value(gauge);
int16_t max = lv_gauge_get_max_value(gauge);
lv_point_t p_mid;
lv_point_t p_end;
lv_point_t p_end_low;
lv_point_t p_end_high;
uint8_t i;
lv_style_copy(&style_needle, style);
p_mid.x = x_ofs;
p_mid.y = y_ofs;
for(i = 0; i < ext->needle_count; i++) {
/*Calculate the end point of a needle*/
int16_t needle_angle = (ext->values[i] - min) * angle * (1 << LV_GAUGE_INTERPOLATE_SHIFT) / (max - min); //+ angle_ofs;
int16_t needle_angle_low = (needle_angle >> LV_GAUGE_INTERPOLATE_SHIFT) + angle_ofs;
int16_t needle_angle_high = needle_angle_low + 1;
p_end_low.y = (lv_trigo_sin(needle_angle_low) * r) / LV_TRIGO_SIN_MAX + y_ofs;
p_end_low.x = (lv_trigo_sin(needle_angle_low + 90) * r) / LV_TRIGO_SIN_MAX + x_ofs;
p_end_high.y = (lv_trigo_sin(needle_angle_high) * r) / LV_TRIGO_SIN_MAX + y_ofs;
p_end_high.x = (lv_trigo_sin(needle_angle_high + 90) * r) / LV_TRIGO_SIN_MAX + x_ofs;
uint16_t rem = needle_angle & ((1 << LV_GAUGE_INTERPOLATE_SHIFT) - 1);
int16_t x_mod = ((LV_MATH_ABS(p_end_high.x - p_end_low.x)) * rem) >> LV_GAUGE_INTERPOLATE_SHIFT;
int16_t y_mod = ((LV_MATH_ABS(p_end_high.y - p_end_low.y)) * rem) >> LV_GAUGE_INTERPOLATE_SHIFT;
if(p_end_high.x < p_end_low.x) x_mod = -x_mod;
if(p_end_high.y < p_end_low.y) y_mod = -y_mod;
p_end.x = p_end_low.x + x_mod;
p_end.y = p_end_low.y + y_mod;
/*Draw the needle with the corresponding color*/
if(ext->needle_colors == NULL) style_needle.line.color = LV_GAUGE_DEF_NEEDLE_COLOR;
else style_needle.line.color = ext->needle_colors[i];
lv_draw_line(&p_mid, &p_end, mask, &style_needle, opa_scale);
}
/*Draw the needle middle area*/
lv_style_t style_neddle_mid;
lv_style_copy(&style_neddle_mid, &lv_style_plain);
style_neddle_mid.body.main_color = style->body.border.color;
style_neddle_mid.body.grad_color = style->body.border.color;
style_neddle_mid.body.radius = LV_RADIUS_CIRCLE;
lv_area_t nm_cord;
nm_cord.x1 = x_ofs - style->body.padding.ver;
nm_cord.y1 = y_ofs - style->body.padding.ver;
nm_cord.x2 = x_ofs + style->body.padding.ver;
nm_cord.y2 = y_ofs + style->body.padding.ver;
lv_draw_rect(&nm_cord, mask, &style_neddle_mid, lv_obj_get_opa_scale(gauge));
}
#endif

View File

@@ -0,0 +1,222 @@
/**
* @file lv_gauge.h
*
*/
#ifndef LV_GAUGE_H
#define LV_GAUGE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#ifdef LV_CONF_INCLUDE_SIMPLE
#include "lv_conf.h"
#else
#include "../../lv_conf.h"
#endif
#if USE_LV_GAUGE != 0
/*Testing of dependencies*/
#if USE_LV_LMETER == 0
#error "lv_gauge: lv_lmeter is required. Enable it in lv_conf.h (USE_LV_LMETER 1) "
#endif
#include "../lv_core/lv_obj.h"
#include "lv_lmeter.h"
#include "lv_label.h"
#include "lv_line.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/*Data of gauge*/
typedef struct
{
lv_lmeter_ext_t lmeter; /*Ext. of ancestor*/
/*New data for this type */
int16_t * values; /*Array of the set values (for needles) */
const lv_color_t * needle_colors; /*Color of the needles (lv_color_t my_colors[needle_num])*/
uint8_t needle_count; /*Number of needles*/
uint8_t label_count; /*Number of labels on the scale*/
} lv_gauge_ext_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a gauge objects
* @param par pointer to an object, it will be the parent of the new gauge
* @param copy pointer to a gauge object, if not NULL then the new object will be copied from it
* @return pointer to the created gauge
*/
lv_obj_t * lv_gauge_create(lv_obj_t * par, const lv_obj_t * copy);
/*=====================
* Setter functions
*====================*/
/**
* Set the number of needles
* @param gauge pointer to gauge object
* @param needle_cnt new count of needles
* @param colors an array of colors for needles (with 'num' elements)
*/
void lv_gauge_set_needle_count(lv_obj_t * gauge, uint8_t needle_cnt, const lv_color_t * colors);
/**
* Set the value of a needle
* @param gauge pointer to a gauge
* @param needle_id the id of the needle
* @param value the new value
*/
void lv_gauge_set_value(lv_obj_t * gauge, uint8_t needle_id, int16_t value);
/**
* Set minimum and the maximum values of a gauge
* @param gauge pointer to he gauge object
* @param min minimum value
* @param max maximum value
*/
static inline void lv_gauge_set_range(lv_obj_t *gauge, int16_t min, int16_t max)
{
lv_lmeter_set_range(gauge, min, max);
}
/**
* Set a critical value on the scale. After this value 'line.color' scale lines will be drawn
* @param gauge pointer to a gauge object
* @param value the critical value
*/
static inline void lv_gauge_set_critical_value(lv_obj_t * gauge, int16_t value)
{
lv_lmeter_set_value(gauge, value);
}
/**
* Set the scale settings of a gauge
* @param gauge pointer to a gauge object
* @param angle angle of the scale (0..360)
* @param line_cnt count of scale lines.
* The get a given "subdivision" lines between label, `line_cnt` = (sub_div + 1) * (label_cnt - 1) + 1
* @param label_cnt count of scale labels.
*/
void lv_gauge_set_scale(lv_obj_t * gauge, uint16_t angle, uint8_t line_cnt, uint8_t label_cnt);
/**
* Set the styles of a gauge
* @param gauge pointer to a gauge object
* @param bg set the style of the gauge
* */
static inline void lv_gauge_set_style(lv_obj_t *gauge, lv_style_t *bg)
{
lv_obj_set_style(gauge, bg);
}
/*=====================
* Getter functions
*====================*/
/**
* Get the value of a needle
* @param gauge pointer to gauge object
* @param needle the id of the needle
* @return the value of the needle [min,max]
*/
int16_t lv_gauge_get_value(const lv_obj_t * gauge, uint8_t needle);
/**
* Get the count of needles on a gauge
* @param gauge pointer to gauge
* @return count of needles
*/
uint8_t lv_gauge_get_needle_count(const lv_obj_t * gauge);
/**
* Get the minimum value of a gauge
* @param gauge pointer to a gauge object
* @return the minimum value of the gauge
*/
static inline int16_t lv_gauge_get_min_value(const lv_obj_t * lmeter)
{
return lv_lmeter_get_min_value(lmeter);
}
/**
* Get the maximum value of a gauge
* @param gauge pointer to a gauge object
* @return the maximum value of the gauge
*/
static inline int16_t lv_gauge_get_max_value(const lv_obj_t * lmeter)
{
return lv_lmeter_get_max_value(lmeter);
}
/**
* Get a critical value on the scale.
* @param gauge pointer to a gauge object
* @return the critical value
*/
static inline int16_t lv_gauge_get_critical_value(const lv_obj_t * gauge)
{
return lv_lmeter_get_value(gauge);
}
/**
* Set the number of labels (and the thicker lines too)
* @param gauge pointer to a gauge object
* @return count of labels
*/
uint8_t lv_gauge_get_label_count(const lv_obj_t * gauge);
/**
* Get the scale number of a gauge
* @param gauge pointer to a gauge object
* @return number of the scale units
*/
static inline uint8_t lv_gauge_get_line_count(const lv_obj_t * gauge)
{
return lv_lmeter_get_line_count(gauge);
}
/**
* Get the scale angle of a gauge
* @param gauge pointer to a gauge object
* @return angle of the scale
*/
static inline uint16_t lv_gauge_get_scale_angle(const lv_obj_t * gauge)
{
return lv_lmeter_get_scale_angle(gauge);
}
/**
* Get the style of a gauge
* @param gauge pointer to a gauge object
* @return pointer to the gauge's style
*/
static inline lv_style_t * lv_gauge_get_style(const lv_obj_t *gauge)
{
return lv_obj_get_style(gauge);
}
/**********************
* MACROS
**********************/
#endif /*USE_LV_GAUGE*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_GAUGE_H*/

View File

@@ -0,0 +1,408 @@
/**
* @file lv_img.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_img.h"
#if USE_LV_IMG != 0
/*Testing of dependencies*/
#if USE_LV_LABEL == 0
#error "lv_img: lv_label is required. Enable it in lv_conf.h (USE_LV_LABEL 1) "
#endif
#include "../lv_core/lv_lang.h"
#include "../lv_themes/lv_theme.h"
#include "../lv_misc/lv_fs.h"
#include "../lv_misc/lv_ufs.h"
#include "../lv_misc/lv_txt.h"
#include "../lv_misc/lv_log.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static bool lv_img_design(lv_obj_t * img, const lv_area_t * mask, lv_design_mode_t mode);
static lv_res_t lv_img_signal(lv_obj_t * img, lv_signal_t sign, void * param);
/**********************
* STATIC VARIABLES
**********************/
static lv_signal_func_t ancestor_signal;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Create an image objects
* @param par pointer to an object, it will be the parent of the new button
* @param copy pointer to a image object, if not NULL then the new object will be copied from it
* @return pointer to the created image
*/
lv_obj_t * lv_img_create(lv_obj_t * par, const lv_obj_t * copy)
{
LV_LOG_TRACE("image create started");
lv_obj_t * new_img = NULL;
/*Create a basic object*/
new_img = lv_obj_create(par, copy);
lv_mem_assert(new_img);
if(new_img == NULL) return NULL;
if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_img);
/*Extend the basic object to image object*/
lv_img_ext_t * ext = lv_obj_allocate_ext_attr(new_img, sizeof(lv_img_ext_t));
lv_mem_assert(ext);
if(ext == NULL) return NULL;
ext->src = NULL;
ext->src_type = LV_IMG_SRC_UNKNOWN;
ext->cf = LV_IMG_CF_UNKOWN;
ext->w = lv_obj_get_width(new_img);
ext->h = lv_obj_get_height(new_img);
ext->auto_size = 1;
#if USE_LV_MULTI_LANG
ext->lang_txt_id = LV_LANG_TXT_ID_NONE;
#endif
/*Init the new object*/
lv_obj_set_signal_func(new_img, lv_img_signal);
lv_obj_set_design_func(new_img, lv_img_design);
if(copy == NULL) {
lv_obj_set_click(new_img, false);
/* Enable auto size for non screens
* because image screens are wallpapers
* and must be screen sized*/
if(par != NULL) {
ext->auto_size = 1;
lv_obj_set_style(new_img, NULL); /*Inherit the style by default*/
} else {
ext->auto_size = 0;
lv_obj_set_style(new_img, &lv_style_plain); /*Set a style for screens*/
}
} else {
lv_img_ext_t * copy_ext = lv_obj_get_ext_attr(copy);
ext->auto_size = copy_ext->auto_size;
lv_img_set_src(new_img, copy_ext->src);
/*Refresh the style with new signal function*/
lv_obj_refresh_style(new_img);
}
LV_LOG_INFO("image created");
return new_img;
}
/*=====================
* Setter functions
*====================*/
/**
* Set the pixel map to display by the image
* @param img pointer to an image object
* @param data the image data
*/
void lv_img_set_src(lv_obj_t * img, const void * src_img)
{
lv_img_src_t src_type = lv_img_src_get_type(src_img);
lv_img_ext_t * ext = lv_obj_get_ext_attr(img);
#if 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(ext->src_type == LV_IMG_SRC_SYMBOL || ext->src_type == LV_IMG_SRC_FILE) {
lv_mem_free(ext->src);
}
ext->src = NULL;
ext->src_type = LV_IMG_SRC_UNKNOWN;
return;
}
lv_img_header_t header;
lv_img_dsc_get_info(src_img, &header);
/*Save the source*/
if(src_type == LV_IMG_SRC_VARIABLE) {
LV_LOG_INFO("lv_img_set_src: `LV_IMG_SRC_VARIABLE` type found");
/*If memory was allocated because of the previous `src_type` then free it*/
if(ext->src_type == LV_IMG_SRC_FILE || ext->src_type == LV_IMG_SRC_SYMBOL) {
lv_mem_free(ext->src);
}
ext->src = src_img;
} 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(ext->src != src_img) {
/*If memory was allocated because of the previous `src_type` then free it*/
if(ext->src_type == LV_IMG_SRC_FILE || ext->src_type == LV_IMG_SRC_SYMBOL) {
lv_mem_free(ext->src);
}
char * new_str = lv_mem_alloc(strlen(src_img) + 1);
lv_mem_assert(new_str);
if(new_str == NULL) return;
strcpy(new_str, src_img);
ext->src = new_str;
}
}
if(src_type == LV_IMG_SRC_SYMBOL) {
/*`lv_img_dsc_get_info` couldn't set the with and height of a font so set it here*/
lv_style_t * style = lv_img_get_style(img);
lv_point_t size;
lv_txt_get_size(&size, src_img, style->text.font, style->text.letter_space, style->text.line_space, LV_COORD_MAX, LV_TXT_FLAG_NONE);
header.w = size.x;
header.h = size.y;
}
ext->src_type = src_type;
ext->w = header.w;
ext->h = header.h;
ext->cf = header.cf;
if(lv_img_get_auto_size(img) != false) {
lv_obj_set_size(img, ext->w, ext->h);
}
lv_obj_invalidate(img);
}
#if USE_LV_MULTI_LANG
/**
* Set an ID which means a the same source but in different languages
* @param img pointer to an image object
* @param src_id ID of the source
*/
void lv_img_set_src_id(lv_obj_t * img, uint32_t src_id)
{
lv_img_ext_t * ext = lv_obj_get_ext_attr(img);
ext->lang_txt_id = src_id;
/*Apply the new language*/
img->signal_func(img, LV_SIGNAL_LANG_CHG, NULL);
}
#endif
/**
* Enable the auto size feature.
* If enabled the object size will be same as the picture size.
* @param img pointer to an image
* @param en true: auto size enable, false: auto size disable
*/
void lv_img_set_auto_size(lv_obj_t * img, bool en)
{
lv_img_ext_t * ext = lv_obj_get_ext_attr(img);
ext->auto_size = (en == false ? 0 : 1);
}
/*=====================
* Getter functions
*====================*/
/**
* Get the source of the image
* @param img pointer to an image object
* @return the image source (symbol, file name or C array)
*/
const void * lv_img_get_src(lv_obj_t * img)
{
lv_img_ext_t * ext = lv_obj_get_ext_attr(img);
return ext->src;
}
/**
* Get the name of the file set for an image
* @param img pointer to an image
* @return file name
*/
const char * lv_img_get_file_name(const lv_obj_t * img)
{
lv_img_ext_t * ext = lv_obj_get_ext_attr(img);
if(ext->src_type == LV_IMG_SRC_FILE) return ext->src;
else return "";
}
#if USE_LV_MULTI_LANG
/**
* Get the source ID of the image. (Used by the multi-language feature)
* @param img pointer to an image
* @return ID of the source
*/
uint16_t lv_img_get_src_id(lv_obj_t * img)
{
lv_img_ext_t * ext = lv_obj_get_ext_attr(img);
return ext->lang_txt_id;
}
#endif
/**
* Get the auto size enable attribute
* @param img pointer to an image
* @return true: auto size is enabled, false: auto size is disabled
*/
bool lv_img_get_auto_size(const lv_obj_t * img)
{
lv_img_ext_t * ext = lv_obj_get_ext_attr(img);
return ext->auto_size == 0 ? false : true;
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Handle the drawing related tasks of the images
* @param img pointer to an object
* @param mask the object will be drawn only in this area
* @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area
* (return 'true' if yes)
* LV_DESIGN_DRAW: draw the object (always return 'true')
* LV_DESIGN_DRAW_POST: drawing after every children are drawn
* @param return true/false, depends on 'mode'
*/
static bool lv_img_design(lv_obj_t * img, const lv_area_t * mask, lv_design_mode_t mode)
{
lv_style_t * style = lv_obj_get_style(img);
lv_img_ext_t * ext = lv_obj_get_ext_attr(img);
if(mode == LV_DESIGN_COVER_CHK) {
bool cover = false;
if(ext->src_type == LV_IMG_SRC_UNKNOWN || ext->src_type == LV_IMG_SRC_SYMBOL) return false;
if(ext->cf == LV_IMG_CF_TRUE_COLOR || ext->cf == LV_IMG_CF_RAW) cover = lv_area_is_in(mask, &img->coords);
return cover;
} else if(mode == LV_DESIGN_DRAW_MAIN) {
if(ext->h == 0 || ext->w == 0) return true;
lv_area_t coords;
lv_opa_t opa_scale = lv_obj_get_opa_scale(img);
lv_obj_get_coords(img, &coords);
if(ext->src_type == LV_IMG_SRC_FILE || ext->src_type == LV_IMG_SRC_VARIABLE) {
LV_LOG_TRACE("lv_img_design: start to draw image");
lv_area_t cords_tmp;
cords_tmp.y1 = coords.y1;
cords_tmp.y2 = coords.y1 + ext->h - 1;
for(; cords_tmp.y1 < coords.y2; cords_tmp.y1 += ext->h, cords_tmp.y2 += ext->h) {
cords_tmp.x1 = coords.x1;
cords_tmp.x2 = coords.x1 + ext->w - 1;
for(; cords_tmp.x1 < coords.x2; cords_tmp.x1 += ext->w, cords_tmp.x2 += ext->w) {
lv_draw_img(&cords_tmp, mask, ext->src, style, opa_scale);
}
}
} else if(ext->src_type == LV_IMG_SRC_SYMBOL) {
LV_LOG_TRACE("lv_img_design: start to draw symbol");
lv_style_t style_mod;
lv_style_copy(&style_mod, style);
style_mod.text.color = style->image.color;
lv_draw_label(&coords, mask, &style_mod, opa_scale, ext->src, LV_TXT_FLAG_NONE, NULL);
} else {
/*Trigger the error handler of image drawer*/
LV_LOG_WARN("lv_img_design: image source type is unknown");
lv_draw_img(&img->coords, mask, NULL, style, opa_scale);
}
}
return true;
}
/**
* Signal function of the image
* @param img pointer to an image object
* @param sign a signal type from lv_signal_t enum
* @param param pointer to a signal specific variable
* @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
*/
static lv_res_t lv_img_signal(lv_obj_t * img, lv_signal_t sign, void * param)
{
lv_res_t res;
/* Include the ancient signal function */
res = ancestor_signal(img, sign, param);
if(res != LV_RES_OK) return res;
lv_img_ext_t * ext = lv_obj_get_ext_attr(img);
if(sign == LV_SIGNAL_CLEANUP) {
if(ext->src_type == LV_IMG_SRC_FILE || ext->src_type == LV_IMG_SRC_SYMBOL) {
lv_mem_free(ext->src);
ext->src = NULL;
ext->src_type = LV_IMG_SRC_UNKNOWN;
}
} else if(sign == LV_SIGNAL_STYLE_CHG) {
/*Refresh the file name to refresh the symbol text size*/
if(ext->src_type == LV_IMG_SRC_SYMBOL) {
lv_img_set_src(img, ext->src);
}
} else if(sign == LV_SIGNAL_LANG_CHG) {
#if USE_LV_MULTI_LANG
if(ext->lang_txt_id != LV_LANG_TXT_ID_NONE) {
const char * lang_src = lv_lang_get_text(ext->lang_txt_id);
if(lang_src) {
lv_img_set_src(img, lang_src);
} else {
LV_LOG_WARN("lv_lang_get_text return NULL for an image's source");
}
}
#endif
} else if(sign == LV_SIGNAL_GET_TYPE) {
lv_obj_type_t * buf = param;
uint8_t i;
for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/
if(buf->type[i] == NULL) break;
}
buf->type[i] = "lv_img";
}
return res;
}
#endif

View File

@@ -0,0 +1,195 @@
/**
* @file lv_img.h
*
*/
#ifndef LV_IMG_H
#define LV_IMG_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#ifdef LV_CONF_INCLUDE_SIMPLE
#include "lv_conf.h"
#else
#include "../../lv_conf.h"
#endif
#if USE_LV_IMG != 0
#include "../lv_core/lv_obj.h"
#include "../lv_misc/lv_fs.h"
#include "../lv_misc/lv_symbol_def.h"
#include "lv_label.h"
#include "../lv_draw/lv_draw.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/*Data of image*/
typedef struct
{
/*No inherited ext. because inherited from the base object*/ /*Ext. of ancestor*/
/*New data for this type */
const void * src; /*Image source: Pointer to an array or a file or a symbol*/
lv_coord_t w; /*Width of the image (Handled by the library)*/
lv_coord_t h; /*Height of the image (Handled by the library)*/
#if USE_LV_MULTI_LANG
uint16_t lang_txt_id; /*The ID of the image to display. */
#endif
uint8_t src_type :2; /*See: lv_img_src_t*/
uint8_t auto_size :1; /*1: automatically set the object size to the image size*/
uint8_t cf :5; /*Color format from `lv_img_color_format_t`*/
} lv_img_ext_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create an image objects
* @param par pointer to an object, it will be the parent of the new button
* @param copy pointer to a image object, if not NULL then the new object will be copied from it
* @return pointer to the created image
*/
lv_obj_t * lv_img_create(lv_obj_t * par, const lv_obj_t * copy);
/*=====================
* Setter functions
*====================*/
/**
* Set the pixel map to display by the image
* @param img pointer to an image object
* @param data the image data
*/
void lv_img_set_src(lv_obj_t * img, const void * src_img);
#if USE_LV_MULTI_LANG
/**
* Set an ID which means a the same source but on different languages
* @param img pointer to an image object
* @param src_id ID of the source
*/
void lv_img_set_src_id(lv_obj_t * img, uint32_t txt_id);
#endif
/**
* Obsolete since v5.1. Just for compatibility with v5.0. Will be removed in v6.0.
* Use 'lv_img_set_src()' instead.
* @param img -
* @param fn -
*/
static inline void lv_img_set_file(lv_obj_t * img, const char * fn)
{
(void) img;
(void) fn;
}
/**
* Enable the auto size feature.
* If enabled the object size will be same as the picture size.
* @param img pointer to an image
* @param en true: auto size enable, false: auto size disable
*/
void lv_img_set_auto_size(lv_obj_t * img, bool autosize_en);
/**
* Set the style of an image
* @param img pointer to an image object
* @param style pointer to a style
*/
static inline void lv_img_set_style(lv_obj_t *img, lv_style_t *style)
{
lv_obj_set_style(img, style);
}
/**
* Obsolete since v5.1. Just for compatibility with v5.0. Will be removed in v6.0
* @param img -
* @param upscale -
*/
static inline void lv_img_set_upscale(lv_obj_t * img, bool upcale)
{
(void) img;
(void) upcale;
}
/*=====================
* Getter functions
*====================*/
/**
* Get the source of the image
* @param img pointer to an image object
* @return the image source (symbol, file name or C array)
*/
const void * lv_img_get_src(lv_obj_t * img);
/**
* Get the name of the file set for an image
* @param img pointer to an image
* @return file name
*/
const char * lv_img_get_file_name(const lv_obj_t * img);
#if USE_LV_MULTI_LANG
/**
* Get the source ID of the image. (Used by the multi-language feature)
* @param img pointer to an image
* @return ID of the source
*/
uint16_t lv_img_get_src_id(lv_obj_t * img);
#endif
/**
* Get the auto size enable attribute
* @param img pointer to an image
* @return true: auto size is enabled, false: auto size is disabled
*/
bool lv_img_get_auto_size(const lv_obj_t * img);
/**
* Get the style of an image object
* @param img pointer to an image object
* @return pointer to the image's style
*/
static inline lv_style_t* lv_img_get_style(const lv_obj_t *img)
{
return lv_obj_get_style(img);
}
/**
* Obsolete since v5.1. Just for compatibility with v5.0. Will be removed in v6.0
* @param img -
* @return false
*/
static inline bool lv_img_get_upscale(const lv_obj_t * img)
{
(void)img;
return false;
}
/**********************
* 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 /*USE_LV_IMG*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_IMG_H*/

View File

@@ -0,0 +1,391 @@
/**
* @file lv_imgbtn.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_imgbtn.h"
#if USE_LV_IMGBTN != 0
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static bool lv_imgbtn_design(lv_obj_t * imgbtn, const lv_area_t * mask, lv_design_mode_t mode);
static lv_res_t lv_imgbtn_signal(lv_obj_t * imgbtn, lv_signal_t sign, void * param);
static void refr_img(lv_obj_t * imgbtn);
/**********************
* STATIC VARIABLES
**********************/
static lv_signal_func_t ancestor_signal;
static lv_design_func_t ancestor_design;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Create a image button object
* @param par pointer to an object, it will be the parent of the new image button
* @param copy pointer to a image button object, if not NULL then the new object will be copied from it
* @return pointer to the created image button
*/
lv_obj_t * lv_imgbtn_create(lv_obj_t * par, const lv_obj_t * copy)
{
LV_LOG_TRACE("image button create started");
/*Create the ancestor of image button*/
lv_obj_t * new_imgbtn = lv_btn_create(par, copy);
lv_mem_assert(new_imgbtn);
if(new_imgbtn == NULL) return NULL;
/*Allocate the image button type specific extended data*/
lv_imgbtn_ext_t * ext = lv_obj_allocate_ext_attr(new_imgbtn, sizeof(lv_imgbtn_ext_t));
lv_mem_assert(ext);
if(ext == NULL) return NULL;
if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_imgbtn);
if(ancestor_design == NULL) ancestor_design = lv_obj_get_design_func(new_imgbtn);
/*Initialize the allocated 'ext' */
#if LV_IMGBTN_TILED == 0
memset(ext->img_src, 0, sizeof(ext->img_src));
#else
memset(ext->img_src_left, 0, sizeof(ext->img_src_left));
memset(ext->img_src_mid, 0, sizeof(ext->img_src_mid));
memset(ext->img_src_right, 0, sizeof(ext->img_src_right));
#endif
ext->act_cf = LV_IMG_CF_UNKOWN;
/*The signal and design functions are not copied so set them here*/
lv_obj_set_signal_func(new_imgbtn, lv_imgbtn_signal);
lv_obj_set_design_func(new_imgbtn, lv_imgbtn_design);
/*Init the new image button image button*/
if(copy == NULL) {
}
/*Copy an existing image button*/
else {
lv_imgbtn_ext_t * copy_ext = lv_obj_get_ext_attr(copy);
#if LV_IMGBTN_TILED == 0
memcpy(ext->img_src, copy_ext->img_src, sizeof(ext->img_src));
#else
memcpy(ext->img_src_left, copy_ext->img_src_left, sizeof(ext->img_src_left));
memcpy(ext->img_src_mid, copy_ext->img_src_mid, sizeof(ext->img_src_mid));
memcpy(ext->img_src_right, copy_ext->img_src_right, sizeof(ext->img_src_right));
#endif
/*Refresh the style with new signal function*/
lv_obj_refresh_style(new_imgbtn);
}
LV_LOG_INFO("image button created");
return new_imgbtn;
}
/*=====================
* Setter functions
*====================*/
#if LV_IMGBTN_TILED == 0
/**
* Set images for a state of the image button
* @param imgbtn pointer to an image button object
* @param state for which state set the new image (from `lv_btn_state_t`) `
* @param src pointer to an image source (a C array or path to a file)
*/
void lv_imgbtn_set_src(lv_obj_t * imgbtn, lv_btn_state_t state, const void * src)
{
lv_imgbtn_ext_t * ext = lv_obj_get_ext_attr(imgbtn);
ext->img_src[state] = src;
refr_img(imgbtn);
}
#else
/**
* Set images for a state of the image button
* @param imgbtn pointer to an image button object
* @param state for which state set the new image (from `lv_btn_state_t`) `
* @param src_left pointer to an image source for the left side of the button (a C array or path to a file)
* @param src_mid pointer to an image source for the middle of the button (ideally 1px wide) (a C array or path to a file)
* @param src_right pointer to an image source for the right side of the button (a C array or path to a file)
*/
void lv_imgbtn_set_src(lv_obj_t * imgbtn, lv_btn_state_t state, const void * src_left, const void * src_mid, const void * src_right)
{
lv_imgbtn_ext_t * ext = lv_obj_get_ext_attr(imgbtn);
ext->img_src_left[state] = src_left;
ext->img_src_mid[state] = src_mid;
ext->img_src_right[state] = src_right;
refr_img(imgbtn);
}
#endif
/**
* Set a style of a image button.
* @param imgbtn pointer to image button object
* @param type which style should be set
* @param style pointer to a style
*/
void lv_imgbtn_set_style(lv_obj_t * imgbtn, lv_imgbtn_style_t type, lv_style_t * style)
{
lv_btn_set_style(imgbtn, type, style);
}
/*=====================
* Getter functions
*====================*/
#if LV_IMGBTN_TILED == 0
/**
* Get the images in a given state
* @param imgbtn pointer to an image button object
* @param state the state where to get the image (from `lv_btn_state_t`) `
* @return pointer to an image source (a C array or path to a file)
*/
const void * lv_imgbtn_get_src(lv_obj_t * imgbtn, lv_btn_state_t state)
{
lv_imgbtn_ext_t * ext = lv_obj_get_ext_attr(imgbtn);
return ext->img_src[state];
}
#else
/**
* Get the left image in a given state
* @param imgbtn pointer to an image button object
* @param state the state where to get the image (from `lv_btn_state_t`) `
* @return pointer to the left image source (a C array or path to a file)
*/
const void * lv_imgbtn_get_src_left(lv_obj_t * imgbtn, lv_btn_state_t state)
{
lv_imgbtn_ext_t * ext = lv_obj_get_ext_attr(imgbtn);
return ext->img_src_left[state];
}
/**
* Get the middle image in a given state
* @param imgbtn pointer to an image button object
* @param state the state where to get the image (from `lv_btn_state_t`) `
* @return pointer to the middle image source (a C array or path to a file)
*/
const void * lv_imgbtn_get_src_middle(lv_obj_t * imgbtn, lv_btn_state_t state)
{
lv_imgbtn_ext_t * ext = lv_obj_get_ext_attr(imgbtn);
return ext->img_src_mid[state];
}
/**
* Get the right image in a given state
* @param imgbtn pointer to an image button object
* @param state the state where to get the image (from `lv_btn_state_t`) `
* @return pointer to the left image source (a C array or path to a file)
*/
const void * lv_imgbtn_get_src_right(lv_obj_t * imgbtn, lv_btn_state_t state)
{
lv_imgbtn_ext_t * ext = lv_obj_get_ext_attr(imgbtn);
return ext->img_src_right[state];
}
#endif
/**
* Get style of a image button.
* @param imgbtn pointer to image button object
* @param type which style should be get
* @return style pointer to the style
*/
lv_style_t * lv_imgbtn_get_style(const lv_obj_t * imgbtn, lv_imgbtn_style_t type)
{
return lv_btn_get_style(imgbtn, type);
}
/*=====================
* Other functions
*====================*/
/*
* New object specific "other" functions come here
*/
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Handle the drawing related tasks of the image buttons
* @param imgbtn pointer to an object
* @param mask the object will be drawn only in this area
* @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area
* (return 'true' if yes)
* LV_DESIGN_DRAW: draw the object (always return 'true')
* LV_DESIGN_DRAW_POST: drawing after every children are drawn
* @param return true/false, depends on 'mode'
*/
static bool lv_imgbtn_design(lv_obj_t * imgbtn, const lv_area_t * mask, lv_design_mode_t mode)
{
/*Return false if the object is not covers the mask_p area*/
if(mode == LV_DESIGN_COVER_CHK) {
lv_imgbtn_ext_t * ext = lv_obj_get_ext_attr(imgbtn);
bool cover = false;
if(ext->act_cf == LV_IMG_CF_TRUE_COLOR || ext->act_cf == LV_IMG_CF_RAW) {
cover = lv_area_is_in(mask, &imgbtn->coords);
}
return cover;
}
/*Draw the object*/
else if(mode == LV_DESIGN_DRAW_MAIN) {
/*Just draw an image*/
lv_imgbtn_ext_t * ext = lv_obj_get_ext_attr(imgbtn);
lv_btn_state_t state = lv_imgbtn_get_state(imgbtn);
lv_style_t * style = lv_imgbtn_get_style(imgbtn, state);
lv_opa_t opa_scale = lv_obj_get_opa_scale(imgbtn);
#if LV_IMGBTN_TILED == 0
const void * src = ext->img_src[state];
lv_draw_img(&imgbtn->coords, mask, src, style, opa_scale);
#else
const void * src;
lv_img_header_t header;
lv_area_t coords;
lv_coord_t left_w = 0;
lv_coord_t right_w = 0;
src = ext->img_src_left[state];
if(src) {
lv_img_dsc_get_info(src, &header);
left_w = header.w;
coords.x1 = imgbtn->coords.x1;
coords.y1 = imgbtn->coords.y1;
coords.x2 = coords.x1 + header.w - 1;
coords.y2 = coords.y1 + header.h - 1;
lv_draw_img(&coords, mask, src, style, opa_scale);
}
src = ext->img_src_right[state];
if(src) {
lv_img_dsc_get_info(src, &header);
right_w = header.w;
coords.x1 = imgbtn->coords.x2 - header.w + 1;
coords.y1 = imgbtn->coords.y1;
coords.x2 = imgbtn->coords.x2;
coords.y2 = imgbtn->coords.y1 + header.h - 1;
lv_draw_img(&coords, mask, src, style, opa_scale);
}
src = ext->img_src_mid[state];
if(src) {
lv_coord_t obj_w = lv_obj_get_width(imgbtn);
lv_coord_t i;
lv_img_dsc_get_info(src, &header);
coords.x1 = imgbtn->coords.x1 + left_w ;
coords.y1 = imgbtn->coords.y1;
coords.x2 = coords.x1 + header.w - 1;
coords.y2 = imgbtn->coords.y1 + header.h - 1;
for(i = 0; i < obj_w - right_w - left_w; i += header.w) {
lv_draw_img(&coords, mask, src, style, opa_scale);
coords.x1 = coords.x2 + 1;
coords.x2 += header.w;
}
}
#endif
}
/*Post draw when the children are drawn*/
else if(mode == LV_DESIGN_DRAW_POST) {
}
return true;
}
/**
* Signal function of the image button
* @param imgbtn pointer to a image button object
* @param sign a signal type from lv_signal_t enum
* @param param pointer to a signal specific variable
* @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
*/
static lv_res_t lv_imgbtn_signal(lv_obj_t * imgbtn, lv_signal_t sign, void * param)
{
lv_res_t res;
/* Include the ancient signal function */
res = ancestor_signal(imgbtn, sign, param);
if(res != LV_RES_OK) return res;
if(sign == LV_SIGNAL_STYLE_CHG) {
/* If the style changed then the button was clicked, released etc. so probably the state was changed as well
* Set the new image for the new state.*/
refr_img(imgbtn);
} else if(sign == LV_SIGNAL_CLEANUP) {
/*Nothing to cleanup. (No dynamically allocated memory in 'ext')*/
} else if(sign == LV_SIGNAL_GET_TYPE) {
lv_obj_type_t * buf = param;
uint8_t i;
for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/
if(buf->type[i] == NULL) break;
}
buf->type[i] = "lv_imgbtn";
}
return res;
}
static void refr_img(lv_obj_t * imgbtn)
{
lv_imgbtn_ext_t * ext = lv_obj_get_ext_attr(imgbtn);
lv_btn_state_t state = lv_imgbtn_get_state(imgbtn);
lv_img_header_t header;
#if LV_IMGBTN_TILED == 0
const void * src = ext->img_src[state];
#else
const void * src = ext->img_src_mid[state];
#endif
lv_res_t info_res;
info_res = lv_img_dsc_get_info(src, &header);
if(info_res == LV_RES_OK) {
ext->act_cf = header.cf;
#if LV_IMGBTN_TILED == 0
lv_obj_set_size(imgbtn, header.w, header.h);
#else
lv_obj_set_height(imgbtn, header.h);
#endif
} else {
ext->act_cf = LV_IMG_CF_UNKOWN;
}
lv_obj_invalidate(imgbtn);
}
#endif

View File

@@ -0,0 +1,249 @@
/**
* @file lv_imgbtn.h
*
*/
#ifndef LV_IMGBTN_H
#define LV_IMGBTN_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#ifdef LV_CONF_INCLUDE_SIMPLE
#include "lv_conf.h"
#else
#include "../../lv_conf.h"
#endif
#if USE_LV_IMGBTN != 0
/*Testing of dependencies*/
#if USE_LV_BTN == 0
#error "lv_imgbtn: lv_btn is required. Enable it in lv_conf.h (USE_LV_BTN 1) "
#endif
#include "../lv_core/lv_obj.h"
#include "lv_btn.h"
#include "../lv_draw/lv_draw_img.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/*Data of image button*/
typedef struct {
lv_btn_ext_t btn; /*Ext. of ancestor*/
/*New data for this type */
int idx;
#if LV_IMGBTN_TILED == 0
const void * img_src[LV_BTN_STATE_NUM]; /*Store images to each state*/
#else
const void * img_src_left[LV_BTN_STATE_NUM]; /*Store left side images to each state*/
const void * img_src_mid[LV_BTN_STATE_NUM]; /*Store center images to each state*/
const void * img_src_right[LV_BTN_STATE_NUM]; /*Store right side images to each state*/
#endif
lv_img_cf_t act_cf; /*Color format of the currently active image*/
} lv_imgbtn_ext_t;
/*Styles*/
enum {
LV_IMGBTN_STYLE_REL,
LV_IMGBTN_STYLE_PR,
LV_IMGBTN_STYLE_TGL_REL,
LV_IMGBTN_STYLE_TGL_PR,
LV_IMGBTN_STYLE_INA,
};
typedef uint8_t lv_imgbtn_style_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a image button objects
* @param par pointer to an object, it will be the parent of the new image button
* @param copy pointer to a image button object, if not NULL then the new object will be copied from it
* @return pointer to the created image button
*/
lv_obj_t * lv_imgbtn_create(lv_obj_t * par, const lv_obj_t * copy);
/*======================
* Add/remove functions
*=====================*/
/*=====================
* Setter functions
*====================*/
#if LV_IMGBTN_TILED == 0
/**
* Set images for a state of the image button
* @param imgbtn pointer to an image button object
* @param state for which state set the new image (from `lv_btn_state_t`) `
* @param src pointer to an image source (a C array or path to a file)
*/
void lv_imgbtn_set_src(lv_obj_t * imgbtn, lv_btn_state_t state, const void * src);
#else
/**
* Set images for a state of the image button
* @param imgbtn pointer to an image button object
* @param state for which state set the new image (from `lv_btn_state_t`) `
* @param src_left pointer to an image source for the left side of the button (a C array or path to a file)
* @param src_mid pointer to an image source for the middle of the button (ideally 1px wide) (a C array or path to a file)
* @param src_right pointer to an image source for the right side of the button (a C array or path to a file)
*/
void lv_imgbtn_set_src(lv_obj_t * imgbtn, lv_btn_state_t state, const void * src_left, const void * src_mid, const void * src_right);
#endif
/**
* Enable the toggled states. On release the button will change from/to toggled state.
* @param imgbtn pointer to an image button object
* @param tgl true: enable toggled states, false: disable
*/
static inline void lv_imgbtn_set_toggle(lv_obj_t * imgbtn, bool tgl)
{
lv_btn_set_toggle(imgbtn, tgl);
}
/**
* Set the state of the image button
* @param imgbtn pointer to an image button object
* @param state the new state of the button (from lv_btn_state_t enum)
*/
static inline void lv_imgbtn_set_state(lv_obj_t * imgbtn, lv_btn_state_t state)
{
lv_btn_set_state(imgbtn, state);
}
/**
* Toggle the state of the image button (ON->OFF, OFF->ON)
* @param imgbtn pointer to a image button object
*/
static inline void lv_imgbtn_toggle(lv_obj_t * imgbtn)
{
lv_btn_toggle(imgbtn);
}
/**
* Set a function to call when a button event happens
* @param imgbtn pointer to an image button object
* @param action type of event form 'lv_action_t' (press, release, long press, long press repeat)
*/
static inline void lv_imgbtn_set_action(lv_obj_t * imgbtn, lv_btn_action_t type, lv_action_t action)
{
lv_btn_set_action(imgbtn, type, action);
}
/**
* Set a style of a image button.
* @param imgbtn pointer to image button object
* @param type which style should be set
* @param style pointer to a style
*/
void lv_imgbtn_set_style(lv_obj_t * imgbtn, lv_imgbtn_style_t type, lv_style_t *style);
/*=====================
* Getter functions
*====================*/
#if LV_IMGBTN_TILED == 0
/**
* Get the images in a given state
* @param imgbtn pointer to an image button object
* @param state the state where to get the image (from `lv_btn_state_t`) `
* @return pointer to an image source (a C array or path to a file)
*/
const void * lv_imgbtn_get_src(lv_obj_t * imgbtn, lv_btn_state_t state);
#else
/**
* Get the left image in a given state
* @param imgbtn pointer to an image button object
* @param state the state where to get the image (from `lv_btn_state_t`) `
* @return pointer to the left image source (a C array or path to a file)
*/
const void * lv_imgbtn_get_src_left(lv_obj_t * imgbtn, lv_btn_state_t state);
/**
* Get the middle image in a given state
* @param imgbtn pointer to an image button object
* @param state the state where to get the image (from `lv_btn_state_t`) `
* @return pointer to the middle image source (a C array or path to a file)
*/
const void * lv_imgbtn_get_src_middle(lv_obj_t * imgbtn, lv_btn_state_t state);
/**
* Get the right image in a given state
* @param imgbtn pointer to an image button object
* @param state the state where to get the image (from `lv_btn_state_t`) `
* @return pointer to the left image source (a C array or path to a file)
*/
const void * lv_imgbtn_get_src_right(lv_obj_t * imgbtn, lv_btn_state_t state);
#endif
/**
* Get the current state of the image button
* @param imgbtn pointer to a image button object
* @return the state of the button (from lv_btn_state_t enum)
*/
static inline lv_btn_state_t lv_imgbtn_get_state(const lv_obj_t * imgbtn)
{
return lv_btn_get_state(imgbtn);
}
/**
* Get the toggle enable attribute of the image button
* @param imgbtn pointer to a image button object
* @return ture: toggle enabled, false: disabled
*/
static inline bool lv_imgbtn_get_toggle(const lv_obj_t * imgbtn)
{
return lv_btn_get_toggle(imgbtn);
}
/**
* Get the release action of a image button
* @param imgbtn pointer to a image button object
* @return pointer to the release action function
*/
static inline lv_action_t lv_imgbtn_get_action(const lv_obj_t * imgbtn, lv_btn_action_t type)
{
return lv_btn_get_action(imgbtn, type);
}
/**
* Get style of a image button.
* @param imgbtn pointer to image button object
* @param type which style should be get
* @return style pointer to the style
*/
lv_style_t * lv_imgbtn_get_style(const lv_obj_t * imgbtn, lv_imgbtn_style_t type);
/*=====================
* Other functions
*====================*/
/**********************
* MACROS
**********************/
#endif /*USE_LV_IMGBTN*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_IMGBTN_H*/

View File

@@ -0,0 +1,488 @@
/*
* Copyright (c) 2019-2020 CTCaer
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file lv_kb.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_kb.h"
#if USE_LV_KB != 0
#include "lv_ta.h"
#include "../lv_themes/lv_theme.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static lv_res_t lv_kb_signal(lv_obj_t * kb, lv_signal_t sign, void * param);
static lv_res_t lv_kb_def_action(lv_obj_t * kb, const char * txt);
/**********************
* STATIC VARIABLES
**********************/
static lv_signal_func_t ancestor_signal;
static const char * kb_map_lc[] = {
"\2051#", "\204q", "\204w", "\204e", "\204r", "\204t", "\204y", "\204u", "\204i", "\204o", "\204p", "\207Bksp", "\n",
"\226ABC", "\203a", "\203s", "\203d", "\203f", "\203g", "\203h", "\203j", "\203k", "\203l", "\207Enter", "\n",
"_", "-", "z", "x", "c", "v", "b", "n", "m", ".", ",", ":", "\n",
"\202"SYMBOL_CLOSE, "\202"SYMBOL_LEFT, "\206 ", "\202"SYMBOL_RIGHT, "\202"SYMBOL_OK, ""
};
static const char * kb_map_uc[] = {
"\2051#", "\204Q", "\204W", "\204E", "\204R", "\204T", "\204Y", "\204U", "\204I", "\204O", "\204P", "\207Bksp", "\n",
"\226abc", "\203A", "\203S", "\203D", "\203F", "\203G", "\203H", "\203J", "\203K", "\203L", "\207Enter", "\n",
"_", "-", "Z", "X", "C", "V", "B", "N", "M", ".", ",", ":", "\n",
"\202"SYMBOL_CLOSE, "\202"SYMBOL_LEFT, "\206 ", "\202"SYMBOL_RIGHT, "\202"SYMBOL_OK, ""
};
static const char * kb_map_spec[] = {
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "\202Bksp", "\n",
"\222abc", "+", "-", "/", "*", "=", "%", "!", "?", "#", "<", ">", "\n",
"\\", "@", "$", "(", ")", "{", "}", "[", "]", ";", "\"", "'", "\n",
"\202"SYMBOL_CLOSE, "\202"SYMBOL_LEFT, "\206 ", "\202"SYMBOL_RIGHT, "\202"SYMBOL_OK, ""
};
static const char * kb_map_num[] = {
"1", "2", "3", "\202"SYMBOL_CLOSE, "\n",
"4", "5", "6", "\202"SYMBOL_OK, "\n",
"7", "8", "9", "\202Bksp", "\n",
"+/-", "0", ".", SYMBOL_LEFT, SYMBOL_RIGHT, ""
};
static const char * kb_map_hex[] = {
"1", "2", "3", "A", "D", "\212", "\n",
"4", "5", "6", "B", "E", "\202Bksp", "\n",
"7", "8", "9", "C", "F", "\202"SYMBOL_OK, "\n",
"\211", "0", "\213", SYMBOL_LEFT, SYMBOL_RIGHT, ""
};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Create a keyboard objects
* @param par pointer to an object, it will be the parent of the new keyboard
* @param copy pointer to a keyboard object, if not NULL then the new object will be copied from it
* @return pointer to the created keyboard
*/
lv_obj_t * lv_kb_create(lv_obj_t * par, const lv_obj_t * copy)
{
LV_LOG_TRACE("keyboard create started");
/*Create the ancestor of keyboard*/
lv_obj_t * new_kb = lv_btnm_create(par, copy);
lv_mem_assert(new_kb);
if(new_kb == NULL) return NULL;
if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_kb);
/*Allocate the keyboard type specific extended data*/
lv_kb_ext_t * ext = lv_obj_allocate_ext_attr(new_kb, sizeof(lv_kb_ext_t));
lv_mem_assert(ext);
if(ext == NULL) return NULL;
/*Initialize the allocated 'ext' */
ext->ta = NULL;
ext->mode = LV_KB_MODE_TEXT;
ext->cursor_mng = 0;
ext->hide_action = NULL;
ext->ok_action = NULL;
/*The signal and design functions are not copied so set them here*/
lv_obj_set_signal_func(new_kb, lv_kb_signal);
/*Init the new keyboard keyboard*/
if(copy == NULL) {
lv_obj_set_size(new_kb, LV_HOR_RES, LV_VER_RES / 2);
lv_obj_align(new_kb, NULL, LV_ALIGN_IN_BOTTOM_MID, 0, 0);
lv_btnm_set_action(new_kb, lv_kb_def_action);
lv_btnm_set_map(new_kb, kb_map_lc);
/*Set the default styles*/
lv_theme_t * th = lv_theme_get_current();
if(th) {
lv_kb_set_style(new_kb, LV_KB_STYLE_BG, th->kb.bg);
lv_kb_set_style(new_kb, LV_KB_STYLE_BTN_REL, th->kb.btn.rel);
lv_kb_set_style(new_kb, LV_KB_STYLE_BTN_PR, th->kb.btn.pr);
lv_kb_set_style(new_kb, LV_KB_STYLE_BTN_TGL_REL, th->kb.btn.tgl_rel);
lv_kb_set_style(new_kb, LV_KB_STYLE_BTN_TGL_PR, th->kb.btn.tgl_pr);
lv_kb_set_style(new_kb, LV_KB_STYLE_BTN_INA, th->kb.btn.ina);
} else {
/*Let the button matrix's styles*/
}
}
/*Copy an existing keyboard*/
else {
lv_kb_ext_t * copy_ext = lv_obj_get_ext_attr(copy);
ext->ta = NULL;
ext->ta = copy_ext->ta;
ext->mode = copy_ext->mode;
ext->cursor_mng = copy_ext->cursor_mng;
ext->hide_action = copy_ext->hide_action;
ext->ok_action = copy_ext->ok_action;
/*Refresh the style with new signal function*/
lv_obj_refresh_style(new_kb);
}
LV_LOG_INFO("keyboard created");
return new_kb;
}
/*=====================
* Setter functions
*====================*/
/**
* Assign a Text Area to the Keyboard. The pressed characters will be put there.
* @param kb pointer to a Keyboard object
* @param ta pointer to a Text Area object to write there
*/
void lv_kb_set_ta(lv_obj_t * kb, lv_obj_t * ta)
{
lv_kb_ext_t * ext = lv_obj_get_ext_attr(kb);
lv_cursor_type_t cur_type;
/*Hide the cursor of the old Text area if cursor management is enabled*/
if(ext->ta && ext->cursor_mng) {
cur_type = lv_ta_get_cursor_type(ext->ta);
lv_ta_set_cursor_type(ext->ta, cur_type | LV_CURSOR_HIDDEN);
}
ext->ta = ta;
/*Show the cursor of the new Text area if cursor management is enabled*/
if(ext->ta && ext->cursor_mng) {
cur_type = lv_ta_get_cursor_type(ext->ta);
lv_ta_set_cursor_type(ext->ta, cur_type & (~LV_CURSOR_HIDDEN));
}
}
/**
* Set a new a mode (text or number map)
* @param kb pointer to a Keyboard object
* @param mode the mode from 'lv_kb_mode_t'
*/
void lv_kb_set_mode(lv_obj_t * kb, lv_kb_mode_t mode)
{
lv_kb_ext_t * ext = lv_obj_get_ext_attr(kb);
if(ext->mode == mode) return;
ext->mode = mode;
if(mode == LV_KB_MODE_TEXT) lv_btnm_set_map(kb, kb_map_lc);
else if(mode == LV_KB_MODE_NUM) lv_btnm_set_map(kb, kb_map_num);
else if (mode == LV_KB_MODE_HEX) lv_btnm_set_map(kb, kb_map_hex);
}
/**
* Automatically hide or show the cursor of Text Area
* @param kb pointer to a Keyboard object
* @param en true: show cursor on the current text area, false: hide cursor
*/
void lv_kb_set_cursor_manage(lv_obj_t * kb, bool en)
{
lv_kb_ext_t * ext = lv_obj_get_ext_attr(kb);
if(ext->cursor_mng == en) return;
ext->cursor_mng = en == false ? 0 : 1;
if(ext->ta) {
lv_cursor_type_t cur_type;
cur_type = lv_ta_get_cursor_type(ext->ta);
if(ext->cursor_mng) {
lv_ta_set_cursor_type(ext->ta, cur_type & (~LV_CURSOR_HIDDEN));
} else {
lv_ta_set_cursor_type(ext->ta, cur_type | LV_CURSOR_HIDDEN);
}
}
}
/**
* Set call back to call when the "Ok" button is pressed
* @param kb pointer to Keyboard object
* @param action a callback with 'lv_action_t' type
*/
void lv_kb_set_ok_action(lv_obj_t * kb, lv_action_t action)
{
lv_kb_ext_t * ext = lv_obj_get_ext_attr(kb);
ext->ok_action = action;
}
/**
* Set call back to call when the "Hide" button is pressed
* @param kb pointer to Keyboard object
* @param action a callback with 'lv_action_t' type
*/
void lv_kb_set_hide_action(lv_obj_t * kb, lv_action_t action)
{
lv_kb_ext_t * ext = lv_obj_get_ext_attr(kb);
ext->hide_action = action;
}
/**
* Set a style of a keyboard
* @param kb pointer to a keyboard object
* @param type which style should be set
* @param style pointer to a style
*/
void lv_kb_set_style(lv_obj_t * kb, lv_kb_style_t type, lv_style_t * style)
{
switch(type) {
case LV_KB_STYLE_BG:
lv_btnm_set_style(kb, LV_BTNM_STYLE_BG, style);
break;
case LV_KB_STYLE_BTN_REL:
lv_btnm_set_style(kb, LV_BTNM_STYLE_BTN_REL, style);
break;
case LV_KB_STYLE_BTN_PR:
lv_btnm_set_style(kb, LV_BTNM_STYLE_BTN_PR, style);
break;
case LV_KB_STYLE_BTN_TGL_REL:
lv_btnm_set_style(kb, LV_BTNM_STYLE_BTN_TGL_REL, style);
break;
case LV_KB_STYLE_BTN_TGL_PR:
lv_btnm_set_style(kb, LV_BTNM_STYLE_BTN_TGL_PR, style);
break;
case LV_KB_STYLE_BTN_INA:
lv_btnm_set_style(kb, LV_BTNM_STYLE_BTN_INA, style);
break;
}
}
/*=====================
* Getter functions
*====================*/
/**
* Assign a Text Area to the Keyboard. The pressed characters will be put there.
* @param kb pointer to a Keyboard object
* @return pointer to the assigned Text Area object
*/
lv_obj_t * lv_kb_get_ta(const lv_obj_t * kb)
{
lv_kb_ext_t * ext = lv_obj_get_ext_attr(kb);
return ext->ta;
}
/**
* Set a new a mode (text or number map)
* @param kb pointer to a Keyboard object
* @return the current mode from 'lv_kb_mode_t'
*/
lv_kb_mode_t lv_kb_get_mode(const lv_obj_t * kb)
{
lv_kb_ext_t * ext = lv_obj_get_ext_attr(kb);
return ext->mode;
}
/**
* Get the current cursor manage mode.
* @param kb pointer to a Keyboard object
* @return true: show cursor on the current text area, false: hide cursor
*/
bool lv_kb_get_cursor_manage(const lv_obj_t * kb)
{
lv_kb_ext_t * ext = lv_obj_get_ext_attr(kb);
return ext->cursor_mng == 0 ? false : true;
}
/**
* Get the callback to call when the "Ok" button is pressed
* @param kb pointer to Keyboard object
* @return the ok callback
*/
lv_action_t lv_kb_get_ok_action(const lv_obj_t * kb)
{
lv_kb_ext_t * ext = lv_obj_get_ext_attr(kb);
return ext->ok_action;
}
/**
* Get the callback to call when the "Hide" button is pressed
* @param kb pointer to Keyboard object
* @return the close callback
*/
lv_action_t lv_kb_get_hide_action(const lv_obj_t * kb)
{
lv_kb_ext_t * ext = lv_obj_get_ext_attr(kb);
return ext->hide_action;
}
/**
* Get a style of a keyboard
* @param kb pointer to a keyboard object
* @param type which style should be get
* @return style pointer to a style
*/
lv_style_t * lv_kb_get_style(const lv_obj_t * kb, lv_kb_style_t type)
{
lv_style_t * style = NULL;
switch(type) {
case LV_KB_STYLE_BG:
style = lv_btnm_get_style(kb, LV_BTNM_STYLE_BG);
break;
case LV_KB_STYLE_BTN_REL:
style = lv_btnm_get_style(kb, LV_BTNM_STYLE_BTN_REL);
break;
case LV_KB_STYLE_BTN_PR:
style = lv_btnm_get_style(kb, LV_BTNM_STYLE_BTN_PR);
break;
case LV_KB_STYLE_BTN_TGL_REL:
style = lv_btnm_get_style(kb, LV_BTNM_STYLE_BTN_TGL_REL);
break;
case LV_KB_STYLE_BTN_TGL_PR:
style = lv_btnm_get_style(kb, LV_BTNM_STYLE_BTN_TGL_PR);
break;
case LV_KB_STYLE_BTN_INA:
style = lv_btnm_get_style(kb, LV_BTNM_STYLE_BTN_INA);
break;
default:
style = NULL;
break;
}
return style;
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Signal function of the keyboard
* @param kb pointer to a keyboard object
* @param sign a signal type from lv_signal_t enum
* @param param pointer to a signal specific variable
* @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
*/
static lv_res_t lv_kb_signal(lv_obj_t * kb, lv_signal_t sign, void * param)
{
lv_res_t res;
/* Include the ancient signal function */
res = ancestor_signal(kb, sign, param);
if(res != LV_RES_OK) return res;
if(sign == LV_SIGNAL_CLEANUP) {
/*Nothing to cleanup. (No dynamically allocated memory in 'ext')*/
} else if(sign == LV_SIGNAL_GET_TYPE) {
lv_obj_type_t * buf = param;
uint8_t i;
for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/
if(buf->type[i] == NULL) break;
}
buf->type[i] = "lv_kb";
}
return res;
}
/**
* Called when a button of 'kb_btnm' is released
* @param btnm pointer to 'kb_btnm'
* @param i the index of the released button from the current btnm map
* @return LV_ACTION_RES_INV if the btnm is deleted else LV_ACTION_RES_OK
*/
static lv_res_t lv_kb_def_action(lv_obj_t * kb, const char * txt)
{
lv_kb_ext_t * ext = lv_obj_get_ext_attr(kb);
lv_res_t res = LV_RES_OK;
/*Do the corresponding action according to the text of the button*/
if(strcmp(txt, "abc") == 0) {
lv_btnm_set_map(kb, kb_map_lc);
return LV_RES_OK;
} else if(strcmp(txt, "ABC") == 0) {
lv_btnm_set_map(kb, kb_map_uc);
return LV_RES_OK;
} else if(strcmp(txt, "1#") == 0) {
lv_btnm_set_map(kb, kb_map_spec);
return LV_RES_OK;
} else if(strcmp(txt, SYMBOL_CLOSE) == 0) {
if(ext->hide_action) res = ext->hide_action(kb);
else {
lv_kb_set_ta(kb, NULL); /*De-assign the text area to hide it cursor if needed*/
lv_obj_del(kb);
}
return res;
} else if(strcmp(txt, SYMBOL_OK) == 0) {
if(ext->ok_action) res = ext->ok_action(kb);
else {
lv_kb_set_ta(kb, NULL); /*De-assign the text area to hide it cursor if needed*/
res = lv_obj_del(kb);
}
return res;
}
if(res != LV_RES_OK) return res; /*The keyboard might be deleted in the actions*/
/*Add the characters to the text area if set*/
if(ext->ta == NULL) return res;
if(strcmp(txt, "Enter") == 0)lv_ta_add_char(ext->ta, '\n');
else if(strcmp(txt, SYMBOL_LEFT) == 0) lv_ta_cursor_left(ext->ta);
else if(strcmp(txt, SYMBOL_RIGHT) == 0) lv_ta_cursor_right(ext->ta);
else if(strcmp(txt, "Bksp") == 0) lv_ta_del_char(ext->ta);
else if(strcmp(txt, "+/-") == 0) {
uint16_t cur = lv_ta_get_cursor_pos(ext->ta);
const char * ta_txt = lv_ta_get_text(ext->ta);
if(ta_txt[0] == '-') {
lv_ta_set_cursor_pos(ext->ta, 1);
lv_ta_del_char(ext->ta);
lv_ta_add_char(ext->ta, '+');
lv_ta_set_cursor_pos(ext->ta, cur);
} else if(ta_txt[0] == '+') {
lv_ta_set_cursor_pos(ext->ta, 1);
lv_ta_del_char(ext->ta);
lv_ta_add_char(ext->ta, '-');
lv_ta_set_cursor_pos(ext->ta, cur);
} else {
lv_ta_set_cursor_pos(ext->ta, 0);
lv_ta_add_char(ext->ta, '-');
lv_ta_set_cursor_pos(ext->ta, cur + 1);
}
} else {
lv_ta_add_text(ext->ta, txt);
}
return LV_RES_OK;
}
#endif

View File

@@ -0,0 +1,216 @@
/*
* Copyright (c) 2019-2020 CTCaer
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file lv_kb.h
*
*/
#ifndef LV_KB_H
#define LV_KB_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#ifdef LV_CONF_INCLUDE_SIMPLE
#include "lv_conf.h"
#else
#include "../../lv_conf.h"
#endif
#if USE_LV_KB != 0
/*Testing of dependencies*/
#if USE_LV_BTNM == 0
#error "lv_kb: lv_btnm is required. Enable it in lv_conf.h (USE_LV_BTNM 1) "
#endif
#if USE_LV_TA == 0
#error "lv_kb: lv_ta is required. Enable it in lv_conf.h (USE_LV_TA 1) "
#endif
#include "../lv_core/lv_obj.h"
#include "lv_btnm.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
enum {
LV_KB_MODE_TEXT,
LV_KB_MODE_NUM,
LV_KB_MODE_HEX
};
typedef uint8_t lv_kb_mode_t;
/*Data of keyboard*/
typedef struct {
lv_btnm_ext_t btnm; /*Ext. of ancestor*/
/*New data for this type */
lv_obj_t *ta; /*Pointer to the assigned text area*/
lv_kb_mode_t mode; /*Key map type*/
uint8_t cursor_mng :1; /*1: automatically show/hide cursor when a text area is assigned or left*/
lv_action_t ok_action; /*Called when the "Ok" button is clicked*/
lv_action_t hide_action; /*Called when the "Hide" button is clicked*/
} lv_kb_ext_t;
enum {
LV_KB_STYLE_BG,
LV_KB_STYLE_BTN_REL,
LV_KB_STYLE_BTN_PR,
LV_KB_STYLE_BTN_TGL_REL,
LV_KB_STYLE_BTN_TGL_PR,
LV_KB_STYLE_BTN_INA,
};
typedef uint8_t lv_kb_style_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a keyboard objects
* @param par pointer to an object, it will be the parent of the new keyboard
* @param copy pointer to a keyboard object, if not NULL then the new object will be copied from it
* @return pointer to the created keyboard
*/
lv_obj_t * lv_kb_create(lv_obj_t * par, const lv_obj_t * copy);
/*=====================
* Setter functions
*====================*/
/**
* Assign a Text Area to the Keyboard. The pressed characters will be put there.
* @param kb pointer to a Keyboard object
* @param ta pointer to a Text Area object to write there
*/
void lv_kb_set_ta(lv_obj_t * kb, lv_obj_t * ta);
/**
* Set a new a mode (text or number map)
* @param kb pointer to a Keyboard object
* @param mode the mode from 'lv_kb_mode_t'
*/
void lv_kb_set_mode(lv_obj_t * kb, lv_kb_mode_t mode);
/**
* Automatically hide or show the cursor of the current Text Area
* @param kb pointer to a Keyboard object
* @param en true: show cursor on the current text area, false: hide cursor
*/
void lv_kb_set_cursor_manage(lv_obj_t * kb, bool en);
/**
* Set call back to call when the "Ok" button is pressed
* @param kb pointer to Keyboard object
* @param action a callback with 'lv_action_t' type
*/
void lv_kb_set_ok_action(lv_obj_t * kb, lv_action_t action);
/**
* Set call back to call when the "Hide" button is pressed
* @param kb pointer to Keyboard object
* @param action a callback with 'lv_action_t' type
*/
void lv_kb_set_hide_action(lv_obj_t * kb, lv_action_t action);
/**
* Set a new map for the keyboard
* @param kb pointer to a Keyboard object
* @param map pointer to a string array to describe the map.
* See 'lv_btnm_set_map()' for more info.
*/
static inline void lv_kb_set_map(lv_obj_t *kb, const char ** map)
{
lv_btnm_set_map(kb, map);
}
/**
* Set a style of a keyboard
* @param kb pointer to a keyboard object
* @param type which style should be set
* @param style pointer to a style
*/
void lv_kb_set_style(lv_obj_t *kb, lv_kb_style_t type, lv_style_t *style);
/*=====================
* Getter functions
*====================*/
/**
* Assign a Text Area to the Keyboard. The pressed characters will be put there.
* @param kb pointer to a Keyboard object
* @return pointer to the assigned Text Area object
*/
lv_obj_t * lv_kb_get_ta(const lv_obj_t * kb);
/**
* Set a new a mode (text or number map)
* @param kb pointer to a Keyboard object
* @return the current mode from 'lv_kb_mode_t'
*/
lv_kb_mode_t lv_kb_get_mode(const lv_obj_t * kb);
/**
* Get the current cursor manage mode.
* @param kb pointer to a Keyboard object
* @return true: show cursor on the current text area, false: hide cursor
*/
bool lv_kb_get_cursor_manage(const lv_obj_t * kb);
/**
* Get the callback to call when the "Ok" button is pressed
* @param kb pointer to Keyboard object
* @return the ok callback
*/
lv_action_t lv_kb_get_ok_action(const lv_obj_t * kb);
/**
* Get the callback to call when the "Hide" button is pressed
* @param kb pointer to Keyboard object
* @return the close callback
*/
lv_action_t lv_kb_get_hide_action(const lv_obj_t * kb);
/**
* Get a style of a keyboard
* @param kb pointer to a keyboard object
* @param type which style should be get
* @return style pointer to a style
*/
lv_style_t * lv_kb_get_style(const lv_obj_t *kb, lv_kb_style_t type);
/**********************
* MACROS
**********************/
#endif /*USE_LV_KB*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_KB_H*/

View File

@@ -0,0 +1,987 @@
/**
* @file lv_rect.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_label.h"
#if USE_LV_LABEL != 0
#include "../lv_core/lv_obj.h"
#include "../lv_core/lv_group.h"
#include "../lv_core/lv_lang.h"
#include "../lv_draw/lv_draw.h"
#include "../lv_misc/lv_color.h"
#include "../lv_misc/lv_math.h"
/*********************
* DEFINES
*********************/
/*Test configurations*/
#ifndef LV_LABEL_SCROLL_SPEED
#define LV_LABEL_SCROLL_SPEED (25) /*Hor, or ver. scroll speed (px/sec) in 'LV_LABEL_LONG_SCROLL/ROLL' mode*/
#endif
#define ANIM_WAIT_CHAR_COUNT 3
#define LV_LABEL_DOT_END_INV 0xFFFF
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static lv_res_t lv_label_signal(lv_obj_t * label, lv_signal_t sign, void * param);
static bool lv_label_design(lv_obj_t * label, const lv_area_t * mask, lv_design_mode_t mode);
static void lv_label_refr_text(lv_obj_t * label);
static void lv_label_revert_dots(lv_obj_t * label);
#if USE_LV_ANIMATION
static void lv_label_set_offset_x(lv_obj_t * label, lv_coord_t x);
static void lv_label_set_offset_y(lv_obj_t * label, lv_coord_t y);
#endif
/**********************
* STATIC VARIABLES
**********************/
static lv_signal_func_t ancestor_signal;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Create a label objects
* @param par pointer to an object, it will be the parent of the new label
* @param copy pointer to a button object, if not NULL then the new object will be copied from it
* @return pointer to the created button
*/
lv_obj_t * lv_label_create(lv_obj_t * par, const lv_obj_t * copy)
{
LV_LOG_TRACE("label create started");
/*Create a basic object*/
lv_obj_t * new_label = lv_obj_create(par, copy);
lv_mem_assert(new_label);
if(new_label == NULL) return NULL;
if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_label);
/*Extend the basic object to a label object*/
lv_obj_allocate_ext_attr(new_label, sizeof(lv_label_ext_t));
lv_label_ext_t * ext = lv_obj_get_ext_attr(new_label);
lv_mem_assert(ext);
if(ext == NULL) return NULL;
ext->text = NULL;
ext->static_txt = 0;
ext->recolor = 0;
ext->body_draw = 0;
ext->align = LV_LABEL_ALIGN_LEFT;
ext->dot_end = LV_LABEL_DOT_END_INV;
ext->long_mode = LV_LABEL_LONG_EXPAND;
ext->anim_speed = LV_LABEL_SCROLL_SPEED;
ext->offset.x = 0;
ext->offset.y = 0;
#if USE_LV_MULTI_LANG
ext->lang_txt_id = LV_LANG_TXT_ID_NONE;
#endif
lv_obj_set_design_func(new_label, lv_label_design);
lv_obj_set_signal_func(new_label, lv_label_signal);
/*Init the new label*/
if(copy == NULL) {
lv_obj_set_click(new_label, false);
lv_label_set_long_mode(new_label, LV_LABEL_LONG_EXPAND);
lv_label_set_text(new_label, "Text");
lv_label_set_style(new_label, NULL); /*Inherit parent's style*/
}
/*Copy 'copy' if not NULL*/
else {
lv_label_ext_t * copy_ext = lv_obj_get_ext_attr(copy);
lv_label_set_long_mode(new_label, lv_label_get_long_mode(copy));
lv_label_set_recolor(new_label, lv_label_get_recolor(copy));
lv_label_set_body_draw(new_label, lv_label_get_body_draw(copy));
lv_label_set_align(new_label, lv_label_get_align(copy));
if(copy_ext->static_txt == 0) lv_label_set_text(new_label, lv_label_get_text(copy));
else lv_label_set_static_text(new_label, lv_label_get_text(copy));
/*In DOT mode save the text byte-to-byte because a '\0' can be in the middle*/
if(copy_ext->long_mode == LV_LABEL_LONG_DOT) {
ext->text = lv_mem_realloc(ext->text, lv_mem_get_size(copy_ext->text));
lv_mem_assert(ext->text);
if(ext->text == NULL) return NULL;
memcpy(ext->text, copy_ext->text, lv_mem_get_size(copy_ext->text));
}
memcpy(ext->dot_tmp, copy_ext->dot_tmp, sizeof(ext->dot_tmp));
ext->dot_end = copy_ext->dot_end;
/*Refresh the style with new signal function*/
lv_obj_refresh_style(new_label);
}
LV_LOG_INFO("label created");
return new_label;
}
/*=====================
* Setter functions
*====================*/
/**
* Set a new text for a label. Memory will be allocated to store the text by the label.
* @param label 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 * label, const char * text)
{
lv_obj_invalidate(label);
lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
/*If text is NULL then refresh */
if(text == NULL) {
lv_label_refr_text(label);
return;
}
if(ext->text == text && ext->static_txt == 0) {
/*If set its own text then reallocate it (maybe its size changed)*/
ext->text = lv_mem_realloc(ext->text, strlen(ext->text) + 1);
lv_mem_assert(ext->text);
if(ext->text == NULL) return;
} else {
/*Allocate space for the new text*/
uint32_t len = strlen(text) + 1;
if(ext->text != NULL && ext->static_txt == 0) {
lv_mem_free(ext->text);
ext->text = NULL;
}
ext->text = lv_mem_alloc(len);
lv_mem_assert(ext->text);
if(ext->text == NULL) return;
strcpy(ext->text, text);
ext->static_txt = 0; /*Now the text is dynamically allocated*/
}
lv_label_refr_text(label);
}
/**
* Set a new text for a label from a character array. The array don't has to be '\0' terminated.
* Memory will be allocated to store the array by the label.
* @param label pointer to a label object
* @param array array of characters or NULL to refresh the label
* @param size the size of 'array' in bytes
*/
void lv_label_set_array_text(lv_obj_t * label, const char * array, uint16_t size)
{
lv_obj_invalidate(label);
lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
/*If trying to set its own text or the array is NULL then refresh */
if(array == ext->text || array == NULL) {
lv_label_refr_text(label);
return;
}
/*Allocate space for the new text*/
if(ext->text != NULL && ext->static_txt == 0) {
lv_mem_free(ext->text);
ext->text = NULL;
}
ext->text = lv_mem_alloc(size + 1);
lv_mem_assert(ext->text);
if(ext->text == NULL) return;
memcpy(ext->text, array, size);
ext->text[size] = '\0';
ext->static_txt = 0; /*Now the text is dynamically allocated*/
lv_label_refr_text(label);
}
/**
* Set a static text. It will not be saved by the label so the 'text' variable
* has to be 'alive' while the label exist.
* @param label pointer to a label object
* @param text pointer to a text. NULL to refresh with the current text.
*/
void lv_label_set_static_text(lv_obj_t * label, const char * text)
{
lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
if(ext->static_txt == 0 && ext->text != NULL) {
lv_mem_free(ext->text);
ext->text = NULL;
}
if(text != NULL) {
ext->static_txt = 1;
ext->text = (char *) text;
}
lv_label_refr_text(label);
}
#if USE_LV_MULTI_LANG
/**
*Set a text ID which refers a the same text but in a different languages
* @param label pointer to a label object
* @param txt_id ID of the text
*/
void lv_label_set_text_id(lv_obj_t * label, uint32_t txt_id)
{
lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
ext->lang_txt_id = txt_id;
/*Apply the new language*/
label->signal_func(label, LV_SIGNAL_LANG_CHG, NULL);
}
#endif
/**
* Set the behavior of the label with longer text then the object size
* @param label pointer to a label object
* @param long_mode the new mode from 'lv_label_long_mode' enum.
* In LV_LONG_BREAK/LONG/ROLL the size of the label should be set AFTER this function
*/
void lv_label_set_long_mode(lv_obj_t * label, lv_label_long_mode_t long_mode)
{
lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
#if USE_LV_ANIMATION
/*Delete the old animation (if exists)*/
lv_anim_del(label, (lv_anim_fp_t) lv_obj_set_x);
lv_anim_del(label, (lv_anim_fp_t) lv_obj_set_y);
lv_anim_del(label, (lv_anim_fp_t) lv_label_set_offset_x);
lv_anim_del(label, (lv_anim_fp_t) lv_label_set_offset_y);
#endif
ext->offset.x = 0;
ext->offset.y = 0;
if(long_mode == LV_LABEL_LONG_ROLL || long_mode == LV_LABEL_LONG_CROP) ext->expand = 1;
else ext->expand = 0;
/*Restore the character under the dots*/
if(ext->long_mode == LV_LABEL_LONG_DOT && ext->dot_end != LV_LABEL_DOT_END_INV) {
lv_label_revert_dots(label);
}
ext->long_mode = long_mode;
lv_label_refr_text(label);
}
/**
* Set the align of the label (left or center)
* @param label pointer to a label object
* @param align 'LV_LABEL_ALIGN_LEFT' or 'LV_LABEL_ALIGN_LEFT'
*/
void lv_label_set_align(lv_obj_t * label, lv_label_align_t align)
{
lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
if(ext->align == align) return;
ext->align = align;
lv_obj_invalidate(label); /*Enough to invalidate because alignment is only drawing related (lv_refr_label_text() not required)*/
}
/**
* Enable the recoloring by in-line commands
* @param label pointer to a label object
* @param en true: enable recoloring, false: disable
*/
void lv_label_set_recolor(lv_obj_t * label, bool en)
{
lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
if(ext->recolor == en) return;
ext->recolor = en == false ? 0 : 1;
lv_label_refr_text(label); /*Refresh the text because the potential colo codes in text needs to be hided or revealed*/
}
/**
* Set the label to draw (or not draw) background specified in its style's body
* @param label pointer to a label object
* @param en true: draw body; false: don't draw body
*/
void lv_label_set_body_draw(lv_obj_t * label, bool en)
{
lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
if(ext->body_draw == en) return;
ext->body_draw = en == false ? 0 : 1;
lv_obj_refresh_ext_size(label);
lv_obj_invalidate(label);
}
/**
* Set the label's animation speed in LV_LABEL_LONG_ROLL and SCROLL modes
* @param label pointer to a label object
* @param anim_speed speed of animation in px/sec unit
*/
void lv_label_set_anim_speed(lv_obj_t * label, uint16_t anim_speed)
{
lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
if(ext->anim_speed == anim_speed) return;
ext->anim_speed = anim_speed;
if(ext->long_mode == LV_LABEL_LONG_ROLL || ext->long_mode == LV_LABEL_LONG_SCROLL) {
lv_label_refr_text(label);
}
}
/*=====================
* Getter functions
*====================*/
/**
* Get the text of a label
* @param label pointer to a label object
* @return the text of the label
*/
char * lv_label_get_text(const lv_obj_t * label)
{
lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
return ext->text;
}
#if USE_LV_MULTI_LANG
/**
* Get the text ID of the label. (Used by the multi-language feature)
* @param label pointer to a label object
* @return ID of the text
*/
uint16_t lv_label_get_text_id(lv_obj_t * label)
{
lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
return ext->lang_txt_id;
}
#endif
/**
* Get the long mode of a label
* @param label pointer to a label object
* @return the long mode
*/
lv_label_long_mode_t lv_label_get_long_mode(const lv_obj_t * label)
{
lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
return ext->long_mode;
}
/**
* Get the align attribute
* @param label pointer to a label object
* @return LV_LABEL_ALIGN_LEFT or LV_LABEL_ALIGN_CENTER
*/
lv_label_align_t lv_label_get_align(const lv_obj_t * label)
{
lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
return ext->align;
}
/**
* Get the recoloring attribute
* @param label pointer to a label object
* @return true: recoloring is enabled, false: disable
*/
bool lv_label_get_recolor(const lv_obj_t * label)
{
lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
return ext->recolor == 0 ? false : true;
}
/**
* Get the body draw attribute
* @param label pointer to a label object
* @return true: draw body; false: don't draw body
*/
bool lv_label_get_body_draw(const lv_obj_t * label)
{
lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
return ext->body_draw == 0 ? false : true;
}
/**
* Get the label's animation speed in LV_LABEL_LONG_ROLL and SCROLL modes
* @param label pointer to a label object
* @return speed of animation in px/sec unit
*/
uint16_t lv_label_get_anim_speed(const lv_obj_t * label)
{
lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
return ext->anim_speed;
}
/**
* Get the relative x and y coordinates of a letter
* @param label pointer to a label object
* @param index index of the letter [0 ... text length]. 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)
*/
void lv_label_get_letter_pos(const lv_obj_t * label, uint16_t index, lv_point_t * pos)
{
const char * txt = lv_label_get_text(label);
lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
uint32_t line_start = 0;
uint32_t new_line_start = 0;
lv_coord_t max_w = lv_obj_get_width(label);
lv_style_t * style = lv_obj_get_style(label);
const lv_font_t * font = style->text.font;
uint8_t letter_height = lv_font_get_height(font);
lv_coord_t y = 0;
lv_txt_flag_t flag = LV_TXT_FLAG_NONE;
if(ext->recolor != 0) flag |= LV_TXT_FLAG_RECOLOR;
if(ext->expand != 0) flag |= LV_TXT_FLAG_EXPAND;
if(ext->align == LV_LABEL_ALIGN_CENTER) flag |= LV_TXT_FLAG_CENTER;
/*If the width will be expanded the set the max length to very big */
if(ext->long_mode == LV_LABEL_LONG_EXPAND || ext->long_mode == LV_LABEL_LONG_SCROLL) {
max_w = LV_COORD_MAX;
}
index = lv_txt_encoded_get_byte_id(txt, index);
/*Search the line of the index letter */;
while(txt[new_line_start] != '\0') {
new_line_start += lv_txt_get_next_line(&txt[line_start], font, style->text.letter_space, max_w, flag);
if(index < new_line_start || txt[new_line_start] == '\0') break; /*The line of 'index' letter begins at 'line_start'*/
y += letter_height + style->text.line_space;
line_start = new_line_start;
}
/*If the last character is line break then go to the next line*/
if(index > 0) {
if((txt[index - 1] == '\n' || txt[index - 1] == '\r') && txt[index] == '\0') {
y += letter_height + style->text.line_space;
line_start = index;
}
}
/*Calculate the x coordinate*/
lv_coord_t x = lv_txt_get_width(&txt[line_start], index - line_start,
font, style->text.letter_space, flag);
if(index != line_start) x += style->text.letter_space;
if(ext->align == LV_LABEL_ALIGN_CENTER) {
lv_coord_t line_w;
line_w = lv_txt_get_width(&txt[line_start], new_line_start - line_start,
font, style->text.letter_space, flag);
x += lv_obj_get_width(label) / 2 - line_w / 2;
} else if(ext->align == LV_LABEL_ALIGN_RIGHT) {
lv_coord_t line_w;
line_w = lv_txt_get_width(&txt[line_start], new_line_start - line_start,
font, style->text.letter_space, flag);
x += lv_obj_get_width(label) - line_w;
}
pos->x = x;
pos->y = y;
}
/**
* Get the index of letter on a relative point of a label
* @param label 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)
* Expressed in character index and not byte index (different in UTF-8)
*/
uint16_t lv_label_get_letter_on(const lv_obj_t * label, lv_point_t * pos)
{
const char * txt = lv_label_get_text(label);
lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
uint32_t line_start = 0;
uint32_t new_line_start = 0;
lv_coord_t max_w = lv_obj_get_width(label);
lv_style_t * style = lv_obj_get_style(label);
const lv_font_t * font = style->text.font;
uint8_t letter_height = lv_font_get_height(font);
lv_coord_t y = 0;
lv_txt_flag_t flag = LV_TXT_FLAG_NONE;
if(ext->recolor != 0) flag |= LV_TXT_FLAG_RECOLOR;
if(ext->expand != 0) flag |= LV_TXT_FLAG_EXPAND;
if(ext->align == LV_LABEL_ALIGN_CENTER) flag |= LV_TXT_FLAG_CENTER;
/*If the width will be expanded set the max length to very big */
if(ext->long_mode == LV_LABEL_LONG_EXPAND || ext->long_mode == LV_LABEL_LONG_SCROLL) {
max_w = LV_COORD_MAX;
}
/*Search the line of the index letter */;
while(txt[line_start] != '\0') {
new_line_start += lv_txt_get_next_line(&txt[line_start], font, style->text.letter_space, max_w, flag);
if(pos->y <= y + letter_height) break; /*The line is found (stored in 'line_start')*/
y += letter_height + style->text.line_space;
line_start = new_line_start;
}
/*Calculate the x coordinate*/
lv_coord_t x = 0;
if(ext->align == LV_LABEL_ALIGN_CENTER) {
lv_coord_t line_w;
line_w = lv_txt_get_width(&txt[line_start], new_line_start - line_start,
font, style->text.letter_space, flag);
x += lv_obj_get_width(label) / 2 - line_w / 2;
}
lv_txt_cmd_state_t cmd_state = LV_TXT_CMD_STATE_WAIT;
uint32_t i = line_start;
uint32_t i_current = i;
uint32_t letter;
while(i < new_line_start - 1) {
letter = lv_txt_encoded_next(txt, &i); /*Be careful 'i' already points to the next character*/
/*Handle the recolor command*/
if((flag & LV_TXT_FLAG_RECOLOR) != 0) {
if(lv_txt_is_cmd(&cmd_state, txt[i]) != false) {
continue; /*Skip the letter is it is part of a command*/
}
}
x += lv_font_get_width(font, letter);
if(pos->x < x) {
i = i_current;
break;
}
x += style->text.letter_space;
i_current = i;
}
return lv_encoded_get_char_id(txt, i);
}
/*=====================
* Other functions
*====================*/
/**
* Insert a text to the label. The label text can not be static.
* @param label pointer to a label object
* @param pos character index to insert. Expressed in character index and not byte index (Different in UTF-8)
* 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 * label, uint32_t pos, const char * txt)
{
lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
/*Can not append to static text*/
if(ext->static_txt != 0) return;
lv_obj_invalidate(label);
/*Allocate space for the new text*/
uint32_t old_len = strlen(ext->text);
uint32_t ins_len = strlen(txt);
uint32_t new_len = ins_len + old_len;
ext->text = lv_mem_realloc(ext->text, new_len + 1);
lv_mem_assert(ext->text);
if(ext->text == NULL) return;
if(pos == LV_LABEL_POS_LAST) {
#if LV_TXT_UTF8 == 0
pos = old_len;
#else
pos = lv_txt_get_encoded_length(ext->text);
#endif
}
lv_txt_ins(ext->text, pos, txt);
lv_label_refr_text(label);
}
/**
* Delete characters from a label. The label text can not be static.
* @param label pointer to a label object
* @param pos character index to insert. Expressed in character index and not byte index (Different in UTF-8)
* 0: before first char.
* @param cnt number of characters to cut
*/
void lv_label_cut_text(lv_obj_t * label, uint32_t pos, uint32_t cnt)
{
lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
/*Can not append to static text*/
if(ext->static_txt != 0) return;
lv_obj_invalidate(label);
char * label_txt = lv_label_get_text(label);
/*Delete the characters*/
lv_txt_cut(label_txt, pos, cnt);
/*Refresh the label*/
lv_label_refr_text(label);
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Handle the drawing related tasks of the labels
* @param label pointer to a label object
* @param mask the object will be drawn only in this area
* @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area
* (return 'true' if yes)
* LV_DESIGN_DRAW: draw the object (always return 'true')
* LV_DESIGN_DRAW_POST: drawing after every children are drawn
* @param return true/false, depends on 'mode'
*/
static bool lv_label_design(lv_obj_t * label, const lv_area_t * mask, lv_design_mode_t mode)
{
/* A label never covers an area */
if(mode == LV_DESIGN_COVER_CHK) return false;
else if(mode == LV_DESIGN_DRAW_MAIN) {
lv_area_t coords;
lv_style_t * style = lv_obj_get_style(label);
lv_opa_t opa_scale = lv_obj_get_opa_scale(label);
lv_obj_get_coords(label, &coords);
#if USE_LV_GROUP
lv_group_t * g = lv_obj_get_group(label);
if(lv_group_get_focused(g) == label) {
lv_draw_rect(&coords, mask, style, opa_scale);
}
#endif
lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
if(ext->body_draw) {
lv_area_t bg;
lv_obj_get_coords(label, &bg);
bg.x1 -= style->body.padding.hor;
bg.x2 += style->body.padding.hor;
bg.y1 -= style->body.padding.ver;
bg.y2 += style->body.padding.ver;
lv_draw_rect(&bg, mask, style, lv_obj_get_opa_scale(label));
}
/*TEST: draw a background for the label*/
//lv_draw_rect(&label->coords, mask, &lv_style_plain_color, LV_OPA_COVER);
lv_txt_flag_t flag = LV_TXT_FLAG_NONE;
if(ext->recolor != 0) flag |= LV_TXT_FLAG_RECOLOR;
if(ext->expand != 0) flag |= LV_TXT_FLAG_EXPAND;
if(ext->align == LV_LABEL_ALIGN_CENTER) flag |= LV_TXT_FLAG_CENTER;
if(ext->align == LV_LABEL_ALIGN_RIGHT) flag |= LV_TXT_FLAG_RIGHT;
/* In ROLL mode the CENTER and RIGHT are pointless so remove them.
* (In addition they will result mis-alignment is this case)*/
if((ext->long_mode == LV_LABEL_LONG_ROLL) &&
(ext->align == LV_LABEL_ALIGN_CENTER || ext->align == LV_LABEL_ALIGN_RIGHT)) {
lv_point_t size;
lv_txt_get_size(&size, ext->text, style->text.font, style->text.letter_space, style->text.line_space, LV_COORD_MAX, flag);
if(size.x > lv_obj_get_width(label)) {
flag &= ~LV_TXT_FLAG_RIGHT;
flag &= ~LV_TXT_FLAG_CENTER;
}
}
lv_draw_label(&coords, mask, style, opa_scale, ext->text, flag, &ext->offset);
}
return true;
}
/**
* Signal function of the label
* @param label pointer to a label object
* @param sign a signal type from lv_signal_t enum
* @param param pointer to a signal specific variable
* @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
*/
static lv_res_t lv_label_signal(lv_obj_t * label, lv_signal_t sign, void * param)
{
lv_res_t res;
/* Include the ancient signal function */
res = ancestor_signal(label, sign, param);
if(res != LV_RES_OK) return res;
lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
if(sign == LV_SIGNAL_CLEANUP) {
if(ext->static_txt == 0) {
lv_mem_free(ext->text);
ext->text = NULL;
}
} else if(sign == LV_SIGNAL_STYLE_CHG) {
/*Revert dots for proper refresh*/
lv_label_revert_dots(label);
lv_label_refr_text(label);
} else if(sign == LV_SIGNAL_CORD_CHG) {
if(lv_area_get_width(&label->coords) != lv_area_get_width(param) ||
lv_area_get_height(&label->coords) != lv_area_get_height(param)) {
lv_label_revert_dots(label);
lv_label_refr_text(label);
}
} else if(sign == LV_SIGNAL_REFR_EXT_SIZE) {
if(ext->body_draw) {
lv_style_t * style = lv_label_get_style(label);
label->ext_size = LV_MATH_MAX(label->ext_size, style->body.padding.hor);
label->ext_size = LV_MATH_MAX(label->ext_size, style->body.padding.ver);
}
} else if(sign == LV_SIGNAL_LANG_CHG) {
#if USE_LV_MULTI_LANG
if(ext->lang_txt_id != LV_LANG_TXT_ID_NONE) {
const char * lang_txt = lv_lang_get_text(ext->lang_txt_id);
if(lang_txt) {
lv_label_set_text(label, lang_txt);
} else {
LV_LOG_WARN("lv_lang_get_text return NULL for a label's text");
}
}
#endif
} else if(sign == LV_SIGNAL_GET_TYPE) {
lv_obj_type_t * buf = param;
uint8_t i;
for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/
if(buf->type[i] == NULL) break;
}
buf->type[i] = "lv_label";
}
return res;
}
/**
* Refresh the label with its text stored in its extended data
* @param label pointer to a label object
*/
static void lv_label_refr_text(lv_obj_t * label)
{
lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
if(ext->text == NULL) return;
lv_coord_t max_w = lv_obj_get_width(label);
lv_style_t * style = lv_obj_get_style(label);
const lv_font_t * font = style->text.font;
/*If the width will be expanded set the max length to very big */
if(ext->long_mode == LV_LABEL_LONG_EXPAND ||
ext->long_mode == LV_LABEL_LONG_SCROLL) {
max_w = LV_COORD_MAX;
}
/*Calc. the height and longest line*/
lv_point_t size;
lv_txt_flag_t flag = LV_TXT_FLAG_NONE;
if(ext->recolor != 0) flag |= LV_TXT_FLAG_RECOLOR;
if(ext->expand != 0) flag |= LV_TXT_FLAG_EXPAND;
lv_txt_get_size(&size, ext->text, font, style->text.letter_space, style->text.line_space, max_w, flag);
/*Set the full size in expand mode*/
if(ext->long_mode == LV_LABEL_LONG_EXPAND || ext->long_mode == LV_LABEL_LONG_SCROLL) {
lv_obj_set_size(label, size.x, size.y);
/*Start scrolling if the label is greater then its parent*/
if(ext->long_mode == LV_LABEL_LONG_SCROLL) {
#if USE_LV_ANIMATION
lv_obj_t * parent = lv_obj_get_parent(label);
/*Delete the potential previous scroller animations*/
lv_anim_del(label, (lv_anim_fp_t) lv_obj_set_x);
lv_anim_del(label, (lv_anim_fp_t) lv_obj_set_y);
lv_anim_t anim;
anim.var = label;
anim.repeat = 1;
anim.playback = 1;
anim.start = 0;
anim.act_time = 0;
anim.end_cb = NULL;
anim.path = lv_anim_path_linear;
anim.playback_pause = (((lv_font_get_width(style->text.font, ' ') +
style->text.letter_space) * 1000) / ext->anim_speed) * ANIM_WAIT_CHAR_COUNT;
anim.repeat_pause = anim.playback_pause;
if(lv_obj_get_width(label) > lv_obj_get_width(parent)) {
anim.end = lv_obj_get_width(parent) - lv_obj_get_width(label);
anim.fp = (lv_anim_fp_t) lv_obj_set_x;
anim.time = lv_anim_speed_to_time(ext->anim_speed, anim.start, anim.end);
lv_anim_create(&anim);
} else if(lv_obj_get_height(label) > lv_obj_get_height(parent)) {
anim.end = lv_obj_get_height(parent) - lv_obj_get_height(label) - lv_font_get_height(font);
anim.fp = (lv_anim_fp_t)lv_obj_set_y;
anim.time = lv_anim_speed_to_time(ext->anim_speed, anim.start, anim.end);
lv_anim_create(&anim);
}
#endif
}
}
/*In roll mode keep the size but start offset animations*/
else if(ext->long_mode == LV_LABEL_LONG_ROLL) {
#if USE_LV_ANIMATION
lv_anim_t anim;
anim.var = label;
anim.repeat = 1;
anim.playback = 1;
anim.start = 0;
anim.act_time = 0;
anim.end_cb = NULL;
anim.path = lv_anim_path_linear;
anim.playback_pause = (((lv_font_get_width(style->text.font, ' ') + style->text.letter_space) * 1000) / ext->anim_speed) * ANIM_WAIT_CHAR_COUNT;
anim.repeat_pause = anim.playback_pause;
bool hor_anim = false;
if(size.x > lv_obj_get_width(label)) {
anim.end = lv_obj_get_width(label) - size.x;
anim.fp = (lv_anim_fp_t) lv_label_set_offset_x;
anim.time = lv_anim_speed_to_time(ext->anim_speed, anim.start, anim.end);
lv_anim_create(&anim);
hor_anim = true;
} else {
/*Delete the offset animation if not required*/
lv_anim_del(label, (lv_anim_fp_t) lv_label_set_offset_x);
ext->offset.x = 0;
}
if(size.y > lv_obj_get_height(label) && hor_anim == false) {
anim.end = lv_obj_get_height(label) - size.y - (lv_font_get_height(font));
anim.fp = (lv_anim_fp_t)lv_label_set_offset_y;
anim.time = lv_anim_speed_to_time(ext->anim_speed, anim.start, anim.end);
lv_anim_create(&anim);
} else {
/*Delete the offset animation if not required*/
lv_anim_del(label, (lv_anim_fp_t) lv_label_set_offset_y);
ext->offset.y = 0;
}
#endif
} else if(ext->long_mode == LV_LABEL_LONG_DOT) {
if(size.y <= lv_obj_get_height(label)) { /*No dots are required, the text is short enough*/
ext->dot_end = LV_LABEL_DOT_END_INV;
} else if(lv_txt_get_encoded_length(ext->text) <= LV_LABEL_DOT_NUM) { /*Don't turn to dots all the characters*/
ext->dot_end = LV_LABEL_DOT_END_INV;
} else {
lv_point_t p;
p.x = lv_obj_get_width(label) - (lv_font_get_width(style->text.font, '.') + style->text.letter_space) * LV_LABEL_DOT_NUM; /*Shrink with dots*/
p.y = lv_obj_get_height(label);
p.y -= p.y % (lv_font_get_height(style->text.font) + style->text.line_space); /*Round down to the last line*/
p.y -= style->text.line_space; /*Trim the last line space*/
uint32_t letter_id = lv_label_get_letter_on(label, &p);
#if LV_TXT_UTF8 == 0
/*Save letters under the dots and replace them with dots*/
uint8_t i;
for(i = 0; i < LV_LABEL_DOT_NUM; i++) {
ext->dot_tmp[i] = ext->text[letter_id + i];
ext->text[letter_id + i] = '.';
}
ext->dot_tmp[LV_LABEL_DOT_NUM] = ext->text[letter_id + LV_LABEL_DOT_NUM];
ext->text[letter_id + LV_LABEL_DOT_NUM] = '\0';
ext->dot_end = letter_id + LV_LABEL_DOT_NUM;
#else
/*Save letters under the dots and replace them with dots*/
uint32_t i;
uint32_t byte_id = lv_txt_encoded_get_byte_id(ext->text, letter_id);
uint32_t byte_id_ori = byte_id;
uint8_t len = 0;
for(i = 0; i <= LV_LABEL_DOT_NUM; i++) {
len += lv_txt_encoded_size(&ext->text[byte_id]);
lv_txt_encoded_next(ext->text, &byte_id);
}
memcpy(ext->dot_tmp, &ext->text[byte_id_ori], len);
ext->dot_tmp[len] = '\0'; /*Close with a zero*/
for(i = 0; i < LV_LABEL_DOT_NUM; i++) {
ext->text[byte_id_ori + i] = '.';
}
ext->text[byte_id_ori + LV_LABEL_DOT_NUM] = '\0';
ext->dot_end = letter_id + LV_LABEL_DOT_NUM;
#endif
}
}
/*In break mode only the height can change*/
else if(ext->long_mode == LV_LABEL_LONG_BREAK) {
lv_obj_set_height(label, size.y);
}
/*Do not set the size in Clip mode*/
else if(ext->long_mode == LV_LABEL_LONG_CROP) {
/*Do nothing*/
}
lv_obj_invalidate(label);
}
static void lv_label_revert_dots(lv_obj_t * label)
{
lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
if(ext->long_mode != LV_LABEL_LONG_DOT) return;
if(ext->dot_end == LV_LABEL_DOT_END_INV) return;
#if LV_TXT_UTF8 == 0
uint32_t i;
for(i = 0; i <= LV_LABEL_DOT_NUM; i++) {
ext->text[ext->dot_end - i] = ext->dot_tmp[LV_LABEL_DOT_NUM - i];
}
#else
uint32_t letter_i = ext->dot_end - LV_LABEL_DOT_NUM;
uint32_t byte_i = lv_txt_encoded_get_byte_id(ext->text, letter_i);
/*Restore the characters*/
uint8_t i = 0;
while(ext->dot_tmp[i] != '\0') {
ext->text[byte_i + i] = ext->dot_tmp[i];
i++;
}
#endif
ext->dot_end = LV_LABEL_DOT_END_INV;
}
#if USE_LV_ANIMATION
static void lv_label_set_offset_x(lv_obj_t * label, lv_coord_t x)
{
lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
ext->offset.x = x;
lv_obj_invalidate(label);
}
static void lv_label_set_offset_y(lv_obj_t * label, lv_coord_t y)
{
lv_label_ext_t * ext = lv_obj_get_ext_attr(label);
ext->offset.y = y;
lv_obj_invalidate(label);
}
#endif
#endif

View File

@@ -0,0 +1,295 @@
/**
* @file lv_rect.h
*
*/
#ifndef LV_LABEL_H
#define LV_LABEL_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#ifdef LV_CONF_INCLUDE_SIMPLE
#include "lv_conf.h"
#else
#include "../../lv_conf.h"
#endif
#if USE_LV_LABEL != 0
#include "../lv_core/lv_obj.h"
#include "../lv_misc/lv_font.h"
#include "../lv_misc/lv_txt.h"
#include "../lv_misc/lv_symbol_def.h"
/*********************
* DEFINES
*********************/
#define LV_LABEL_DOT_NUM 3
#define LV_LABEL_POS_LAST 0xFFFF
/**********************
* TYPEDEFS
**********************/
/*Long mode behaviors. Used in 'lv_label_ext_t' */
enum
{
LV_LABEL_LONG_EXPAND, /*Expand the object size to the text size*/
LV_LABEL_LONG_BREAK, /*Keep the object width, break the too long lines and expand the object height*/
LV_LABEL_LONG_SCROLL, /*Expand the object size and scroll the text on the parent (move the label object)*/
LV_LABEL_LONG_DOT, /*Keep the size and write dots at the end if the text is too long*/
LV_LABEL_LONG_ROLL, /*Keep the size and roll the text infinitely*/
LV_LABEL_LONG_CROP, /*Keep the size and crop the text out of it*/
};
typedef uint8_t lv_label_long_mode_t;
/*Label align policy*/
enum {
LV_LABEL_ALIGN_LEFT,
LV_LABEL_ALIGN_CENTER,
LV_LABEL_ALIGN_RIGHT,
};
typedef uint8_t lv_label_align_t;
/*Data of label*/
typedef struct
{
/*Inherited from 'base_obj' so no inherited ext.*/ /*Ext. of ancestor*/
/*New data for this type */
char * text; /*Text of the label*/
lv_label_long_mode_t long_mode; /*Determinate what to do with the long texts*/
#if LV_TXT_UTF8 == 0
char dot_tmp[LV_LABEL_DOT_NUM + 1]; /*Store the character which are replaced by dots (Handled by the library)*/
#else
char dot_tmp[LV_LABEL_DOT_NUM * 4 + 1]; /*Store the character which are replaced by dots (Handled by the library)*/
#endif
#if USE_LV_MULTI_LANG
uint16_t lang_txt_id; /*The ID of the text to display*/
#endif
uint16_t dot_end; /*The text end position in dot mode (Handled by the library)*/
uint16_t anim_speed; /*Speed of scroll and roll animation in px/sec unit*/
lv_point_t offset; /*Text draw position offset*/
uint8_t static_txt :1; /*Flag to indicate the text is static*/
uint8_t align :2; /*Align type from 'lv_label_align_t'*/
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_ROLL)*/
uint8_t body_draw :1; /*Draw background body*/
} lv_label_ext_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a label objects
* @param par pointer to an object, it will be the parent of the new label
* @param copy pointer to a button object, if not NULL then the new object will be copied from it
* @return pointer to the created button
*/
lv_obj_t * lv_label_create(lv_obj_t * par, const lv_obj_t * copy);
/*=====================
* Setter functions
*====================*/
/**
* Set a new text for a label. Memory will be allocated to store the text by the label.
* @param label 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 * label, const char * text);
/**
* Set a new text for a label from a character array. The array don't has to be '\0' terminated.
* Memory will be allocated to store the array by the label.
* @param label pointer to a label object
* @param array array of characters or NULL to refresh the label
* @param size the size of 'array' in bytes
*/
void lv_label_set_array_text(lv_obj_t * label, const char * array, uint16_t size);
/**
* Set a static text. It will not be saved by the label so the 'text' variable
* has to be 'alive' while the label exist.
* @param label pointer to a label object
* @param text pointer to a text. NULL to refresh with the current text.
*/
void lv_label_set_static_text(lv_obj_t * label, const char * text);
/**
*Set a text ID which means a the same text but on different languages
* @param label pointer to a label object
* @param txt_id ID of the text
*/
#if USE_LV_MULTI_LANG
void lv_label_set_text_id(lv_obj_t * label, uint32_t txt_id);
#endif
/**
* Set the behavior of the label with longer text then the object size
* @param label pointer to a label object
* @param long_mode the new mode from 'lv_label_long_mode' enum.
* In LV_LONG_BREAK/LONG/ROLL the size of the label should be set AFTER this function
*/
void lv_label_set_long_mode(lv_obj_t * label, lv_label_long_mode_t long_mode);
/**
* Set the align of the label (left or center)
* @param label pointer to a label object
* @param align 'LV_LABEL_ALIGN_LEFT' or 'LV_LABEL_ALIGN_LEFT'
*/
void lv_label_set_align(lv_obj_t *label, lv_label_align_t align);
/**
* Enable the recoloring by in-line commands
* @param label pointer to a label object
* @param en true: enable recoloring, false: disable
*/
void lv_label_set_recolor(lv_obj_t * label, bool en);
/**
* Set the label to draw (or not draw) background specified in its style's body
* @param label pointer to a label object
* @param en true: draw body; false: don't draw body
*/
void lv_label_set_body_draw(lv_obj_t *label, bool en);
/**
* Set the label's animation speed in LV_LABEL_LONG_ROLL and SCROLL modes
* @param label pointer to a label object
* @param anim_speed speed of animation in px/sec unit
*/
void lv_label_set_anim_speed(lv_obj_t *label, uint16_t anim_speed);
/**
* Set the style of an label
* @param label pointer to an label object
* @param style pointer to a style
*/
static inline void lv_label_set_style(lv_obj_t *label, lv_style_t *style)
{
lv_obj_set_style(label, style);
}
/*=====================
* Getter functions
*====================*/
/**
* Get the text of a label
* @param label pointer to a label object
* @return the text of the label
*/
char * lv_label_get_text(const lv_obj_t * label);
#if USE_LV_MULTI_LANG
/**
* Get the text ID of the label. (Used by the multi-language feature)
* @param label pointer to a label object
* @return ID of the text
*/
uint16_t lv_label_get_text_id(lv_obj_t * label);
#endif
/**
* Get the long mode of a label
* @param label pointer to a label object
* @return the long mode
*/
lv_label_long_mode_t lv_label_get_long_mode(const lv_obj_t * label);
/**
* Get the align attribute
* @param label pointer to a label object
* @return LV_LABEL_ALIGN_LEFT or LV_LABEL_ALIGN_CENTER
*/
lv_label_align_t lv_label_get_align(const lv_obj_t * label);
/**
* Get the recoloring attribute
* @param label pointer to a label object
* @return true: recoloring is enabled, false: disable
*/
bool lv_label_get_recolor(const lv_obj_t * label);
/**
* Get the body draw attribute
* @param label pointer to a label object
* @return true: draw body; false: don't draw body
*/
bool lv_label_get_body_draw(const lv_obj_t *label);
/**
* Get the label's animation speed in LV_LABEL_LONG_ROLL and SCROLL modes
* @param label pointer to a label object
* @return speed of animation in px/sec unit
*/
uint16_t lv_label_get_anim_speed(const lv_obj_t *label);
/**
* Get the relative x and y coordinates of a letter
* @param label pointer to a label object
* @param index index of the letter [0 ... text length]. 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)
*/
void lv_label_get_letter_pos(const lv_obj_t * label, uint16_t index, lv_point_t * pos);
/**
* Get the index of letter on a relative point of a label
* @param label 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)
* Expressed in character index and not byte index (different in UTF-8)
*/
uint16_t lv_label_get_letter_on(const lv_obj_t * label, lv_point_t * pos);
/**
* Get the style of an label object
* @param label pointer to an label object
* @return pointer to the label's style
*/
static inline lv_style_t* lv_label_get_style(const lv_obj_t *label)
{
return lv_obj_get_style(label);
}
/*=====================
* Other functions
*====================*/
/**
* Insert a text to the label. The label text can not be static.
* @param label pointer to a label object
* @param pos character index to insert. Expressed in character index and not byte index (Different in UTF-8)
* 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 * label, uint32_t pos, const char * txt);
/**
* Delete characters from a label. The label text can not be static.
* @param label pointer to a label object
* @param pos character index to insert. Expressed in character index and not byte index (Different in UTF-8)
* 0: before first char.
* @param cnt number of characters to cut
*/
void lv_label_cut_text(lv_obj_t * label, uint32_t pos, uint32_t cnt);
/**********************
* MACROS
**********************/
#endif /*USE_LV_LABEL*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_LABEL_H*/

View File

@@ -0,0 +1,244 @@
/**
* @file lv_led.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_led.h"
#if USE_LV_LED != 0
#include "../lv_themes/lv_theme.h"
#include "../lv_draw/lv_draw.h"
/*********************
* DEFINES
*********************/
#define LV_LED_WIDTH_DEF (LV_DPI / 3)
#define LV_LED_HEIGHT_DEF (LV_DPI / 3)
#define LV_LED_BRIGHT_OFF 100
#define LV_LED_BRIGHT_ON 255
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static bool lv_led_design(lv_obj_t * led, const lv_area_t * mask, lv_design_mode_t mode);
static lv_res_t lv_led_signal(lv_obj_t * led, lv_signal_t sign, void * param);
/**********************
* STATIC VARIABLES
**********************/
static lv_design_func_t ancestor_design_f;
static lv_signal_func_t ancestor_signal;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Create a led objects
* @param par pointer to an object, it will be the parent of the new led
* @param copy pointer to a led object, if not NULL then the new object will be copied from it
* @return pointer to the created led
*/
lv_obj_t * lv_led_create(lv_obj_t * par, const lv_obj_t * copy)
{
LV_LOG_TRACE("led create started");
/*Create the ancestor basic object*/
lv_obj_t * new_led = lv_obj_create(par, copy);
lv_mem_assert(new_led);
if(new_led == NULL) return NULL;
if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_led);
if(ancestor_design_f == NULL) ancestor_design_f = lv_obj_get_design_func(new_led);
/*Allocate the object type specific extended data*/
lv_led_ext_t * ext = lv_obj_allocate_ext_attr(new_led, sizeof(lv_led_ext_t));
lv_mem_assert(ext);
if(ext == NULL) return NULL;
ext->bright = LV_LED_BRIGHT_ON;
lv_obj_set_signal_func(new_led, lv_led_signal);
lv_obj_set_design_func(new_led, lv_led_design);
/*Init the new led object*/
if(copy == NULL) {
lv_obj_set_size(new_led, LV_LED_WIDTH_DEF, LV_LED_HEIGHT_DEF);
/*Set the default styles*/
lv_theme_t * th = lv_theme_get_current();
if(th) {
lv_led_set_style(new_led, th->led);
} else {
lv_led_set_style(new_led, &lv_style_pretty_color);
}
}
/*Copy an existing object*/
else {
lv_led_ext_t * copy_ext = lv_obj_get_ext_attr(copy);
ext->bright = copy_ext->bright;
/*Refresh the style with new signal function*/
lv_obj_refresh_style(new_led);
}
LV_LOG_INFO("led created");
return new_led;
}
/*=====================
* Setter functions
*====================*/
/**
* Set the brightness of a LED object
* @param led pointer to a LED object
* @param bright 0 (max. dark) ... 255 (max. light)
*/
void lv_led_set_bright(lv_obj_t * led, uint8_t bright)
{
/*Set the brightness*/
lv_led_ext_t * ext = lv_obj_get_ext_attr(led);
if(ext->bright == bright) return;
ext->bright = bright;
/*Invalidate the object there fore it will be redrawn*/
lv_obj_invalidate(led);
}
/**
* Light on a LED
* @param led pointer to a LED object
*/
void lv_led_on(lv_obj_t * led)
{
lv_led_set_bright(led, LV_LED_BRIGHT_ON);
}
/**
* Light off a LED
* @param led pointer to a LED object
*/
void lv_led_off(lv_obj_t * led)
{
lv_led_set_bright(led, LV_LED_BRIGHT_OFF);
}
/**
* Toggle the state of a LED
* @param led pointer to a LED object
*/
void lv_led_toggle(lv_obj_t * led)
{
uint8_t bright = lv_led_get_bright(led);
if(bright > (LV_LED_BRIGHT_OFF + LV_LED_BRIGHT_ON) >> 1) lv_led_off(led);
else lv_led_on(led);
}
/*=====================
* Getter functions
*====================*/
/**
* Get the brightness of a LEd object
* @param led pointer to LED object
* @return bright 0 (max. dark) ... 255 (max. light)
*/
uint8_t lv_led_get_bright(const lv_obj_t * led)
{
lv_led_ext_t * ext = lv_obj_get_ext_attr(led);
return ext->bright;
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Handle the drawing related tasks of the leds
* @param led pointer to an object
* @param mask the object will be drawn only in this area
* @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area
* (return 'true' if yes)
* LV_DESIGN_DRAW: draw the object (always return 'true')
* LV_DESIGN_DRAW_POST: drawing after every children are drawn
* @param return true/false, depends on 'mode'
*/
static bool lv_led_design(lv_obj_t * led, const lv_area_t * mask, lv_design_mode_t mode)
{
if(mode == LV_DESIGN_COVER_CHK) {
/*Return false if the object is not covers the mask area*/
return ancestor_design_f(led, mask, mode);
} else if(mode == LV_DESIGN_DRAW_MAIN) {
/*Make darker colors in a temporary style according to the brightness*/
lv_led_ext_t * ext = lv_obj_get_ext_attr(led);
lv_style_t * style = lv_obj_get_style(led);
/* Store the real pointer because of 'lv_group'
* If the object is in focus 'lv_obj_get_style()' will give a pointer to tmp style
* and to the real object style. It is important because of style change tricks below*/
lv_style_t * style_ori_p = led->style_p;
/*Create a temporal style*/
lv_style_t leds_tmp;
memcpy(&leds_tmp, style, sizeof(leds_tmp));
/*Mix. the color with black proportionally with brightness*/
leds_tmp.body.main_color = lv_color_mix(leds_tmp.body.main_color, LV_COLOR_BLACK, ext->bright);
leds_tmp.body.grad_color = lv_color_mix(leds_tmp.body.grad_color, LV_COLOR_BLACK, ext->bright);
leds_tmp.body.border.color = lv_color_mix(leds_tmp.body.border.color, LV_COLOR_BLACK, ext->bright);
/*Set the current swidth according to brightness proportionally between LV_LED_BRIGHT_OFF and LV_LED_BRIGHT_ON*/
uint16_t bright_tmp = ext->bright;
leds_tmp.body.shadow.width = ((bright_tmp - LV_LED_BRIGHT_OFF) * style->body.shadow.width) / (LV_LED_BRIGHT_ON - LV_LED_BRIGHT_OFF);
led->style_p = &leds_tmp;
ancestor_design_f(led, mask, mode);
led->style_p = style_ori_p; /*Restore the ORIGINAL style pointer*/
}
return true;
}
/**
* Signal function of the led
* @param led pointer to a led object
* @param sign a signal type from lv_signal_t enum
* @param param pointer to a signal specific variable
* @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
*/
static lv_res_t lv_led_signal(lv_obj_t * led, lv_signal_t sign, void * param)
{
lv_res_t res;
/* Include the ancient signal function */
res = ancestor_signal(led, sign, param);
if(res != LV_RES_OK) return res;
if(sign == LV_SIGNAL_GET_TYPE) {
lv_obj_type_t * buf = param;
uint8_t i;
for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/
if(buf->type[i] == NULL) break;
}
buf->type[i] = "lv_led";
}
return res;
}
#endif

View File

@@ -0,0 +1,116 @@
/**
* @file lv_led.h
*
*/
#ifndef LV_LED_H
#define LV_LED_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#ifdef LV_CONF_INCLUDE_SIMPLE
#include "lv_conf.h"
#else
#include "../../lv_conf.h"
#endif
#if USE_LV_LED != 0
#include "../lv_core/lv_obj.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/*Data of led*/
typedef struct
{
/*No inherited ext.*/
/*New data for this type */
uint8_t bright; /*Current brightness of the LED (0..255)*/
} lv_led_ext_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a led objects
* @param par pointer to an object, it will be the parent of the new led
* @param copy pointer to a led object, if not NULL then the new object will be copied from it
* @return pointer to the created led
*/
lv_obj_t * lv_led_create(lv_obj_t * par, const lv_obj_t * copy);
/**
* Set the brightness of a LED object
* @param led pointer to a LED object
* @param bright 0 (max. dark) ... 255 (max. light)
*/
void lv_led_set_bright(lv_obj_t * led, uint8_t bright);
/**
* Light on a LED
* @param led pointer to a LED object
*/
void lv_led_on(lv_obj_t * led);
/**
* Light off a LED
* @param led pointer to a LED object
*/
void lv_led_off(lv_obj_t * led);
/**
* Toggle the state of a LED
* @param led pointer to a LED object
*/
void lv_led_toggle(lv_obj_t * led);
/**
* Set the style of a led
* @param led pointer to a led object
* @param style pointer to a style
*/
static inline void lv_led_set_style(lv_obj_t *led, lv_style_t *style)
{
lv_obj_set_style(led, style);
}
/**
* Get the brightness of a LEd object
* @param led pointer to LED object
* @return bright 0 (max. dark) ... 255 (max. light)
*/
uint8_t lv_led_get_bright(const lv_obj_t * led);
/**
* Get the style of an led object
* @param led pointer to an led object
* @return pointer to the led's style
*/
static inline lv_style_t* lv_led_get_style(const lv_obj_t *led)
{
return lv_obj_get_style(led);
}
/**********************
* MACROS
**********************/
#endif /*USE_LV_LED*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_LED_H*/

View File

@@ -0,0 +1,301 @@
/**
* @file lv_line.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_line.h"
#if USE_LV_LINE != 0
#include "../lv_draw/lv_draw.h"
#include "../lv_misc/lv_math.h"
#include <stdint.h>
#include <string.h>
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static bool lv_line_design(lv_obj_t * line, const lv_area_t * mask, lv_design_mode_t mode);
static lv_res_t lv_line_signal(lv_obj_t * line, lv_signal_t sign, void * param);
/**********************
* STATIC VARIABLES
**********************/
static lv_signal_func_t ancestor_signal;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Create a line objects
* @param par 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 * par, const lv_obj_t * copy)
{
LV_LOG_TRACE("line create started");
/*Create a basic object*/
lv_obj_t * new_line = lv_obj_create(par, copy);
lv_mem_assert(new_line);
if(new_line == NULL) return NULL;
if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_line);
/*Extend the basic object to line object*/
lv_line_ext_t * ext = lv_obj_allocate_ext_attr(new_line, sizeof(lv_line_ext_t));
lv_mem_assert(ext);
if(ext == NULL) return NULL;
ext->point_num = 0;
ext->point_array = NULL;
ext->auto_size = 1;
ext->y_inv = 0;
lv_obj_set_design_func(new_line, lv_line_design);
lv_obj_set_signal_func(new_line, lv_line_signal);
/*Init the new line*/
if(copy == NULL) {
lv_obj_set_size(new_line, LV_DPI, LV_DPI); /*Auto size is enables, but set default size until no points are added*/
lv_obj_set_style(new_line, NULL); /*Inherit parent's style*/
lv_obj_set_click(new_line, false);
}
/*Copy an existing object*/
else {
lv_line_ext_t * copy_ext = lv_obj_get_ext_attr(copy);
lv_line_set_auto_size(new_line, lv_line_get_auto_size(copy));
lv_line_set_y_invert(new_line, lv_line_get_y_invert(copy));
lv_line_set_auto_size(new_line, lv_line_get_auto_size(copy));
lv_line_set_points(new_line, copy_ext->point_array, copy_ext->point_num);
/*Refresh the style with new signal function*/
lv_obj_refresh_style(new_line);
}
LV_LOG_INFO("line created");
return new_line;
}
/*=====================
* Setter functions
*====================*/
/**
* Set an array of points. The line object will connect these points.
* @param line pointer to a line object
* @param point_a an array of points. Only the address is saved,
* so the array can NOT be a local variable which will be destroyed
* @param point_num number of points in 'point_a'
*/
void lv_line_set_points(lv_obj_t * line, const lv_point_t * point_a, uint16_t point_num)
{
lv_line_ext_t * ext = lv_obj_get_ext_attr(line);
ext->point_array = point_a;
ext->point_num = point_num;
if(point_num > 0 && ext->auto_size != 0) {
uint16_t i;
lv_coord_t xmax = LV_COORD_MIN;
lv_coord_t ymax = LV_COORD_MIN;
for(i = 0; i < point_num; i++) {
xmax = LV_MATH_MAX(point_a[i].x, xmax);
ymax = LV_MATH_MAX(point_a[i].y, ymax);
}
lv_style_t * style = lv_line_get_style(line);
lv_obj_set_size(line, xmax + style->line.width, ymax + style->line.width);
}
lv_obj_invalidate(line);
}
/**
* Enable (or disable) the auto-size option. The size of the object will fit to its points.
* (set width to x max and height to y max)
* @param line pointer to a line object
* @param en true: auto size is enabled, false: auto size is disabled
*/
void lv_line_set_auto_size(lv_obj_t * line, bool en)
{
lv_line_ext_t * ext = lv_obj_get_ext_attr(line);
if(ext->auto_size == en) return;
ext->auto_size = en == false ? 0 : 1;
/*Refresh the object*/
if(en) lv_line_set_points(line, ext->point_array, ext->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 line 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 * line, bool en)
{
lv_line_ext_t * ext = lv_obj_get_ext_attr(line);
if(ext->y_inv == en) return;
ext->y_inv = en == false ? 0 : 1;
lv_obj_invalidate(line);
}
/*=====================
* Getter functions
*====================*/
/**
* Get the auto size attribute
* @param line pointer to a line object
* @return true: auto size is enabled, false: disabled
*/
bool lv_line_get_auto_size(const lv_obj_t * line)
{
lv_line_ext_t * ext = lv_obj_get_ext_attr(line);
return ext->auto_size == 0 ? false : true;
}
/**
* Get the y inversion attribute
* @param line pointer to a line object
* @return true: y inversion is enabled, false: disabled
*/
bool lv_line_get_y_invert(const lv_obj_t * line)
{
lv_line_ext_t * ext = lv_obj_get_ext_attr(line);
return ext->y_inv == 0 ? false : true;
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Handle the drawing related tasks of the lines
* @param line pointer to an object
* @param mask the object will be drawn only in this area
* @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area
* (return 'true' if yes)
* LV_DESIGN_DRAW: draw the object (always return 'true')
* LV_DESIGN_DRAW_POST: drawing after every children are drawn
* @param return true/false, depends on 'mode'
*/
static bool lv_line_design(lv_obj_t * line, const lv_area_t * mask, lv_design_mode_t mode)
{
/*A line never covers an area*/
if(mode == LV_DESIGN_COVER_CHK) return false;
else if(mode == LV_DESIGN_DRAW_MAIN) {
lv_line_ext_t * ext = lv_obj_get_ext_attr(line);
if(ext->point_num == 0 || ext->point_array == NULL) return false;
lv_style_t * style = lv_obj_get_style(line);
lv_opa_t opa_scale = lv_obj_get_opa_scale(line);
lv_area_t area;
lv_obj_get_coords(line, &area);
lv_coord_t x_ofs = area.x1;
lv_coord_t y_ofs = area.y1;
lv_point_t p1;
lv_point_t p2;
lv_coord_t h = lv_obj_get_height(line);
uint16_t i;
lv_style_t circle_style; /*If rounded...*/
lv_style_copy(&circle_style, style);
circle_style.body.radius = LV_RADIUS_CIRCLE;
circle_style.body.main_color = style->line.color;
circle_style.body.grad_color = style->line.color;
circle_style.body.opa = style->line.opa;
lv_area_t circle_area;
/*Read all points and draw the lines*/
for(i = 0; i < ext->point_num - 1; i++) {
p1.x = ext->point_array[i].x + x_ofs;
p2.x = ext->point_array[i + 1].x + x_ofs;
if(ext->y_inv == 0) {
p1.y = ext->point_array[i].y + y_ofs;
p2.y = ext->point_array[i + 1].y + y_ofs;
} else {
p1.y = h - ext->point_array[i].y + y_ofs;
p2.y = h - ext->point_array[i + 1].y + y_ofs;
}
lv_draw_line(&p1, &p2, mask, style, opa_scale);
/*Draw circle on the joints if enabled*/
if(style->line.rounded) {
circle_area.x1 = p1.x - ((style->line.width - 1) >> 1) - ((style->line.width - 1) & 0x1);
circle_area.y1 = p1.y - ((style->line.width - 1) >> 1) - ((style->line.width - 1) & 0x1);
circle_area.x2 = p1.x + ((style->line.width - 1) >> 1);
circle_area.y2 = p1.y + ((style->line.width - 1) >> 1);
lv_draw_rect(&circle_area, mask, &circle_style, opa_scale);
}
}
/*Draw circle on the last point too if enabled*/
if(style->line.rounded) {
circle_area.x1 = p2.x - ((style->line.width - 1) >> 1) - ((style->line.width - 1) & 0x1);
circle_area.y1 = p2.y - ((style->line.width - 1) >> 1) - ((style->line.width - 1) & 0x1);
circle_area.x2 = p2.x + ((style->line.width - 1) >> 1);
circle_area.y2 = p2.y + ((style->line.width - 1) >> 1);
lv_draw_rect(&circle_area, mask, &circle_style, opa_scale);
}
}
return true;
}
/**
* Signal function of the line
* @param line pointer to a line object
* @param sign a signal type from lv_signal_t enum
* @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
*/
static lv_res_t lv_line_signal(lv_obj_t * line, lv_signal_t sign, void * param)
{
lv_res_t res;
/* Include the ancient signal function */
res = ancestor_signal(line, sign, param);
if(res != LV_RES_OK) return res;
if(sign == LV_SIGNAL_GET_TYPE) {
lv_obj_type_t * buf = param;
uint8_t i;
for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/
if(buf->type[i] == NULL) break;
}
buf->type[i] = "lv_line";
} else if(sign == LV_SIGNAL_REFR_EXT_SIZE) {
lv_style_t * style = lv_line_get_style(line);
if(line->ext_size < style->line.width) line->ext_size = style->line.width;
}
return res;
}
#endif

View File

@@ -0,0 +1,158 @@
/**
* @file lv_line.h
*
*/
#ifndef LV_LINE_H
#define LV_LINE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#ifdef LV_CONF_INCLUDE_SIMPLE
#include "lv_conf.h"
#else
#include "../../lv_conf.h"
#endif
#if USE_LV_LINE != 0
#include "../lv_core/lv_obj.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/*Data of line*/
typedef struct
{
/*Inherited from 'base_obj' so no inherited ext.*/ /*Ext. of ancestor*/
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 auto_size :1; /*1: set obj. width to x max and obj. height to y max */
uint8_t y_inv :1; /*1: y == 0 will be on the bottom*/
} lv_line_ext_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a line objects
* @param par 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 * par, const lv_obj_t * copy);
/*=====================
* Setter functions
*====================*/
/**
* Set an array of points. The line object will connect these points.
* @param line pointer to a line object
* @param point_a an array of points. Only the address is saved,
* so the array can NOT be a local variable which will be destroyed
* @param point_num number of points in 'point_a'
*/
void lv_line_set_points(lv_obj_t * line, const lv_point_t * point_a, uint16_t point_num);
/**
* Enable (or disable) the auto-size option. The size of the object will fit to its points.
* (set width to x max and height to y max)
* @param line pointer to a line object
* @param en true: auto size is enabled, false: auto size is disabled
*/
void lv_line_set_auto_size(lv_obj_t * line, bool en);
/**
* 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 line 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 * line, bool en);
#define lv_line_set_y_inv lv_line_set_y_invert /*The name was inconsistent. In v.6.0 only `lv_line_set_y_invert`will work */
/**
* Set the style of a line
* @param line pointer to a line object
* @param style pointer to a style
*/
static inline void lv_line_set_style(lv_obj_t *line, lv_style_t *style)
{
lv_obj_set_style(line, style);
}
/**
* Obsolete since v5.1. Just for compatibility with v5.0. Will be removed in v6.0
* @param line -
* @param upscale -
*/
static inline void lv_line_set_upscale(lv_obj_t * line, bool upcale)
{
(void) line;
(void) upcale;
}
/*=====================
* Getter functions
*====================*/
/**
* Get the auto size attribute
* @param line pointer to a line object
* @return true: auto size is enabled, false: disabled
*/
bool lv_line_get_auto_size(const lv_obj_t * line);
/**
* Get the y inversion attribute
* @param line pointer to a line object
* @return true: y inversion is enabled, false: disabled
*/
bool lv_line_get_y_invert(const lv_obj_t * line);
/**
* Get the style of an line object
* @param line pointer to an line object
* @return pointer to the line's style
*/
static inline lv_style_t* lv_line_get_style(const lv_obj_t *line)
{
return lv_obj_get_style(line);
}
/**
* Obsolete since v5.1. Just for compatibility with v5.0. Will be removed in v6.0
* @param line -
* @return false
*/
static inline bool lv_line_get_upscale(const lv_obj_t * line)
{
(void) line;
return false;
}
/**********************
* MACROS
**********************/
#endif /*USE_LV_LINE*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_LINE_H*/

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,336 @@
/**
* @file lv_list.h
*
*/
#ifndef LV_LIST_H
#define LV_LIST_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#ifdef LV_CONF_INCLUDE_SIMPLE
#include "lv_conf.h"
#else
#include "../../lv_conf.h"
#endif
#if USE_LV_LIST != 0
/*Testing of dependencies*/
#if USE_LV_PAGE == 0
#error "lv_list: lv_page is required. Enable it in lv_conf.h (USE_LV_PAGE 1) "
#endif
#if USE_LV_BTN == 0
#error "lv_list: lv_btn is required. Enable it in lv_conf.h (USE_LV_BTN 1) "
#endif
#if USE_LV_LABEL == 0
#error "lv_list: lv_label is required. Enable it in lv_conf.h (USE_LV_LABEL 1) "
#endif
#include "../lv_core/lv_obj.h"
#include "lv_page.h"
#include "lv_btn.h"
#include "lv_label.h"
#include "lv_img.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/*Data of list*/
typedef struct
{
lv_page_ext_t page; /*Ext. of ancestor*/
/*New data for this type */
uint16_t anim_time; /*Scroll animation time*/
lv_style_t *styles_btn[LV_BTN_STATE_NUM]; /*Styles of the list element buttons*/
lv_style_t *style_img; /*Style of the list element images on buttons*/
uint32_t size; /*the number of items(buttons) in the list*/
bool single_mode; /* whether single selected mode is enabled */
#if USE_LV_GROUP
lv_obj_t * last_sel; /* The last selected button. It will be reverted when the list is focused again */
lv_obj_t * selected_btn; /* The button is currently being selected*/
#endif
} lv_list_ext_t;
enum {
LV_LIST_STYLE_BG,
LV_LIST_STYLE_SCRL,
LV_LIST_STYLE_SB,
LV_LIST_STYLE_EDGE_FLASH,
LV_LIST_STYLE_BTN_REL,
LV_LIST_STYLE_BTN_PR,
LV_LIST_STYLE_BTN_TGL_REL,
LV_LIST_STYLE_BTN_TGL_PR,
LV_LIST_STYLE_BTN_INA,
};
typedef uint8_t lv_list_style_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a list objects
* @param par pointer to an object, it will be the parent of the new list
* @param copy pointer to a list object, if not NULL then the new object will be copied from it
* @return pointer to the created list
*/
lv_obj_t * lv_list_create(lv_obj_t * par, const lv_obj_t * copy);
/**
* Delete all children of the scrl object, without deleting scrl child.
* @param obj pointer to an object
*/
void lv_list_clean(lv_obj_t *obj);
/*======================
* Add/remove functions
*=====================*/
/**
* Add a list element to the list
* @param list pointer to list object
* @param img_fn file name of an image before the text (NULL if unused)
* @param txt text of the list element (NULL if unused)
* @param rel_action pointer to release action function (like with lv_btn)
* @return pointer to the new list element which can be customized (a button)
*/
lv_obj_t * lv_list_add(lv_obj_t * list, const void * img_src, const char * txt, lv_action_t rel_action);
/**
* Remove the index of the button in the list
* @param list pointer to a list object
* @param index pointer to a the button's index in the list, index must be 0 <= index < lv_list_ext_t.size
* @return true: successfully deleted
*/
bool lv_list_remove(const lv_obj_t * list, uint32_t index);
/*=====================
* Setter functions
*====================*/
/**
* Set single button selected mode, only one button will be selected if enabled.
* @param list pointer to the currently pressed list object
* @param mode, enable(true)/disable(false) single selected mode.
*/
void lv_list_set_single_mode(lv_obj_t *list, bool mode);
#if USE_LV_GROUP
/**
* Make a button selected. Can be used while navigating in the list with a keypad.
* @param list pointer to a list object
* @param btn pointer to a button to select
*/
void lv_list_set_btn_selected(lv_obj_t * list, lv_obj_t * btn);
#endif
/**
* Set scroll animation duration on 'list_up()' 'list_down()' 'list_focus()'
* @param list pointer to a list object
* @param anim_time duration of animation [ms]
*/
void lv_list_set_anim_time(lv_obj_t *list, uint16_t anim_time);
/**
* Set the scroll bar mode of a list
* @param list pointer to a list object
* @param sb_mode the new mode from 'lv_page_sb_mode_t' enum
*/
static inline void lv_list_set_sb_mode(lv_obj_t * list, lv_sb_mode_t mode)
{
lv_page_set_sb_mode(list, mode);
}
/**
* Enable the scroll propagation feature. If enabled then the List will move its parent if there is no more space to scroll.
* @param list pointer to a List
* @param en true or false to enable/disable scroll propagation
*/
static inline void lv_list_set_scroll_propagation(lv_obj_t * list, bool en)
{
lv_page_set_scroll_propagation(list, en);
}
/**
* Enable the edge flash effect. (Show an arc when the an edge is reached)
* @param list pointer to a List
* @param en true or false to enable/disable end flash
*/
static inline void lv_list_set_edge_flash(lv_obj_t * list, bool en)
{
lv_page_set_edge_flash(list, en);
}
/**
* Set a style of a list
* @param list pointer to a list object
* @param type which style should be set
* @param style pointer to a style
*/
void lv_list_set_style(lv_obj_t *list, lv_list_style_t type, lv_style_t *style);
/*=====================
* Getter functions
*====================*/
/**
* Get single button selected mode.
* @param list pointer to the currently pressed list object.
*/
bool lv_list_get_single_mode(lv_obj_t *list);
/**
* Get the text of a list element
* @param btn pointer to list element
* @return pointer to the text
*/
const char * lv_list_get_btn_text(const lv_obj_t * btn);
/**
* Get the label object from a list element
* @param btn pointer to a list element (button)
* @return pointer to the label from the list element or NULL if not found
*/
lv_obj_t * lv_list_get_btn_label(const lv_obj_t * btn);
/**
* Get the image object from a list element
* @param btn pointer to a list element (button)
* @return pointer to the image from the list element or NULL if not found
*/
lv_obj_t * lv_list_get_btn_img(const lv_obj_t * btn);
/**
* Get the next button from list. (Starts from the bottom button)
* @param list pointer to a list object
* @param prev_btn pointer to button. Search the next after it.
* @return pointer to the next button or NULL when no more buttons
*/
lv_obj_t * lv_list_get_prev_btn(const lv_obj_t * list, lv_obj_t * prev_btn);
/**
* Get the previous button from list. (Starts from the top button)
* @param list pointer to a list object
* @param prev_btn pointer to button. Search the previous before it.
* @return pointer to the previous button or NULL when no more buttons
*/
lv_obj_t * lv_list_get_next_btn(const lv_obj_t * list, lv_obj_t * prev_btn);
/**
* Get the index of the button in the list
* @param list pointer to a list object. If NULL, assumes btn is part of a list.
* @param btn pointer to a list element (button)
* @return the index of the button in the list, or -1 of the button not in this list
*/
int32_t lv_list_get_btn_index(const lv_obj_t * list, const lv_obj_t * btn);
/**
* Get the number of buttons in the list
* @param list pointer to a list object
* @return the number of buttons in the list
*/
uint32_t lv_list_get_size(const lv_obj_t * list);
#if USE_LV_GROUP
/**
* Get the currently selected button. Can be used while navigating in the list with a keypad.
* @param list pointer to a list object
* @return pointer to the selected button
*/
lv_obj_t * lv_list_get_btn_selected(const lv_obj_t * list);
#endif
/**
* Get scroll animation duration
* @param list pointer to a list object
* @return duration of animation [ms]
*/
uint16_t lv_list_get_anim_time(const lv_obj_t *list);
/**
* Get the scroll bar mode of a list
* @param list pointer to a list object
* @return scrollbar mode from 'lv_page_sb_mode_t' enum
*/
static inline lv_sb_mode_t lv_list_get_sb_mode(const lv_obj_t * list)
{
return lv_page_get_sb_mode(list);
}
/**
* Get the scroll propagation property
* @param list pointer to a List
* @return true or false
*/
static inline bool lv_list_get_scroll_propagation(lv_obj_t * list)
{
return lv_page_get_scroll_propagation(list);
}
/**
* Get the scroll propagation property
* @param list pointer to a List
* @return true or false
*/
static inline bool lv_list_get_edge_flash(lv_obj_t * list)
{
return lv_page_get_edge_flash(list);
}
/**
* Get a style of a list
* @param list pointer to a list object
* @param type which style should be get
* @return style pointer to a style
* */
lv_style_t * lv_list_get_style(const lv_obj_t *list, lv_list_style_t type);
/*=====================
* Other functions
*====================*/
/**
* Move the list elements up by one
* @param list pointer a to list object
*/
void lv_list_up(const lv_obj_t * list);
/**
* Move the list elements down by one
* @param list pointer to a list object
*/
void lv_list_down(const lv_obj_t * list);
/**
* Focus on a list button. It ensures that the button will be visible on the list.
* @param btn pointer to a list button to focus
* @param anim_en true: scroll with animation, false: without animation
*/
void lv_list_focus(const lv_obj_t *btn, bool anim_en);
/**********************
* MACROS
**********************/
#endif /*USE_LV_LIST*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_LIST_H*/

View File

@@ -0,0 +1,382 @@
/**
* @file lv_lmeter.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_lmeter.h"
#if USE_LV_LMETER != 0
#include "../lv_draw/lv_draw.h"
#include "../lv_themes/lv_theme.h"
#include "../lv_core/lv_group.h"
#include "../lv_misc/lv_math.h"
/*********************
* DEFINES
*********************/
#define LV_LMETER_LINE_UPSCALE 5 /*2^x upscale of line to make rounding*/
#define LV_LMETER_LINE_UPSCALE_MASK ((1 << LV_LMETER_LINE_UPSCALE) - 1)
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static bool lv_lmeter_design(lv_obj_t * lmeter, const lv_area_t * mask, lv_design_mode_t mode);
static lv_res_t lv_lmeter_signal(lv_obj_t * lmeter, lv_signal_t sign, void * param);
static lv_coord_t lv_lmeter_coord_round(int32_t x);
/**********************
* STATIC VARIABLES
**********************/
static lv_signal_func_t ancestor_signal;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Create a line meter objects
* @param par pointer to an object, it will be the parent of the new line meter
* @param copy pointer to a line meter object, if not NULL then the new object will be copied from it
* @return pointer to the created line meter
*/
lv_obj_t * lv_lmeter_create(lv_obj_t * par, const lv_obj_t * copy)
{
LV_LOG_TRACE("line meter create started");
/*Create the ancestor of line meter*/
lv_obj_t * new_lmeter = lv_obj_create(par, copy);
lv_mem_assert(new_lmeter);
if(new_lmeter == NULL) return NULL;
if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_lmeter);
/*Allocate the line meter type specific extended data*/
lv_lmeter_ext_t * ext = lv_obj_allocate_ext_attr(new_lmeter, sizeof(lv_lmeter_ext_t));
lv_mem_assert(ext);
if(ext == NULL) return NULL;
/*Initialize the allocated 'ext' */
ext->min_value = 0;
ext->max_value = 100;
ext->cur_value = 0;
ext->line_cnt = 21; /*Odd scale number looks better*/
ext->scale_angle = 240; /*(scale_num - 1) * N looks better */
/*The signal and design functions are not copied so set them here*/
lv_obj_set_signal_func(new_lmeter, lv_lmeter_signal);
lv_obj_set_design_func(new_lmeter, lv_lmeter_design);
/*Init the new line meter line meter*/
if(copy == NULL) {
lv_obj_set_size(new_lmeter, LV_DPI, LV_DPI);
/*Set the default styles*/
lv_theme_t * th = lv_theme_get_current();
if(th) {
lv_lmeter_set_style(new_lmeter, th->lmeter);
} else {
lv_lmeter_set_style(new_lmeter, &lv_style_pretty_color);
}
}
/*Copy an existing line meter*/
else {
lv_lmeter_ext_t * copy_ext = lv_obj_get_ext_attr(copy);
ext->scale_angle = copy_ext->scale_angle;
ext->line_cnt = copy_ext->line_cnt;
ext->min_value = copy_ext->min_value;
ext->max_value = copy_ext->max_value;
ext->cur_value = copy_ext->cur_value;
/*Refresh the style with new signal function*/
lv_obj_refresh_style(new_lmeter);
}
LV_LOG_INFO("line meter created");
return new_lmeter;
}
/*=====================
* Setter functions
*====================*/
/**
* Set a new value on the line meter
* @param lmeter pointer to a line meter object
* @param value new value
*/
void lv_lmeter_set_value(lv_obj_t * lmeter, int16_t value)
{
lv_lmeter_ext_t * ext = lv_obj_get_ext_attr(lmeter);
if(ext->cur_value == value) return;
ext->cur_value = value > ext->max_value ? ext->max_value : value;
ext->cur_value = ext->cur_value < ext->min_value ? ext->min_value : ext->cur_value;
lv_obj_invalidate(lmeter);
}
/**
* Set minimum and the maximum values of a line meter
* @param lmeter pointer to he line meter object
* @param min minimum value
* @param max maximum value
*/
void lv_lmeter_set_range(lv_obj_t * lmeter, int16_t min, int16_t max)
{
lv_lmeter_ext_t * ext = lv_obj_get_ext_attr(lmeter);
if(ext->min_value == min && ext->max_value == max) return;
ext->max_value = max;
ext->min_value = min;
if(ext->cur_value > max) {
ext->cur_value = max;
lv_lmeter_set_value(lmeter, ext->cur_value);
}
if(ext->cur_value < min) {
ext->cur_value = min;
lv_lmeter_set_value(lmeter, ext->cur_value);
}
lv_obj_invalidate(lmeter);
}
/**
* Set the scale settings of a line meter
* @param lmeter pointer to a line meter object
* @param angle angle of the scale (0..360)
* @param line_cnt number of lines
*/
void lv_lmeter_set_scale(lv_obj_t * lmeter, uint16_t angle, uint8_t line_cnt)
{
lv_lmeter_ext_t * ext = lv_obj_get_ext_attr(lmeter);
if(ext->scale_angle == angle && ext->line_cnt == line_cnt) return;
ext->scale_angle = angle;
ext->line_cnt = line_cnt;
lv_obj_invalidate(lmeter);
}
/*=====================
* Getter functions
*====================*/
/**
* Get the value of a line meter
* @param lmeter pointer to a line meter object
* @return the value of the line meter
*/
int16_t lv_lmeter_get_value(const lv_obj_t * lmeter)
{
lv_lmeter_ext_t * ext = lv_obj_get_ext_attr(lmeter);
return ext->cur_value;
}
/**
* Get the minimum value of a line meter
* @param lmeter pointer to a line meter object
* @return the minimum value of the line meter
*/
int16_t lv_lmeter_get_min_value(const lv_obj_t * lmeter)
{
lv_lmeter_ext_t * ext = lv_obj_get_ext_attr(lmeter);
return ext->min_value;
}
/**
* Get the maximum value of a line meter
* @param lmeter pointer to a line meter object
* @return the maximum value of the line meter
*/
int16_t lv_lmeter_get_max_value(const lv_obj_t * lmeter)
{
lv_lmeter_ext_t * ext = lv_obj_get_ext_attr(lmeter);
return ext->max_value;
}
/**
* Get the scale number of a line meter
* @param lmeter pointer to a line meter object
* @return number of the scale units
*/
uint8_t lv_lmeter_get_line_count(const lv_obj_t * lmeter)
{
lv_lmeter_ext_t * ext = lv_obj_get_ext_attr(lmeter);
return ext->line_cnt ;
}
/**
* Get the scale angle of a line meter
* @param lmeter pointer to a line meter object
* @return angle of the scale
*/
uint16_t lv_lmeter_get_scale_angle(const lv_obj_t * lmeter)
{
lv_lmeter_ext_t * ext = lv_obj_get_ext_attr(lmeter);
return ext->scale_angle;
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Handle the drawing related tasks of the line meters
* @param lmeter pointer to an object
* @param mask the object will be drawn only in this area
* @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area
* (return 'true' if yes)
* LV_DESIGN_DRAW: draw the object (always return 'true')
* LV_DESIGN_DRAW_POST: drawing after every children are drawn
* @param return true/false, depends on 'mode'
*/
static bool lv_lmeter_design(lv_obj_t * lmeter, const lv_area_t * mask, lv_design_mode_t mode)
{
/*Return false if the object is not covers the mask_p area*/
if(mode == LV_DESIGN_COVER_CHK) {
return false;
}
/*Draw the object*/
else if(mode == LV_DESIGN_DRAW_MAIN) {
lv_lmeter_ext_t * ext = lv_obj_get_ext_attr(lmeter);
lv_style_t * style = lv_obj_get_style(lmeter);
lv_opa_t opa_scale = lv_obj_get_opa_scale(lmeter);
lv_style_t style_tmp;
memcpy(&style_tmp, style, sizeof(lv_style_t));
#if USE_LV_GROUP
lv_group_t * g = lv_obj_get_group(lmeter);
if(lv_group_get_focused(g) == lmeter) {
style_tmp.line.width += 1;
}
#endif
lv_coord_t r_out = lv_obj_get_width(lmeter) / 2;
lv_coord_t r_in = r_out - style->body.padding.hor;
if(r_in < 1) r_in = 1;
lv_coord_t x_ofs = lv_obj_get_width(lmeter) / 2 + lmeter->coords.x1;
lv_coord_t y_ofs = lv_obj_get_height(lmeter) / 2 + lmeter->coords.y1;
int16_t angle_ofs = 90 + (360 - ext->scale_angle) / 2;
int16_t level = (int32_t)((int32_t)(ext->cur_value - ext->min_value) * ext->line_cnt) / (ext->max_value - ext->min_value);
uint8_t i;
style_tmp.line.color = style->body.main_color;
/*Calculate every coordinate in a bigger size to make rounding later*/
r_out = r_out << LV_LMETER_LINE_UPSCALE;
r_in = r_in << LV_LMETER_LINE_UPSCALE;
for(i = 0; i < ext->line_cnt; i++) {
/*Calculate the position a scale label*/
int16_t angle = (i * ext->scale_angle) / (ext->line_cnt - 1) + angle_ofs;
lv_coord_t y_out = (int32_t)((int32_t)lv_trigo_sin(angle) * r_out) >> LV_TRIGO_SHIFT;
lv_coord_t x_out = (int32_t)((int32_t)lv_trigo_sin(angle + 90) * r_out) >> LV_TRIGO_SHIFT;
lv_coord_t y_in = (int32_t)((int32_t)lv_trigo_sin(angle) * r_in) >> LV_TRIGO_SHIFT;
lv_coord_t x_in = (int32_t)((int32_t)lv_trigo_sin(angle + 90) * r_in) >> LV_TRIGO_SHIFT;
/*Rounding*/
x_out = lv_lmeter_coord_round(x_out);
x_in = lv_lmeter_coord_round(x_in);
y_out = lv_lmeter_coord_round(y_out);
y_in = lv_lmeter_coord_round(y_in);
lv_point_t p1;
lv_point_t p2;
p2.x = x_in + x_ofs;
p2.y = y_in + y_ofs;
p1.x = x_out + x_ofs;
p1.y = y_out + y_ofs;
if(i >= level) style_tmp.line.color = style->line.color;
else {
style_tmp.line.color = lv_color_mix(style->body.grad_color, style->body.main_color, (255 * i) / ext->line_cnt);
}
lv_draw_line(&p1, &p2, mask, &style_tmp, opa_scale);
}
}
/*Post draw when the children are drawn*/
else if(mode == LV_DESIGN_DRAW_POST) {
}
return true;
}
/**
* Signal function of the line meter
* @param lmeter pointer to a line meter object
* @param sign a signal type from lv_signal_t enum
* @param param pointer to a signal specific variable
* @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
*/
static lv_res_t lv_lmeter_signal(lv_obj_t * lmeter, lv_signal_t sign, void * param)
{
lv_res_t res;
/* Include the ancient signal function */
res = ancestor_signal(lmeter, sign, param);
if(res != LV_RES_OK) return res;
if(sign == LV_SIGNAL_CLEANUP) {
/*Nothing to cleanup. (No dynamically allocated memory in 'ext')*/
} else if(sign == LV_SIGNAL_STYLE_CHG) {
lv_obj_refresh_ext_size(lmeter);
} else if(sign == LV_SIGNAL_REFR_EXT_SIZE) {
lv_style_t * style = lv_lmeter_get_style(lmeter);
lmeter->ext_size = LV_MATH_MAX(lmeter->ext_size, style->line.width);
} else if(sign == LV_SIGNAL_GET_TYPE) {
lv_obj_type_t * buf = param;
uint8_t i;
for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/
if(buf->type[i] == NULL) break;
}
buf->type[i] = "lv_lmeter";
}
return res;
}
/**
* Round a coordinate which is upscaled (>=x.5 -> x + 1; <x.5 -> x)
* @param x a coordinate which is greater then it should be
* @return the downscaled and rounded coordinate (+-1)
*/
static lv_coord_t lv_lmeter_coord_round(int32_t x)
{
#if LV_LMETER_LINE_UPSCALE > 0
bool was_negative = false;
if(x < 0) {
was_negative = true;
x = -x;
}
x = (x >> LV_LMETER_LINE_UPSCALE) + ((x & LV_LMETER_LINE_UPSCALE_MASK) >> (LV_LMETER_LINE_UPSCALE - 1));
if(was_negative) x = -x;
return x;
#else
return x;
#endif
}
#endif

View File

@@ -0,0 +1,153 @@
/**
* @file lv_lmeter.h
*
*/
#ifndef LV_LMETER_H
#define LV_LMETER_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#ifdef LV_CONF_INCLUDE_SIMPLE
#include "lv_conf.h"
#else
#include "../../lv_conf.h"
#endif
#if USE_LV_LMETER != 0
#include "../lv_core/lv_obj.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/*Data of line meter*/
typedef struct
{
/*No inherited ext.*/ /*Ext. of ancestor*/
/*New data for this type */
uint16_t scale_angle; /*Angle of the scale in deg. (0..360)*/
uint8_t line_cnt; /*Count of lines */
int16_t cur_value;
int16_t min_value;
int16_t max_value;
} lv_lmeter_ext_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a line meter objects
* @param par pointer to an object, it will be the parent of the new line meter
* @param copy pointer to a line meter object, if not NULL then the new object will be copied from it
* @return pointer to the created line meter
*/
lv_obj_t * lv_lmeter_create(lv_obj_t * par, const lv_obj_t * copy);
/*=====================
* Setter functions
*====================*/
/**
* Set a new value on the line meter
* @param lmeter pointer to a line meter object
* @param value new value
*/
void lv_lmeter_set_value(lv_obj_t *lmeter, int16_t value);
/**
* Set minimum and the maximum values of a line meter
* @param lmeter pointer to he line meter object
* @param min minimum value
* @param max maximum value
*/
void lv_lmeter_set_range(lv_obj_t *lmeter, int16_t min, int16_t max);
/**
* Set the scale settings of a line meter
* @param lmeter pointer to a line meter object
* @param angle angle of the scale (0..360)
* @param line_cnt number of lines
*/
void lv_lmeter_set_scale(lv_obj_t * lmeter, uint16_t angle, uint8_t line_cnt);
/**
* Set the styles of a line meter
* @param lmeter pointer to a line meter object
* @param bg set the style of the line meter
*/
static inline void lv_lmeter_set_style(lv_obj_t *lmeter, lv_style_t *bg)
{
lv_obj_set_style(lmeter, bg);
}
/*=====================
* Getter functions
*====================*/
/**
* Get the value of a line meter
* @param lmeter pointer to a line meter object
* @return the value of the line meter
*/
int16_t lv_lmeter_get_value(const lv_obj_t *lmeter);
/**
* Get the minimum value of a line meter
* @param lmeter pointer to a line meter object
* @return the minimum value of the line meter
*/
int16_t lv_lmeter_get_min_value(const lv_obj_t * lmeter);
/**
* Get the maximum value of a line meter
* @param lmeter pointer to a line meter object
* @return the maximum value of the line meter
*/
int16_t lv_lmeter_get_max_value(const lv_obj_t * lmeter);
/**
* Get the scale number of a line meter
* @param lmeter pointer to a line meter object
* @return number of the scale units
*/
uint8_t lv_lmeter_get_line_count(const lv_obj_t * lmeter);
/**
* Get the scale angle of a line meter
* @param lmeter pointer to a line meter object
* @return angle of the scale
*/
uint16_t lv_lmeter_get_scale_angle(const lv_obj_t * lmeter);
/**
* Get the style of a line meter
* @param lmeter pointer to a line meter object
* @return pointer to the line meter's style
*/
static inline lv_style_t * lv_lmeter_get_style(const lv_obj_t * lmeter)
{
return lv_obj_get_style(lmeter);
}
/**********************
* MACROS
**********************/
#endif /*USE_LV_LMETER*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_LMETER_H*/

View File

@@ -0,0 +1,532 @@
/*
* Copyright (c) 2019 CTCaer
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file lv_mbox.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_mbox.h"
#if USE_LV_MBOX != 0
#include "../lv_core/lv_group.h"
#include "../lv_themes/lv_theme.h"
#include "../lv_misc/lv_anim.h"
#include "../lv_misc/lv_math.h"
/*********************
* DEFINES
*********************/
#if USE_LV_ANIMATION
# ifndef LV_MBOX_CLOSE_ANIM_TIME
# define LV_MBOX_CLOSE_ANIM_TIME 200 /*List close animation time) */
# endif
#else
# undef LV_MBOX_CLOSE_ANIM_TIME
# define LV_MBOX_CLOSE_ANIM_TIME 0 /*No animations*/
#endif
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static lv_res_t lv_mbox_signal(lv_obj_t * mbox, lv_signal_t sign, void * param);
static void mbox_realign(lv_obj_t * mbox);
static lv_res_t lv_mbox_close_action(lv_obj_t * btn, const char * txt);
#if USE_LV_ANIMATION
static void lv_mbox_close_end_cb(lv_obj_t * mbox);
#endif
/**********************
* STATIC VARIABLES
**********************/
static lv_signal_func_t ancestor_signal;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Create a message box objects
* @param par pointer to an object, it will be the parent of the new message box
* @param copy pointer to a message box object, if not NULL then the new object will be copied from it
* @return pointer to the created message box
*/
lv_obj_t * lv_mbox_create(lv_obj_t * par, const lv_obj_t * copy)
{
LV_LOG_TRACE("mesasge box create started");
/*Create the ancestor message box*/
lv_obj_t * new_mbox = lv_cont_create(par, copy);
lv_mem_assert(new_mbox);
if(new_mbox == NULL) return NULL;
if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_mbox);
/*Allocate the message box type specific extended data*/
lv_mbox_ext_t * ext = lv_obj_allocate_ext_attr(new_mbox, sizeof(lv_mbox_ext_t));
lv_mem_assert(ext);
if(ext == NULL) return NULL;
ext->text = NULL;
ext->btnm = NULL;
ext->anim_time = LV_MBOX_CLOSE_ANIM_TIME;
/*The signal and design functions are not copied so set them here*/
lv_obj_set_signal_func(new_mbox, lv_mbox_signal);
/*Init the new message box message box*/
if(copy == NULL) {
ext->text = lv_label_create(new_mbox, NULL);
lv_label_set_align(ext->text, LV_LABEL_ALIGN_CENTER);
lv_label_set_long_mode(ext->text, LV_LABEL_LONG_BREAK);
lv_label_set_text(ext->text, "Message");
lv_cont_set_layout(new_mbox, LV_LAYOUT_COL_M);
lv_cont_set_fit(new_mbox, false, true);
lv_obj_set_width(new_mbox, LV_HOR_RES / 2);
lv_obj_align(new_mbox, NULL, LV_ALIGN_CENTER, 0, 0);
/*Set the default styles*/
lv_theme_t * th = lv_theme_get_current();
if(th) {
lv_mbox_set_style(new_mbox, LV_MBOX_STYLE_BG, th->mbox.bg);
} else {
lv_mbox_set_style(new_mbox, LV_MBOX_STYLE_BG, &lv_style_pretty);
}
}
/*Copy an existing message box*/
else {
lv_mbox_ext_t * copy_ext = lv_obj_get_ext_attr(copy);
ext->text = lv_label_create(new_mbox, copy_ext->text);
/*Copy the buttons and the label on them*/
if(copy_ext->btnm) ext->btnm = lv_btnm_create(new_mbox, copy_ext->btnm);
/*Refresh the style with new signal function*/
lv_obj_refresh_style(new_mbox);
}
LV_LOG_INFO("mesasge box created");
return new_mbox;
}
/*======================
* Add/remove functions
*=====================*/
/**
* Add button to the message box
* @param mbox pointer to message box object
* @param btn_map button descriptor (button matrix map).
* E.g. a const char *txt[] = {"ok", "close", ""} (Can not be local variable)
* @param action a function which will be called when a button is released
*/
void lv_mbox_add_btns(lv_obj_t * mbox, const char ** btn_map, lv_btnm_action_t action)
{
lv_mbox_ext_t * ext = lv_obj_get_ext_attr(mbox);
/*Create a button matrix if not exists yet*/
if(ext->btnm == NULL) {
ext->btnm = lv_btnm_create(mbox, NULL);
/*Set the default styles*/
lv_theme_t * th = lv_theme_get_current();
if(th) {
lv_mbox_set_style(mbox, LV_MBOX_STYLE_BTN_BG, th->mbox.btn.bg);
lv_mbox_set_style(mbox, LV_MBOX_STYLE_BTN_REL, th->mbox.btn.rel);
lv_mbox_set_style(mbox, LV_MBOX_STYLE_BTN_PR, th->mbox.btn.pr);
} else {
lv_btnm_set_style(ext->btnm, LV_BTNM_STYLE_BG, &lv_style_transp_fit);
}
}
lv_btnm_set_map(ext->btnm, btn_map);
if(action == NULL) lv_btnm_set_action(ext->btnm, lv_mbox_close_action); /*Set a default action anyway*/
else lv_btnm_set_action(ext->btnm, action);
mbox_realign(mbox);
}
/*=====================
* Setter functions
*====================*/
/**
* Set the text of the message box
* @param mbox pointer to a message box
* @param txt a '\0' terminated character string which will be the message box text
*/
void lv_mbox_set_text(lv_obj_t * mbox, const char * txt)
{
lv_mbox_ext_t * ext = lv_obj_get_ext_attr(mbox);
lv_label_set_text(ext->text, txt);
mbox_realign(mbox);
}
/**
* Stop the action to call when button is released
* @param pointer to an 'lv_btnm_action_t' action. In the action you need to use `lv_mbox_get_from_btn()` to get the `mbox`.
* @param pointer to an 'lv_btnm_action_t' action
*/
void lv_mbox_set_action(lv_obj_t * mbox, lv_btnm_action_t action)
{
lv_mbox_ext_t * ext = lv_obj_get_ext_attr(mbox);
lv_btnm_set_action(ext->btnm, action);
}
/**
* Set animation duration
* @param mbox pointer to a message box object
* @param anim_time animation length in milliseconds (0: no animation)
*/
void lv_mbox_set_anim_time(lv_obj_t * mbox, uint16_t anim_time)
{
lv_mbox_ext_t * ext = lv_obj_get_ext_attr(mbox);
#if USE_LV_ANIMATION == 0
anim_time = 0;
#endif
ext->anim_time = anim_time;
}
/**
* Automatically delete the message box after a given time
* @param mbox pointer to a message box object
* @param delay a time (in milliseconds) to wait before delete the message box
*/
void lv_mbox_start_auto_close(lv_obj_t * mbox, uint16_t delay)
{
#if USE_LV_ANIMATION
lv_mbox_ext_t * ext = lv_obj_get_ext_attr(mbox);
if(ext->anim_time != 0) {
/*Add shrinking animations*/
lv_obj_animate(mbox, LV_ANIM_GROW_H | LV_ANIM_OUT, ext->anim_time, delay, NULL);
lv_obj_animate(mbox, LV_ANIM_GROW_V | LV_ANIM_OUT, ext->anim_time, delay, lv_mbox_close_end_cb);
/*Disable fit to let shrinking work*/
lv_cont_set_fit(mbox, false, false);
} else {
lv_obj_animate(mbox, LV_ANIM_NONE, ext->anim_time, delay, lv_mbox_close_end_cb);
}
#else
(void)delay; /*Unused*/
lv_obj_del(mbox);
#endif
}
/**
* Stop the auto. closing of message box
* @param mbox pointer to a message box object
*/
void lv_mbox_stop_auto_close(lv_obj_t * mbox)
{
#if USE_LV_ANIMATION
lv_anim_del(mbox, NULL);
#else
(void)mbox; /*Unused*/
#endif
}
/**
* Set a style of a message box
* @param mbox pointer to a message box object
* @param type which style should be set
* @param style pointer to a style
*/
void lv_mbox_set_style(lv_obj_t * mbox, lv_mbox_style_t type, lv_style_t * style)
{
lv_mbox_ext_t * ext = lv_obj_get_ext_attr(mbox);
switch(type) {
case LV_MBOX_STYLE_BG:
lv_obj_set_style(mbox, style);
break;
case LV_MBOX_STYLE_BTN_BG:
lv_btnm_set_style(ext->btnm, LV_BTNM_STYLE_BG, style);
break;
case LV_MBOX_STYLE_BTN_REL:
lv_btnm_set_style(ext->btnm, LV_BTNM_STYLE_BTN_REL, style);
break;
case LV_MBOX_STYLE_BTN_PR:
lv_btnm_set_style(ext->btnm, LV_BTNM_STYLE_BTN_PR, style);
break;
case LV_MBOX_STYLE_BTN_TGL_REL:
lv_btnm_set_style(ext->btnm, LV_BTNM_STYLE_BTN_TGL_REL, style);
break;
case LV_MBOX_STYLE_BTN_TGL_PR:
lv_btnm_set_style(ext->btnm, LV_BTNM_STYLE_BTN_TGL_PR, style);
break;
case LV_MBOX_STYLE_BTN_INA:
lv_btnm_set_style(ext->btnm, LV_BTNM_STYLE_BTN_INA, style);
break;
}
mbox_realign(mbox);
}
/**
* Set whether recoloring is enabled
* @param btnm pointer to button matrix object
* @param en whether recoloring is enabled
*/
void lv_mbox_set_recolor(lv_obj_t * mbox, bool en)
{
lv_mbox_ext_t * ext = lv_obj_get_ext_attr(mbox);
if(ext->btnm)
lv_btnm_set_recolor(ext->btnm, en);
}
void lv_mbox_set_recolor_text(lv_obj_t * mbox, bool en)
{
lv_mbox_ext_t * ext = lv_obj_get_ext_attr(mbox);
if (ext->text)
lv_label_set_recolor(ext->text, en);
}
/*=====================
* Getter functions
*====================*/
/**
* Get the text of the message box
* @param mbox pointer to a message box object
* @return pointer to the text of the message box
*/
const char * lv_mbox_get_text(const lv_obj_t * mbox)
{
lv_mbox_ext_t * ext = lv_obj_get_ext_attr(mbox);
return lv_label_get_text(ext->text);
}
/**
* Get the message box object from one of its button.
* It is useful in the button release actions where only the button is known
* @param btn pointer to a button of a message box
* @return pointer to the button's message box
*/
lv_obj_t * lv_mbox_get_from_btn(const lv_obj_t * btn)
{
lv_obj_t * mbox = lv_obj_get_parent(btn);
return mbox;
}
/**
* Get the animation duration (close animation time)
* @param mbox pointer to a message box object
* @return animation length in milliseconds (0: no animation)
*/
uint16_t lv_mbox_get_anim_time(const lv_obj_t * mbox)
{
lv_mbox_ext_t * ext = lv_obj_get_ext_attr(mbox);
return ext->anim_time;
}
/**
* Get a style of a message box
* @param mbox pointer to a message box object
* @param type which style should be get
* @return style pointer to a style
*/
lv_style_t * lv_mbox_get_style(const lv_obj_t * mbox, lv_mbox_style_t type)
{
lv_style_t * style = NULL;
lv_mbox_ext_t * ext = lv_obj_get_ext_attr(mbox);
switch(type) {
case LV_MBOX_STYLE_BG:
style = lv_obj_get_style(mbox);
break;
case LV_MBOX_STYLE_BTN_BG:
style = lv_btnm_get_style(ext->btnm, LV_BTNM_STYLE_BG);
break;
case LV_MBOX_STYLE_BTN_REL:
style = lv_btnm_get_style(ext->btnm, LV_BTNM_STYLE_BTN_REL);
break;
case LV_MBOX_STYLE_BTN_PR:
style = lv_btnm_get_style(ext->btnm, LV_BTNM_STYLE_BTN_PR);
break;
case LV_MBOX_STYLE_BTN_TGL_REL:
style = lv_btnm_get_style(ext->btnm, LV_BTNM_STYLE_BTN_TGL_REL);
break;
case LV_MBOX_STYLE_BTN_TGL_PR:
style = lv_btnm_get_style(ext->btnm, LV_BTNM_STYLE_BTN_TGL_PR);
break;
case LV_MBOX_STYLE_BTN_INA:
style = lv_btnm_get_style(ext->btnm, LV_BTNM_STYLE_BTN_INA);
break;
default:
style = NULL;
break;
}
return style;
}
/**
* Get whether recoloring is enabled
* @param btnm pointer to button matrix object
* @return whether recoloring is enabled
*/
bool lv_mbox_get_recolor(const lv_obj_t * mbox)
{
lv_mbox_ext_t * ext = lv_obj_get_ext_attr(mbox);
if(!ext->btnm)
return false;
return lv_btnm_get_recolor(ext->btnm);
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Signal function of the message box
* @param mbox pointer to a message box object
* @param sign a signal type from lv_signal_t enum
* @param param pointer to a signal specific variable
* @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
*/
static lv_res_t lv_mbox_signal(lv_obj_t * mbox, lv_signal_t sign, void * param)
{
lv_res_t res;
/*Translate LV_GROUP_KEY_UP/DOWN to LV_GROUP_KEY_LEFT/RIGHT */
char c_trans = 0;
if(sign == LV_SIGNAL_CONTROLL) {
c_trans = *((char *)param);
if(c_trans == LV_GROUP_KEY_DOWN) c_trans = LV_GROUP_KEY_LEFT;
if(c_trans == LV_GROUP_KEY_UP) c_trans = LV_GROUP_KEY_RIGHT;
param = &c_trans;
}
/* Include the ancient signal function */
res = ancestor_signal(mbox, sign, param);
if(res != LV_RES_OK) return res;
lv_mbox_ext_t * ext = lv_obj_get_ext_attr(mbox);
if(sign == LV_SIGNAL_CORD_CHG) {
if(lv_obj_get_width(mbox) != lv_area_get_width(param)) {
mbox_realign(mbox);
}
} else if(sign == LV_SIGNAL_STYLE_CHG) {
mbox_realign(mbox);
} else if(sign == LV_SIGNAL_FOCUS || sign == LV_SIGNAL_DEFOCUS ||
sign == LV_SIGNAL_CONTROLL || sign == LV_SIGNAL_GET_EDITABLE) {
if(ext->btnm) {
ext->btnm->signal_func(ext->btnm, sign, param);
}
/* The button matrix with ENCODER input supposes it's in a group but in this case it isn't (Only the message box's container)
* So so some actions here instead*/
if(sign == LV_SIGNAL_FOCUS) {
#if USE_LV_GROUP
lv_indev_t * indev = lv_indev_get_act();
lv_hal_indev_type_t indev_type = lv_indev_get_type(indev);
if(indev_type == LV_INDEV_TYPE_ENCODER) {
/*In navigation mode don't select any button but in edit mode select the fist*/
lv_btnm_ext_t * btnm_ext = lv_obj_get_ext_attr(ext->btnm);
if(lv_group_get_editing(lv_obj_get_group(mbox))) btnm_ext->btn_id_pr = 0;
else btnm_ext->btn_id_pr = LV_BTNM_PR_NONE;
}
#endif
}
} else if(sign == LV_SIGNAL_GET_TYPE) {
lv_obj_type_t * buf = param;
uint8_t i;
for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/
if(buf->type[i] == NULL) break;
}
buf->type[i] = "lv_mbox";
}
return res;
}
/**
* Resize the button holder to fit
* @param mbox pointer to message box object
*/
static void mbox_realign(lv_obj_t * mbox)
{
lv_mbox_ext_t * ext = lv_obj_get_ext_attr(mbox);
lv_style_t * style = lv_mbox_get_style(mbox, LV_MBOX_STYLE_BG);
lv_coord_t w = lv_obj_get_width(mbox) - 2 * style->body.padding.hor;
if(ext->text) {
lv_obj_set_width(ext->text, w);
}
if(ext->btnm) {
lv_style_t * btn_bg_style = lv_mbox_get_style(mbox, LV_MBOX_STYLE_BTN_BG);
lv_style_t * btn_rel_style = lv_mbox_get_style(mbox, LV_MBOX_STYLE_BTN_REL);
lv_coord_t font_h = lv_font_get_height(btn_rel_style->text.font);
lv_obj_set_size(ext->btnm, w, font_h + 2 * btn_rel_style->body.padding.ver + 2 * btn_bg_style->body.padding.ver);
}
}
static lv_res_t lv_mbox_close_action(lv_obj_t * btn, const char * txt)
{
lv_obj_t * mbox = lv_mbox_get_from_btn(btn);
if(txt[0] != '\0') {
lv_mbox_start_auto_close(mbox, 0);
return LV_RES_INV;
}
return LV_RES_OK;
}
#if USE_LV_ANIMATION
static void lv_mbox_close_end_cb(lv_obj_t * mbox)
{
lv_obj_del(mbox);
}
#endif
#endif

View File

@@ -0,0 +1,221 @@
/*
* Copyright (c) 2019 CTCaer
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file lv_mbox.h
*
*/
#ifndef LV_MBOX_H
#define LV_MBOX_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#ifdef LV_CONF_INCLUDE_SIMPLE
#include "lv_conf.h"
#else
#include "../../lv_conf.h"
#endif
#if USE_LV_MBOX != 0
/*Testing of dependencies*/
#if USE_LV_CONT == 0
#error "lv_mbox: lv_cont is required. Enable it in lv_conf.h (USE_LV_CONT 1) "
#endif
#if USE_LV_BTNM == 0
#error "lv_mbox: lv_btnm is required. Enable it in lv_conf.h (USE_LV_BTNM 1) "
#endif
#if USE_LV_LABEL == 0
#error "lv_mbox: lv_label is required. Enable it in lv_conf.h (USE_LV_LABEL 1) "
#endif
#include "../lv_core/lv_obj.h"
#include "lv_cont.h"
#include "lv_btnm.h"
#include "lv_label.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/*Data of message box*/
typedef struct
{
lv_cont_ext_t bg; /*Ext. of ancestor*/
/*New data for this type */
lv_obj_t *text; /*Text of the message box*/
lv_obj_t *btnm; /*Button matrix for the buttons*/
uint16_t anim_time; /*Duration of close animation [ms] (0: no animation)*/
} lv_mbox_ext_t;
enum {
LV_MBOX_STYLE_BG,
LV_MBOX_STYLE_BTN_BG,
LV_MBOX_STYLE_BTN_REL,
LV_MBOX_STYLE_BTN_PR,
LV_MBOX_STYLE_BTN_TGL_REL,
LV_MBOX_STYLE_BTN_TGL_PR,
LV_MBOX_STYLE_BTN_INA,
};
typedef uint8_t lv_mbox_style_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a message box objects
* @param par pointer to an object, it will be the parent of the new message box
* @param copy pointer to a message box object, if not NULL then the new object will be copied from it
* @return pointer to the created message box
*/
lv_obj_t * lv_mbox_create(lv_obj_t * par, const lv_obj_t * copy);
/*======================
* Add/remove functions
*=====================*/
/**
* Add button to the message box
* @param mbox pointer to message box object
* @param btn_map button descriptor (button matrix map).
* E.g. a const char *txt[] = {"ok", "close", ""} (Can not be local variable)
* @param action a function which will be called when a button is released
*/
void lv_mbox_add_btns(lv_obj_t * mbox, const char **btn_map, lv_btnm_action_t action);
/*=====================
* Setter functions
*====================*/
/**
* Set the text of the message box
* @param mbox pointer to a message box
* @param txt a '\0' terminated character string which will be the message box text
*/
void lv_mbox_set_text(lv_obj_t * mbox, const char * txt);
/**
* Stop the action to call when button is released
* @param mbox pointer to a message box object
* @param pointer to an 'lv_btnm_action_t' action. In the action you need to use `lv_mbox_get_from_btn()` to get the `mbox`.
*/
void lv_mbox_set_action(lv_obj_t * mbox, lv_btnm_action_t action);
/**
* Set animation duration
* @param mbox pointer to a message box object
* @param anim_time animation length in milliseconds (0: no animation)
*/
void lv_mbox_set_anim_time(lv_obj_t * mbox, uint16_t anim_time);
/**
* Automatically delete the message box after a given time
* @param mbox pointer to a message box object
* @param delay a time (in milliseconds) to wait before delete the message box
*/
void lv_mbox_start_auto_close(lv_obj_t * mbox, uint16_t delay);
/**
* Stop the auto. closing of message box
* @param mbox pointer to a message box object
*/
void lv_mbox_stop_auto_close(lv_obj_t * mbox);
/**
* Set a style of a message box
* @param mbox pointer to a message box object
* @param type which style should be set
* @param style pointer to a style
*/
void lv_mbox_set_style(lv_obj_t *mbox, lv_mbox_style_t type, lv_style_t *style);
/**
* Set whether recoloring is enabled. Must be called after `lv_mbox_add_btns`.
* @param btnm pointer to button matrix object
* @param en whether recoloring is enabled
*/
void lv_mbox_set_recolor(lv_obj_t * mbox, bool en);
void lv_mbox_set_recolor_text(lv_obj_t * mbox, bool en);
/*=====================
* Getter functions
*====================*/
/**
* Get the text of the message box
* @param mbox pointer to a message box object
* @return pointer to the text of the message box
*/
const char * lv_mbox_get_text(const lv_obj_t * mbox);
/**
* Get the message box object from one of its button.
* It is useful in the button release actions where only the button is known
* @param btn pointer to a button of a message box
* @return pointer to the button's message box
*/
lv_obj_t * lv_mbox_get_from_btn(const lv_obj_t * btn);
/**
* Get the animation duration (close animation time)
* @param mbox pointer to a message box object
* @return animation length in milliseconds (0: no animation)
*/
uint16_t lv_mbox_get_anim_time(const lv_obj_t * mbox);
/**
* Get a style of a message box
* @param mbox pointer to a message box object
* @param type which style should be get
* @return style pointer to a style
*/
lv_style_t * lv_mbox_get_style(const lv_obj_t *mbox, lv_mbox_style_t type);
/**
* Get whether recoloring is enabled
* @param btnm pointer to button matrix object
* @return whether recoloring is enabled
*/
bool lv_mbox_get_recolor(const lv_obj_t * mbox);
/**********************
* MACROS
**********************/
#endif /*USE_LV_MBOX*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_MBOX_H*/

View File

@@ -0,0 +1,36 @@
CSRCS += lv_arc.c
CSRCS += lv_bar.c
CSRCS += lv_cb.c
CSRCS += lv_ddlist.c
CSRCS += lv_kb.c
CSRCS += lv_line.c
CSRCS += lv_mbox.c
CSRCS += lv_preload.c
CSRCS += lv_roller.c
CSRCS += lv_table.c
CSRCS += lv_tabview.c
CSRCS += lv_tileview.c
CSRCS += lv_btn.c
CSRCS += lv_calendar.c
CSRCS += lv_chart.c
CSRCS += lv_canvas.c
CSRCS += lv_gauge.c
CSRCS += lv_label.c
CSRCS += lv_list.c
CSRCS += lv_slider.c
CSRCS += lv_ta.c
CSRCS += lv_spinbox.c
CSRCS += lv_btnm.c
CSRCS += lv_cont.c
CSRCS += lv_img.c
CSRCS += lv_imgbtn.c
CSRCS += lv_led.c
CSRCS += lv_lmeter.c
CSRCS += lv_page.c
CSRCS += lv_sw.c
CSRCS += lv_win.c
DEPPATH += --dep-path $(LVGL_DIR)/lvgl/lv_objx
VPATH += :$(LVGL_DIR)/lvgl/lv_objx
CFLAGS += "-I$(LVGL_DIR)/lvgl/lv_objx"

View File

@@ -0,0 +1,231 @@
/**
* @file lv_templ.c
*
*/
/* TODO Remove these instructions
* Search an replace: template -> object normal name with lower case (e.g. button, label etc.)
* templ -> object short name with lower case(e.g. btn, label etc)
* TEMPL -> object short name with upper case (e.g. BTN, LABEL etc.)
*
*/
/*********************
* INCLUDES
*********************/
//#include "lv_templ.h" /*TODO uncomment this*/
#if USE_LV_TEMPL != 0
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static bool lv_templ_design(lv_obj_t * templ, const lv_area_t * mask, lv_design_mode_t mode);
static lv_res_t lv_templ_signal(lv_obj_t * templ, lv_signal_t sign, void * param);
/**********************
* STATIC VARIABLES
**********************/
static lv_signal_func_t ancestor_signal;
static lv_design_func_t ancestor_design;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Create a template object
* @param par pointer to an object, it will be the parent of the new template
* @param copy pointer to a template object, if not NULL then the new object will be copied from it
* @return pointer to the created template
*/
lv_obj_t * lv_templ_create(lv_obj_t * par, const lv_obj_t * copy)
{
LV_LOG_TRACE("template create started");
/*Create the ancestor of template*/
/*TODO modify it to the ancestor create function */
lv_obj_t * new_templ = lv_ANCESTOR_create(par, copy);
lv_mem_assert(new_templ);
if(new_templ == NULL) return NULL;
/*Allocate the template type specific extended data*/
lv_templ_ext_t * ext = lv_obj_allocate_ext_attr(new_templ, sizeof(lv_templ_ext_t));
lv_mem_assert(ext);
if(ext == NULL) return NULL;
if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_templ);
if(ancestor_design == NULL) ancestor_design = lv_obj_get_design_func(new_templ);
/*Initialize the allocated 'ext' */
ext->xyz = 0;
/*The signal and design functions are not copied so set them here*/
lv_obj_set_signal_func(new_templ, lv_templ_signal);
lv_obj_set_design_func(new_templ, lv_templ_design);
/*Init the new template template*/
if(copy == NULL) {
}
/*Copy an existing template*/
else {
lv_templ_ext_t * copy_ext = lv_obj_get_ext_attr(copy);
/*Refresh the style with new signal function*/
lv_obj_refresh_style(new_templ);
}
LV_LOG_INFO("template created");
return new_templ;
}
/*======================
* Add/remove functions
*=====================*/
/*
* New object specific "add" or "remove" functions come here
*/
/*=====================
* Setter functions
*====================*/
/*
* New object specific "set" functions come here
*/
/**
* Set a style of a template.
* @param templ pointer to template object
* @param type which style should be set
* @param style pointer to a style
*/
void lv_templ_set_style(lv_obj_t * templ, lv_templ_style_t type, lv_style_t * style)
{
lv_templ_ext_t * ext = lv_obj_get_ext_attr(templ);
switch(type) {
case LV_TEMPL_STYLE_X:
break;
case LV_TEMPL_STYLE_Y:
break;
}
}
/*=====================
* Getter functions
*====================*/
/*
* New object specific "get" functions come here
*/
/**
* Get style of a template.
* @param templ pointer to template object
* @param type which style should be get
* @return style pointer to the style
*/
lv_style_t * lv_templ_get_style(const lv_obj_t * templ, lv_templ_style_t type)
{
lv_templ_ext_t * ext = lv_obj_get_ext_attr(templ);
lv_style_t * style = NULL;
switch(type) {
case LV_TEMPL_STYLE_X:
style = NULL; /*Replace NULL with a pointer to the style*/
case LV_TEMPL_STYLE_Y:
style = NULL; /*Replace NULL with a pointer to the style*/
default:
style = NULL;
}
return style;
}
/*=====================
* Other functions
*====================*/
/*
* New object specific "other" functions come here
*/
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Handle the drawing related tasks of the templates
* @param templ pointer to an object
* @param mask the object will be drawn only in this area
* @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area
* (return 'true' if yes)
* LV_DESIGN_DRAW: draw the object (always return 'true')
* LV_DESIGN_DRAW_POST: drawing after every children are drawn
* @param return true/false, depends on 'mode'
*/
static bool lv_templ_design(lv_obj_t * templ, const lv_area_t * mask, lv_design_mode_t mode)
{
/*Return false if the object is not covers the mask_p area*/
if(mode == LV_DESIGN_COVER_CHK) {
return false;
}
/*Draw the object*/
else if(mode == LV_DESIGN_DRAW_MAIN) {
}
/*Post draw when the children are drawn*/
else if(mode == LV_DESIGN_DRAW_POST) {
}
return true;
}
/**
* Signal function of the template
* @param templ pointer to a template object
* @param sign a signal type from lv_signal_t enum
* @param param pointer to a signal specific variable
* @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
*/
static lv_res_t lv_templ_signal(lv_obj_t * templ, lv_signal_t sign, void * param)
{
lv_res_t res;
/* Include the ancient signal function */
res = ancestor_signal(templ, sign, param);
if(res != LV_RES_OK) return res;
if(sign == LV_SIGNAL_CLEANUP) {
/*Nothing to cleanup. (No dynamically allocated memory in 'ext')*/
} else if(sign == LV_SIGNAL_GET_TYPE) {
lv_obj_type_t * buf = param;
uint8_t i;
for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/
if(buf->type[i] == NULL) break;
}
buf->type[i] = "lv_templ";
}
return res;
}
#endif

View File

@@ -0,0 +1,111 @@
/**
* @file lv_templ.h
*
*/
/* TODO Remove these instructions
* Search an replace: template -> object normal name with lower case (e.g. button, label etc.)
* 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
*********************/
#ifdef LV_CONF_INCLUDE_SIMPLE
#include "lv_conf.h"
#else
#include "../../lv_conf.h"
#endif
#if USE_LV_TEMPL != 0
#include "../lv_core/lv_obj.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/*Data of template*/
typedef struct {
lv_ANCESTOR_ext_t ANCESTOR; /*Ext. of ancestor*/
/*New data for this type */
} lv_templ_ext_t;
/*Styles*/
enum {
LV_TEMPL_STYLE_X,
LV_TEMPL_STYLE_Y,
};
typedef uint8_t lv_templ_style_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a template objects
* @param par pointer to an object, it will be the parent of the new template
* @param copy pointer to a template object, if not NULL then the new object will be copied from it
* @return pointer to the created template
*/
lv_obj_t * lv_templ_create(lv_obj_t * par, const lv_obj_t * copy);
/*======================
* Add/remove functions
*=====================*/
/*=====================
* Setter functions
*====================*/
/**
* Set a style of a template.
* @param templ pointer to template object
* @param type which style should be set
* @param style pointer to a style
*/
void lv_templ_set_style(lv_obj_t * templ, lv_templ_style_t type, lv_style_t *style);
/*=====================
* Getter functions
*====================*/
/**
* Get style of a template.
* @param templ pointer to template object
* @param type which style should be get
* @return style pointer to the style
*/
lv_style_t * lv_templ_get_style(const lv_obj_t * templ, lv_templ_style_t type);
/*=====================
* Other functions
*====================*/
/**********************
* MACROS
**********************/
#endif /*USE_LV_TEMPL*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_TEMPL_H*/

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,402 @@
/*
* Copyright (c) 2019 CTCaer
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file lv_page.h
*
*/
#ifndef LV_PAGE_H
#define LV_PAGE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#ifdef LV_CONF_INCLUDE_SIMPLE
#include "lv_conf.h"
#else
#include "../../lv_conf.h"
#endif
#if USE_LV_PAGE != 0
/*Testing of dependencies*/
#if USE_LV_CONT == 0
#error "lv_page: lv_cont is required. Enable it in lv_conf.h (USE_LV_CONT 1) "
#endif
#include "lv_cont.h"
#include "../lv_core/lv_indev.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/*Scrollbar modes: shows when should the scrollbars be visible*/
enum
{
LV_SB_MODE_OFF = 0x0, /*Never show scrollbars*/
LV_SB_MODE_ON = 0x1, /*Always show scrollbars*/
LV_SB_MODE_DRAG = 0x2, /*Show scrollbars when page is being dragged*/
LV_SB_MODE_AUTO = 0x3, /*Show scrollbars when the scrollable container is large enough to be scrolled*/
LV_SB_MODE_HIDE = 0x4, /*Hide the scroll bar temporally*/
LV_SB_MODE_UNHIDE = 0x5, /*Unhide the previously hidden scrollbar. Recover it's type too*/
};
typedef uint8_t lv_sb_mode_t;
/*Data of page*/
typedef struct
{
lv_cont_ext_t bg; /*Ext. of ancestor*/
/*New data for this type */
lv_obj_t * scrl; /*The scrollable object on the background*/
lv_style_t *bgo; /*The scrollable object on the background*/
lv_style_t *pr; /*The scrollable object on the background*/
lv_action_t rel_action; /*Function to call when the page is released*/
lv_action_t pr_action; /*Function to call when the page is pressed*/
struct {
lv_style_t *style; /*Style of scrollbars*/
lv_area_t hor_area; /*Horizontal scrollbar area relative to the page. (Handled by the library) */
lv_area_t ver_area; /*Vertical scrollbar area relative to the page (Handled by the library)*/
uint8_t hor_draw :1; /*1: horizontal scrollbar is visible now (Handled by the library)*/
uint8_t ver_draw :1; /*1: vertical scrollbar is visible now (Handled by the library)*/
lv_sb_mode_t mode:3; /*Scrollbar visibility from 'lv_page_sb_mode_t'*/
} sb;
struct {
uint16_t state; /*Store the current size of the edge flash effect*/
lv_style_t *style; /*Style of edge flash effect (usually homogeneous circle)*/
uint8_t enabled :1; /*1: Show a flash animation on the edge*/
uint8_t top_ip :1; /*Used internally to show that top most position is reached (flash is In Progress)*/
uint8_t bottom_ip :1; /*Used internally to show that bottom most position is reached (flash is In Progress)*/
uint8_t right_ip :1; /*Used internally to show that right most position is reached (flash is In Progress)*/
uint8_t left_ip :1; /*Used internally to show that left most position is reached (flash is In Progress)*/
}edge_flash;
uint8_t arrow_scroll :1; /*1: Enable scrolling with LV_GROUP_KEY_LEFT/RIGHT/UP/DOWN*/
uint8_t scroll_prop :1; /*1: Propagate the scrolling the the parent if the edge is reached*/
uint8_t scroll_prop_ip :1; /*1: Scroll propagation is in progress (used by the library)*/
} lv_page_ext_t;
enum {
LV_PAGE_STYLE_BG,
LV_PAGE_STYLE_BGO,
LV_PAGE_STYLE_PR,
LV_PAGE_STYLE_SCRL,
LV_PAGE_STYLE_SB,
LV_PAGE_STYLE_EDGE_FLASH,
};
typedef uint8_t lv_page_style_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a page objects
* @param par pointer to an object, it will be the parent of the new page
* @param copy pointer to a page object, if not NULL then the new object will be copied from it
* @return pointer to the created page
*/
lv_obj_t * lv_page_create(lv_obj_t * par, const lv_obj_t * copy);
/**
* Delete all children of the scrl object, without deleting scrl child.
* @param obj pointer to an object
*/
void lv_page_clean(lv_obj_t *obj);
/**
* Get the press action of the page
* @param page pointer to a page object
* @return a function to call when the page is pressed
*/
lv_action_t lv_page_get_pr_action(lv_obj_t * page);
/**
* Get the release action of the page
* @param page pointer to a page object
* @return a function to call when the page is released
*/
lv_action_t lv_page_get_rel_action(lv_obj_t * page);
/**
* Get the scrollable object of a page
* @param page pointer to a page object
* @return pointer to a container which is the scrollable part of the page
*/
lv_obj_t * lv_page_get_scrl(const lv_obj_t * page);
/*=====================
* Setter functions
*====================*/
/**
* Set a release action for the page
* @param page pointer to a page object
* @param rel_action a function to call when the page is released
*/
void lv_page_set_rel_action(lv_obj_t * page, lv_action_t rel_action);
/**
* Set a press action for the page
* @param page pointer to a page object
* @param pr_action a function to call when the page is pressed
*/
void lv_page_set_pr_action(lv_obj_t * page, lv_action_t pr_action);
/**
* Set the scroll bar mode on a page
* @param page pointer to a page object
* @param sb_mode the new mode from 'lv_page_sb.mode_t' enum
*/
void lv_page_set_sb_mode(lv_obj_t * page, lv_sb_mode_t sb_mode);
/**
* Enable/Disable scrolling with arrows if the page is in group (arrows: LV_GROUP_KEY_LEFT/RIGHT/UP/DOWN)
* @param page pointer to a page object
* @param en true: enable scrolling with arrows
*/
void lv_page_set_arrow_scroll(lv_obj_t * page, bool en);
/**
* Enable the scroll propagation feature. If enabled then the page will move its parent if there is no more space to scroll.
* @param page pointer to a Page
* @param en true or false to enable/disable scroll propagation
*/
void lv_page_set_scroll_propagation(lv_obj_t * page, bool en);
/**
* Enable the edge flash effect. (Show an arc when the an edge is reached)
* @param page pointer to a Page
* @param en true or false to enable/disable end flash
*/
void lv_page_set_edge_flash(lv_obj_t * page, bool en);
/**
* Set the fit attribute of the scrollable part of a page.
* It means it can set its size automatically to involve all children.
* (Can be set separately horizontally and vertically)
* @param page pointer to a page object
* @param hor_en true: enable horizontal fit
* @param ver_en true: enable vertical fit
*/
static inline void lv_page_set_scrl_fit(lv_obj_t *page, bool hor_en, bool ver_en)
{
lv_cont_set_fit(lv_page_get_scrl(page), hor_en, ver_en);
}
/**
* Set width of the scrollable part of a page
* @param page pointer to a page object
* @param w the new width of the scrollable (it ha no effect is horizontal fit is enabled)
*/
static inline void lv_page_set_scrl_width(lv_obj_t *page, lv_coord_t w)
{
lv_obj_set_width(lv_page_get_scrl(page), w);
}
/**
* Set height of the scrollable part of a page
* @param page pointer to a page object
* @param h the new height of the scrollable (it ha no effect is vertical fit is enabled)
*/
static inline void lv_page_set_scrl_height(lv_obj_t *page, lv_coord_t h)
{
lv_obj_set_height(lv_page_get_scrl(page), h);
}
/**
* Set the layout of the scrollable part of the page
* @param page pointer to a page object
* @param layout a layout from 'lv_cont_layout_t'
*/
static inline void lv_page_set_scrl_layout(lv_obj_t * page, lv_layout_t layout)
{
lv_cont_set_layout(lv_page_get_scrl(page), layout);
}
/**
* Set a style of a page
* @param page pointer to a page object
* @param type which style should be set
* @param style pointer to a style
*/
void lv_page_set_style(lv_obj_t *page, lv_page_style_t type, lv_style_t *style);
/*=====================
* Getter functions
*====================*/
/**
* Set the scroll bar mode on a page
* @param page pointer to a page object
* @return the mode from 'lv_page_sb.mode_t' enum
*/
lv_sb_mode_t lv_page_get_sb_mode(const lv_obj_t * page);
/**
* Get the the scrolling with arrows (LV_GROUP_KEY_LEFT/RIGHT/UP/DOWN) is enabled or not
* @param page pointer to a page object
* @return true: scrolling with arrows is enabled
*/
bool lv_page_get_arrow_scroll(const lv_obj_t * page);
/**
* Get the scroll propagation property
* @param page pointer to a Page
* @return true or false
*/
bool lv_page_get_scroll_propagation(lv_obj_t * page);
/**
* Get the edge flash effect property.
* @param page pointer to a Page
* return true or false
*/
bool lv_page_get_edge_flash(lv_obj_t * page);
/**
* Get that width which can be set to the children to still not cause overflow (show scrollbars)
* @param page pointer to a page object
* @return the width which still fits into the page
*/
lv_coord_t lv_page_get_fit_width(lv_obj_t * page);
/**
* Get that height which can be set to the children to still not cause overflow (show scrollbars)
* @param page pointer to a page object
* @return the height which still fits into the page
*/
lv_coord_t lv_page_get_fit_height(lv_obj_t * page);
/**
* Get width of the scrollable part of a page
* @param page pointer to a page object
* @return the width of the scrollable
*/
static inline lv_coord_t lv_page_get_scrl_width(const lv_obj_t *page)
{
return lv_obj_get_width(lv_page_get_scrl(page));
}
/**
* Get height of the scrollable part of a page
* @param page pointer to a page object
* @return the height of the scrollable
*/
static inline lv_coord_t lv_page_get_scrl_height(const lv_obj_t *page)
{
return lv_obj_get_height(lv_page_get_scrl(page));
}
/**
* Get the layout of the scrollable part of a page
* @param page pointer to page object
* @return the layout from 'lv_cont_layout_t'
*/
static inline lv_layout_t lv_page_get_scrl_layout(const lv_obj_t * page)
{
return lv_cont_get_layout(lv_page_get_scrl(page));
}
/**
* Get horizontal fit attribute of the scrollable part of a page
* @param page pointer to a page object
* @return true: horizontal fit is enabled; false: disabled
*/
static inline bool lv_page_get_scrl_hor_fit(const lv_obj_t * page)
{
return lv_cont_get_hor_fit(lv_page_get_scrl(page));
}
/**
* Get vertical fit attribute of the scrollable part of a page
* @param page pointer to a page object
* @return true: vertical fit is enabled; false: disabled
*/
static inline bool lv_page_get_scrl_fit_ver(const lv_obj_t * page)
{
return lv_cont_get_ver_fit(lv_page_get_scrl(page));
}
/**
* Get a style of a page
* @param page pointer to page object
* @param type which style should be get
* @return style pointer to a style
*/
lv_style_t * lv_page_get_style(const lv_obj_t *page, lv_page_style_t type);
/*=====================
* Other functions
*====================*/
/**
* Glue the object to the page. After it the page can be moved (dragged) with this object too.
* @param obj pointer to an object on a page
* @param glue true: enable glue, false: disable glue
*/
void lv_page_glue_obj(lv_obj_t * obj, bool glue);
/**
* Focus on an object. It ensures that the object will be visible on the page.
* @param page pointer to a page object
* @param obj pointer to an object to focus (must be on the page)
* @param anim_time scroll animation time in milliseconds (0: no animation)
*/
void lv_page_focus(lv_obj_t * page, const lv_obj_t * obj, uint16_t anim_time);
/**
* Scroll the page horizontally
* @param page pointer to a page object
* @param dist the distance to scroll (< 0: scroll left; > 0 scroll right)
*/
void lv_page_scroll_hor(lv_obj_t * page, lv_coord_t dist);
/**
* Scroll the page vertically
* @param page pointer to a page object
* @param dist the distance to scroll (< 0: scroll down; > 0 scroll up)
*/
void lv_page_scroll_ver(lv_obj_t * page, lv_coord_t dist);
/**
* Not intended to use directly by the user but by other object types internally.
* Start an edge flash animation. Exactly one `ext->edge_flash.xxx_ip` should be set
* @param page
*/
void lv_page_start_edge_flash(lv_obj_t * page);
/**********************
* MACROS
**********************/
#endif /*USE_LV_PAGE*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_PAGE_H*/

View File

@@ -0,0 +1,411 @@
/**
* @file lv_preload.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_preload.h"
#if USE_LV_PRELOAD != 0
#include "../lv_misc/lv_math.h"
#include "../lv_draw/lv_draw_rect.h"
#include "../lv_draw/lv_draw_arc.h"
#include "../lv_themes/lv_theme.h"
/*********************
* DEFINES
*********************/
#ifndef LV_PRELOAD_DEF_ARC_LENGTH
# define LV_PRELOAD_DEF_ARC_LENGTH 60 /*[deg]*/
#endif
#ifndef LV_PRELOAD_DEF_SPIN_TIME
# define LV_PRELOAD_DEF_SPIN_TIME 1000 /*[ms]*/
#endif
#ifndef LV_PRELOAD_DEF_ANIM
# define LV_PRELOAD_DEF_ANIM LV_PRELOAD_TYPE_SPINNING_ARC /*animation type*/
#endif
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static bool lv_preload_design(lv_obj_t * preload, const lv_area_t * mask, lv_design_mode_t mode);
static lv_res_t lv_preload_signal(lv_obj_t * preload, lv_signal_t sign, void * param);
/**********************
* STATIC VARIABLES
**********************/
static lv_signal_func_t ancestor_signal;
static lv_design_func_t ancestor_design;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Create a pre loader object
* @param par pointer to an object, it will be the parent of the new pre loader
* @param copy pointer to a pre loader object, if not NULL then the new object will be copied from it
* @return pointer to the created pre loader
*/
lv_obj_t * lv_preload_create(lv_obj_t * par, const lv_obj_t * copy)
{
LV_LOG_TRACE("preload create started");
/*Create the ancestor of pre loader*/
lv_obj_t * new_preload = lv_arc_create(par, copy);
lv_mem_assert(new_preload);
if(new_preload == NULL) return NULL;
/*Allocate the pre loader type specific extended data*/
lv_preload_ext_t * ext = lv_obj_allocate_ext_attr(new_preload, sizeof(lv_preload_ext_t));
lv_mem_assert(ext);
if(ext == NULL) return NULL;
if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_preload);
if(ancestor_design == NULL) ancestor_design = lv_obj_get_design_func(new_preload);
/*Initialize the allocated 'ext' */
ext->arc_length = LV_PRELOAD_DEF_ARC_LENGTH;
ext->anim_type = LV_PRELOAD_DEF_ANIM;
/*The signal and design functions are not copied so set them here*/
lv_obj_set_signal_func(new_preload, lv_preload_signal);
lv_obj_set_design_func(new_preload, lv_preload_design);
/*Init the new pre loader pre loader*/
if(copy == NULL) {
lv_obj_set_size(new_preload, LV_DPI / 2, LV_DPI / 2);
/*Set the default styles*/
lv_theme_t * th = lv_theme_get_current();
if(th) {
lv_preload_set_style(new_preload, LV_PRELOAD_STYLE_MAIN, th->preload);
} else {
lv_obj_set_style(new_preload, &lv_style_pretty_color);
}
ext->time = LV_PRELOAD_DEF_SPIN_TIME;
}
/*Copy an existing pre loader*/
else {
lv_preload_ext_t * copy_ext = lv_obj_get_ext_attr(copy);
ext->arc_length = copy_ext->arc_length;
ext->time = copy_ext->time;
/*Refresh the style with new signal function*/
lv_obj_refresh_style(new_preload);
}
lv_preload_set_animation_type(new_preload, ext->anim_type);
LV_LOG_INFO("preload created");
return new_preload;
}
/*======================
* Add/remove functions
*=====================*/
/**
* Set the length of the spinning arc in degrees
* @param preload pointer to a preload object
* @param deg length of the arc
*/
void lv_preload_set_arc_length(lv_obj_t * preload, uint16_t deg)
{
lv_preload_ext_t * ext = lv_obj_get_ext_attr(preload);
ext->arc_length = deg;
}
/**
* Set the spin time of the arc
* @param preload pointer to a preload object
* @param time time of one round in milliseconds
*/
void lv_preload_set_spin_time(lv_obj_t * preload, uint16_t time)
{
lv_preload_ext_t * ext = lv_obj_get_ext_attr(preload);
ext->time = time;
lv_preload_set_animation_type(preload, ext->anim_type);
}
/*=====================
* Setter functions
*====================*/
/**
* Set a style of a pre loader.
* @param preload pointer to pre loader object
* @param type which style should be set
* @param style pointer to a style
* */
void lv_preload_set_style(lv_obj_t * preload, lv_preload_style_t type, lv_style_t * style)
{
switch(type) {
case LV_PRELOAD_STYLE_MAIN:
lv_arc_set_style(preload, LV_ARC_STYLE_MAIN, style);
break;
}
}
/**
* Set the animation type of a preloadeer.
* @param preload pointer to pre loader object
* @param type animation type of the preload
* */
void lv_preload_set_animation_type(lv_obj_t * preload, lv_preloader_type_t type)
{
#if USE_LV_ANIMATION
lv_preload_ext_t * ext = lv_obj_get_ext_attr(preload);
/*delete previous animation*/
//lv_anim_del(preload, NULL);
switch(type)
{
case LV_PRELOAD_TYPE_FILLSPIN_ARC:
{
ext->anim_type = LV_PRELOAD_TYPE_FILLSPIN_ARC;
lv_anim_t a;
a.var = preload;
a.start = 0;
a.end = 360;
a.fp = (lv_anim_fp_t)lv_preload_spinner_animation;
a.path = lv_anim_path_ease_in_out;
a.end_cb = NULL;
a.act_time = 0;
a.time = ext->time;
a.playback = 0;
a.playback_pause = 0;
a.repeat = 1;
a.repeat_pause = 0;
lv_anim_create(&a);
lv_anim_t b;
b.var = preload;
b.start = ext->arc_length;
b.end = 360 - ext->arc_length;
b.fp = (lv_anim_fp_t)lv_preload_set_arc_length;
b.path = lv_anim_path_ease_in_out;
b.end_cb = NULL;
b.act_time = 0;
b.time = ext->time;
b.playback = 1;
b.playback_pause = 0;
b.repeat = 1;
b.repeat_pause = 0;
lv_anim_create(&b);
break;
}
case LV_PRELOAD_TYPE_SPINNING_ARC:
default:
{
ext->anim_type = LV_PRELOAD_TYPE_SPINNING_ARC;
lv_anim_t a;
a.var = preload;
a.start = 0;
a.end = 360;
a.fp = (lv_anim_fp_t)lv_preload_spinner_animation;
a.path = lv_anim_path_ease_in_out;
a.end_cb = NULL;
a.act_time = 0;
a.time = ext->time;
a.playback = 0;
a.playback_pause = 0;
a.repeat = 1;
a.repeat_pause = 0;
lv_anim_create(&a);
break;
}
}
#endif //USE_LV_ANIMATION
}
/*=====================
* Getter functions
*====================*/
/**
* Get the arc length [degree] of the a pre loader
* @param preload pointer to a pre loader object
*/
uint16_t lv_preload_get_arc_length(const lv_obj_t * preload)
{
lv_preload_ext_t * ext = lv_obj_get_ext_attr(preload);
return ext->arc_length;
}
/**
* Get the spin time of the arc
* @param preload pointer to a pre loader object [milliseconds]
*/
uint16_t lv_preload_get_spin_time(const lv_obj_t * preload)
{
lv_preload_ext_t * ext = lv_obj_get_ext_attr(preload);
return ext->time;
}
/**
* Get style of a pre loader.
* @param preload pointer to pre loader object
* @param type which style should be get
* @return style pointer to the style
* */
lv_style_t * lv_preload_get_style(const lv_obj_t * preload, lv_preload_style_t type)
{
lv_style_t * style = NULL;
switch(type) {
case LV_PRELOAD_STYLE_MAIN:
style = lv_arc_get_style(preload, LV_ARC_STYLE_MAIN);
break;
default:
style = NULL;
break;
}
return style;
}
/**
* Get the animation type of a preloadeer.
* @param preload pointer to pre loader object
* @return animation type
* */
lv_preloader_type_t lv_preload_get_animation_type(lv_obj_t * preload)
{
lv_preload_ext_t * ext = lv_obj_get_ext_attr(preload);
return ext->anim_type;
}
/*=====================
* Other functions
*====================*/
/**
* Automatically in an animation to rotate the arc of spinner.
* @param ptr pointer to preloader
* @param val the current desired value [0..360]
*/
void lv_preload_spinner_animation(void * ptr, int32_t val)
{
lv_obj_t * preload = ptr;
lv_preload_ext_t * ext = lv_obj_get_ext_attr(preload);
int16_t angle_start = val - ext->arc_length / 2 + 180;
int16_t angle_end = angle_start + ext->arc_length;
angle_start = angle_start % 360;
angle_end = angle_end % 360;
lv_arc_set_angles(preload, angle_start, angle_end);
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Handle the drawing related tasks of the pre loaders
* @param preload pointer to an object
* @param mask the object will be drawn only in this area
* @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area
* (return 'true' if yes)
* LV_DESIGN_DRAW: draw the object (always return 'true')
* LV_DESIGN_DRAW_POST: drawing after every children are drawn
* @param return true/false, depends on 'mode'
*/
static bool lv_preload_design(lv_obj_t * preload, const lv_area_t * mask, lv_design_mode_t mode)
{
/*Return false if the object is not covers the mask_p area*/
if(mode == LV_DESIGN_COVER_CHK) {
return false;
}
/*Draw the object*/
else if(mode == LV_DESIGN_DRAW_MAIN) {
/*Draw a circle as background*/
lv_style_t * style = lv_arc_get_style(preload, LV_ARC_STYLE_MAIN);
if(style->body.border.width > 0) {
lv_coord_t r = (LV_MATH_MIN(lv_obj_get_width(preload), lv_obj_get_height(preload))) / 2;
r -= LV_MATH_MIN(style->body.padding.hor, style->body.padding.ver);
lv_coord_t x = preload->coords.x1 + lv_obj_get_width(preload) / 2;
lv_coord_t y = preload->coords.y1 + lv_obj_get_height(preload) / 2;
lv_style_t bg_style;
lv_style_copy(&bg_style, &lv_style_plain);
bg_style.body.empty = 1;
bg_style.body.radius = LV_RADIUS_CIRCLE;
bg_style.body.border.color = style->body.border.color;
bg_style.body.border.width = style->body.border.width;
lv_area_t bg_area;
bg_area.x1 = x - r;
bg_area.y1 = y - r;
bg_area.x2 = x + r;
bg_area.y2 = y + r;
lv_draw_rect(&bg_area, mask, &bg_style, lv_obj_get_opa_scale(preload));
}
/*Draw the arc above the background circle */
ancestor_design(preload, mask, mode);
}
/*Post draw when the children are drawn*/
else if(mode == LV_DESIGN_DRAW_POST) {
}
return true;
}
/**
* Signal function of the pre loader
* @param preload pointer to a pre loader object
* @param sign a signal type from lv_signal_t enum
* @param param pointer to a signal specific variable
* @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
*/
static lv_res_t lv_preload_signal(lv_obj_t * preload, lv_signal_t sign, void * param)
{
lv_res_t res;
/* Include the ancient signal function */
res = ancestor_signal(preload, sign, param);
if(res != LV_RES_OK) return res;
if(sign == LV_SIGNAL_CLEANUP) {
/*Nothing to cleanup. (No dynamically allocated memory in 'ext')*/
} else if(sign == LV_SIGNAL_GET_TYPE) {
lv_obj_type_t * buf = param;
uint8_t i;
for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/
if(buf->type[i] == NULL) break;
}
buf->type[i] = "lv_preload";
}
return res;
}
#endif

View File

@@ -0,0 +1,168 @@
/**
* @file lv_preload.h
*
*/
#ifndef LV_PRELOAD_H
#define LV_PRELOAD_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#ifdef LV_CONF_INCLUDE_SIMPLE
#include "lv_conf.h"
#else
#include "../../lv_conf.h"
#endif
#if USE_LV_PRELOAD != 0
/*Testing of dependencies*/
#if USE_LV_ARC == 0
#error "lv_preload: lv_arc is required. Enable it in lv_conf.h (USE_LV_ARC 1) "
#endif
#if USE_LV_ANIMATION == 0
#error "lv_preload: animations are required. Enable it in lv_conf.h (USE_LV_ANIMATION 1) "
#endif
#include "../lv_core/lv_obj.h"
#include "lv_arc.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
enum {
LV_PRELOAD_TYPE_SPINNING_ARC,
LV_PRELOAD_TYPE_FILLSPIN_ARC,
};
typedef uint8_t lv_preloader_type_t;
/*Data of pre loader*/
typedef struct {
lv_arc_ext_t arc; /*Ext. of ancestor*/
/*New data for this type */
uint16_t arc_length; /*Length of the spinning indicator in degree*/
uint16_t time; /*Time of one round*/
lv_preloader_type_t anim_type; /*Type of the arc animation*/
} lv_preload_ext_t;
/*Styles*/
enum {
LV_PRELOAD_STYLE_MAIN,
};
typedef uint8_t lv_preload_style_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a pre loader objects
* @param par pointer to an object, it will be the parent of the new pre loader
* @param copy pointer to a pre loader object, if not NULL then the new object will be copied from it
* @return pointer to the created pre loader
*/
lv_obj_t * lv_preload_create(lv_obj_t * par, const lv_obj_t * copy);
/*======================
* Add/remove functions
*=====================*/
/**
* Set the length of the spinning arc in degrees
* @param preload pointer to a preload object
* @param deg length of the arc
*/
void lv_preload_set_arc_length(lv_obj_t * preload, uint16_t deg);
/**
* Set the spin time of the arc
* @param preload pointer to a preload object
* @param time time of one round in milliseconds
*/
void lv_preload_set_spin_time(lv_obj_t * preload, uint16_t time);
/*=====================
* Setter functions
*====================*/
/**
* Set a style of a pre loader.
* @param preload pointer to pre loader object
* @param type which style should be set
* @param style pointer to a style
* */
void lv_preload_set_style(lv_obj_t * preload, lv_preload_style_t type, lv_style_t *style);
/**
* Set the animation type of a preloadeer.
* @param preload pointer to pre loader object
* @param type animation type of the preload
* */
void lv_preload_set_animation_type(lv_obj_t * preload, lv_preloader_type_t type);
/*=====================
* Getter functions
*====================*/
/**
* Get the arc length [degree] of the a pre loader
* @param preload pointer to a pre loader object
*/
uint16_t lv_preload_get_arc_length(const lv_obj_t * preload);
/**
* Get the spin time of the arc
* @param preload pointer to a pre loader object [milliseconds]
*/
uint16_t lv_preload_get_spin_time(const lv_obj_t * preload);
/**
* Get style of a pre loader.
* @param preload pointer to pre loader object
* @param type which style should be get
* @return style pointer to the style
* */
lv_style_t * lv_preload_get_style(const lv_obj_t * preload, lv_preload_style_t type);
/**
* Get the animation type of a preloadeer.
* @param preload pointer to pre loader object
* @return animation type
* */
lv_preloader_type_t lv_preload_get_animation_type(lv_obj_t * preload);
/*=====================
* Other functions
*====================*/
/**
* Get style of a pre loader.
* @param preload pointer to pre loader object
* @param type which style should be get
* @return style pointer to the style
* */
void lv_preload_spinner_animation(void * ptr, int32_t val);
/**********************
* MACROS
**********************/
#endif /*USE_LV_PRELOAD*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_PRELOAD_H*/

View File

@@ -0,0 +1,582 @@
/**
* @file lv_roller.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_roller.h"
#if USE_LV_ROLLER != 0
#include "../lv_draw/lv_draw.h"
#include "../lv_core/lv_group.h"
#include "../lv_themes/lv_theme.h"
/*********************
* DEFINES
*********************/
#if USE_LV_ANIMATION
# ifndef LV_ROLLER_ANIM_TIME
# define LV_ROLLER_ANIM_TIME 200 /*ms*/
# endif
#else
# undef LV_ROLLER_ANIM_TIME
# define LV_ROLLER_ANIM_TIME 0 /*No animation*/
#endif
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static bool lv_roller_design(lv_obj_t * roller, const lv_area_t * mask, lv_design_mode_t mode);
static lv_res_t lv_roller_scrl_signal(lv_obj_t * roller_scrl, lv_signal_t sign, void * param);
static lv_res_t lv_roller_signal(lv_obj_t * roller, lv_signal_t sign, void * param);
static void refr_position(lv_obj_t * roller, bool anim_en);
static void draw_bg(lv_obj_t * roller, const lv_area_t * mask);
/**********************
* STATIC VARIABLES
**********************/
static lv_signal_func_t ancestor_signal;
static lv_signal_func_t ancestor_scrl_signal;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Create a roller object
* @param par pointer to an object, it will be the parent of the new roller
* @param copy pointer to a roller object, if not NULL then the new object will be copied from it
* @return pointer to the created roller
*/
lv_obj_t * lv_roller_create(lv_obj_t * par, const lv_obj_t * copy)
{
LV_LOG_TRACE("roller create started");
/*Create the ancestor of roller*/
lv_obj_t * new_roller = lv_ddlist_create(par, copy);
lv_mem_assert(new_roller);
if(new_roller == NULL) return NULL;
if(ancestor_scrl_signal == NULL) ancestor_scrl_signal = lv_obj_get_signal_func(lv_page_get_scrl(new_roller));
if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_roller);
/*Allocate the roller type specific extended data*/
lv_roller_ext_t * ext = lv_obj_allocate_ext_attr(new_roller, sizeof(lv_roller_ext_t));
lv_mem_assert(ext);
if(ext == NULL) return NULL;
ext->ddlist.draw_arrow = 0; /*Do not draw arrow by default*/
/*The signal and design functions are not copied so set them here*/
lv_obj_set_signal_func(new_roller, lv_roller_signal);
lv_obj_set_design_func(new_roller, lv_roller_design);
/*Init the new roller roller*/
if(copy == NULL) {
lv_obj_t * scrl = lv_page_get_scrl(new_roller);
lv_obj_set_drag(scrl, true); /*In ddlist is might be disabled*/
lv_page_set_rel_action(new_roller, NULL); /*Roller don't uses it (like ddlist)*/
lv_page_set_scrl_fit(new_roller, true, false); /*Height is specified directly*/
lv_ddlist_open(new_roller, false);
lv_ddlist_set_anim_time(new_roller, LV_ROLLER_ANIM_TIME);
lv_roller_set_visible_row_count(new_roller, 3);
lv_label_set_align(ext->ddlist.label, LV_LABEL_ALIGN_CENTER);
lv_obj_set_signal_func(scrl, lv_roller_scrl_signal);
/*Set the default styles*/
lv_theme_t * th = lv_theme_get_current();
if(th) {
lv_roller_set_style(new_roller, LV_ROLLER_STYLE_BG, th->roller.bg);
lv_roller_set_style(new_roller, LV_ROLLER_STYLE_SEL, th->roller.sel);
} else {
/*Let the ddlist's style*/
lv_obj_refresh_style(new_roller); /*To set scrollable size automatically*/
}
}
/*Copy an existing roller*/
else {
lv_obj_t * scrl = lv_page_get_scrl(new_roller);
lv_ddlist_open(new_roller, false);
lv_obj_set_signal_func(scrl, lv_roller_scrl_signal);
lv_obj_refresh_style(new_roller); /*Refresh the style with new signal function*/
}
LV_LOG_INFO("roller created");
return new_roller;
}
/*=====================
* Setter functions
*====================*/
/**
* Set the align of the roller's options (left or center)
* @param roller - pointer to a roller object
* @param align - one of lv_label_align_t values (left, right, center)
*/
void lv_roller_set_align(lv_obj_t * roller, lv_label_align_t align)
{
lv_roller_ext_t * ext = lv_obj_get_ext_attr(roller);
lv_mem_assert(ext);
if(ext->ddlist.label == NULL) return; /*Probably the roller is being deleted if the label is NULL.*/
lv_label_set_align(ext->ddlist.label, align);
}
/**
* 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 true: set with animation; false set immediately
*/
void lv_roller_set_selected(lv_obj_t * roller, uint16_t sel_opt, bool anim_en)
{
#if USE_LV_ANIMATION == 0
anim_en = false;
#endif
if(lv_roller_get_selected(roller) == sel_opt) return;
lv_ddlist_set_selected(roller, sel_opt);
refr_position(roller, anim_en);
}
/**
* 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 * roller, uint8_t row_cnt)
{
lv_roller_ext_t * ext = lv_obj_get_ext_attr(roller);
lv_style_t * style_label = lv_obj_get_style(ext->ddlist.label);
uint8_t n_line_space = (row_cnt > 1) ? row_cnt - 1 : 1;
lv_ddlist_set_fix_height(roller, lv_font_get_height(style_label->text.font) * row_cnt + style_label->text.line_space * n_line_space);
}
/**
* Set a style of a roller
* @param roller pointer to a roller object
* @param type which style should be set
* @param style pointer to a style
*/
void lv_roller_set_style(lv_obj_t * roller, lv_roller_style_t type, lv_style_t * style)
{
switch(type) {
case LV_ROLLER_STYLE_BG:
lv_obj_set_style(roller, style);
break;
case LV_ROLLER_STYLE_SEL:
lv_ddlist_set_style(roller, LV_DDLIST_STYLE_SEL, style);
break;
}
}
/*=====================
* Getter functions
*====================*/
/**
* Get the align attribute. Default alignment after _create is LV_LABEL_ALIGN_CENTER
* @param roller pointer to a roller object
* @return LV_LABEL_ALIGN_LEFT, LV_LABEL_ALIGN_RIGHT or LV_LABEL_ALIGN_CENTER
*/
lv_label_align_t lv_roller_get_align(const lv_obj_t * roller)
{
lv_roller_ext_t * ext = lv_obj_get_ext_attr(roller);
lv_mem_assert(ext);
lv_mem_assert(ext->ddlist.label);
return lv_label_get_align(ext->ddlist.label);
}
/**
* Get the auto width set attribute
* @param roller pointer to a roller object
* @return true: auto size enabled; false: manual width settings enabled
*/
bool lv_roller_get_hor_fit(const lv_obj_t * roller)
{
return lv_page_get_scrl_hor_fit(roller);
}
/**
* Get a style of a roller
* @param roller pointer to a roller object
* @param type which style should be get
* @return style pointer to a style
* */
lv_style_t * lv_roller_get_style(const lv_obj_t * roller, lv_roller_style_t type)
{
switch(type) {
case LV_ROLLER_STYLE_BG:
return lv_obj_get_style(roller);
case LV_ROLLER_STYLE_SEL:
return lv_ddlist_get_style(roller, LV_DDLIST_STYLE_SEL);
default:
return NULL;
}
/*To avoid warning*/
return NULL;
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Handle the drawing related tasks of the rollers
* @param roller pointer to an object
* @param mask the object will be drawn only in this area
* @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area
* (return 'true' if yes)
* LV_DESIGN_DRAW: draw the object (always return 'true')
* LV_DESIGN_DRAW_POST: drawing after every children are drawn
* @param return true/false, depends on 'mode'
*/
static bool lv_roller_design(lv_obj_t * roller, const lv_area_t * mask, lv_design_mode_t mode)
{
/*Return false if the object is not covers the mask_p area*/
if(mode == LV_DESIGN_COVER_CHK) {
return false;
}
/*Draw the object*/
else if(mode == LV_DESIGN_DRAW_MAIN) {
draw_bg(roller, mask);
lv_style_t * style = lv_roller_get_style(roller, LV_ROLLER_STYLE_BG);
lv_opa_t opa_scale = lv_obj_get_opa_scale(roller);
const lv_font_t * font = style->text.font;
lv_roller_ext_t * ext = lv_obj_get_ext_attr(roller);
lv_coord_t font_h = lv_font_get_height(font);
lv_area_t rect_area;
rect_area.y1 = roller->coords.y1 + lv_obj_get_height(roller) / 2 - font_h / 2 - style->text.line_space / 2;
if((font_h & 0x1) && (style->text.line_space & 0x1)) rect_area.y1 --; /*Compensate the two rounding error*/
rect_area.y2 = rect_area.y1 + font_h + style->text.line_space - 1;
rect_area.x1 = roller->coords.x1;
rect_area.x2 = roller->coords.x2;
lv_draw_rect(&rect_area, mask, ext->ddlist.sel_style, opa_scale);
}
/*Post draw when the children are drawn*/
else if(mode == LV_DESIGN_DRAW_POST) {
lv_style_t * style = lv_roller_get_style(roller, LV_ROLLER_STYLE_BG);
lv_roller_ext_t * ext = lv_obj_get_ext_attr(roller);
const lv_font_t * font = style->text.font;
lv_coord_t font_h = lv_font_get_height(font);
lv_opa_t opa_scale = lv_obj_get_opa_scale(roller);
/*Redraw the text on the selected area with a different color*/
lv_area_t rect_area;
rect_area.y1 = roller->coords.y1 + lv_obj_get_height(roller) / 2 - font_h / 2 - style->text.line_space / 2;
if((font_h & 0x1) && (style->text.line_space & 0x1)) rect_area.y1 --; /*Compensate the two rounding error*/
rect_area.y2 = rect_area.y1 + font_h + style->text.line_space - 1;
rect_area.x1 = roller->coords.x1;
rect_area.x2 = roller->coords.x2;
lv_area_t mask_sel;
bool area_ok;
area_ok = lv_area_intersect(&mask_sel, mask, &rect_area);
if(area_ok) {
lv_style_t * sel_style = lv_roller_get_style(roller, LV_ROLLER_STYLE_SEL);
lv_style_t new_style;
lv_txt_flag_t txt_align = LV_TXT_FLAG_NONE;
{
lv_label_align_t label_align = lv_label_get_align(ext->ddlist.label);
if(LV_LABEL_ALIGN_CENTER == label_align) {
txt_align |= LV_TXT_FLAG_CENTER;
} else if(LV_LABEL_ALIGN_RIGHT == label_align) {
txt_align |= LV_TXT_FLAG_RIGHT;
}
}
lv_style_copy(&new_style, style);
new_style.text.color = sel_style->text.color;
new_style.text.opa = sel_style->text.opa;
lv_draw_label(&ext->ddlist.label->coords, &mask_sel, &new_style, opa_scale,
lv_label_get_text(ext->ddlist.label), txt_align, NULL);
}
}
return true;
}
/**
* Signal function of the roller
* @param roller pointer to a roller object
* @param sign a signal type from lv_signal_t enum
* @param param pointer to a signal specific variable
* @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
*/
static lv_res_t lv_roller_signal(lv_obj_t * roller, lv_signal_t sign, void * param)
{
lv_res_t res = LV_RES_OK;
/*Don't let the drop down list to handle the control signals. It works differently*/
if(sign != LV_SIGNAL_CONTROLL && sign != LV_SIGNAL_FOCUS && sign != LV_SIGNAL_DEFOCUS) {
/* Include the ancient signal function */
res = ancestor_signal(roller, sign, param);
if(res != LV_RES_OK) return res;
}
lv_roller_ext_t * ext = lv_obj_get_ext_attr(roller);
lv_align_t obj_align = LV_ALIGN_IN_LEFT_MID;
if(ext->ddlist.label) {
lv_label_align_t label_align = lv_label_get_align(ext->ddlist.label);
if(LV_LABEL_ALIGN_CENTER == label_align) obj_align = LV_ALIGN_CENTER;
else if(LV_LABEL_ALIGN_RIGHT == label_align) obj_align = LV_ALIGN_IN_RIGHT_MID;
}
if(sign == LV_SIGNAL_STYLE_CHG) {
lv_obj_set_height(lv_page_get_scrl(roller),
lv_obj_get_height(ext->ddlist.label) + lv_obj_get_height(roller));
lv_obj_align(ext->ddlist.label, NULL, obj_align, 0, 0);
lv_ddlist_set_selected(roller, ext->ddlist.sel_opt_id);
refr_position(roller, false);
} else if(sign == LV_SIGNAL_CORD_CHG) {
if(lv_obj_get_width(roller) != lv_area_get_width(param) ||
lv_obj_get_height(roller) != lv_area_get_height(param)) {
lv_ddlist_set_fix_height(roller, lv_obj_get_height(roller));
lv_obj_set_height(lv_page_get_scrl(roller),
lv_obj_get_height(ext->ddlist.label) + lv_obj_get_height(roller));
lv_obj_align(ext->ddlist.label, NULL, obj_align, 0, 0);
lv_ddlist_set_selected(roller, ext->ddlist.sel_opt_id);
refr_position(roller, false);
}
} else if(sign == LV_SIGNAL_FOCUS) {
#if USE_LV_GROUP
lv_group_t * g = lv_obj_get_group(roller);
bool editing = lv_group_get_editing(g);
lv_hal_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(ext->ddlist.sel_opt_id != ext->ddlist.sel_opt_id_ori) {
ext->ddlist.sel_opt_id = ext->ddlist.sel_opt_id_ori;
refr_position(roller, true);
}
}
/*Save the current state when entered to edit mode*/
else {
ext->ddlist.sel_opt_id_ori = ext->ddlist.sel_opt_id;
}
} else {
ext->ddlist.sel_opt_id_ori = ext->ddlist.sel_opt_id; /*Save the current value. Used to revert this state if ENER wont't be pressed*/
}
#endif
} else if(sign == LV_SIGNAL_DEFOCUS) {
#if USE_LV_GROUP
/*Revert the original state*/
if(ext->ddlist.sel_opt_id != ext->ddlist.sel_opt_id_ori) {
ext->ddlist.sel_opt_id = ext->ddlist.sel_opt_id_ori;
refr_position(roller, true);
}
#endif
} else if(sign == LV_SIGNAL_CONTROLL) {
char c = *((char *)param);
if(c == LV_GROUP_KEY_RIGHT || c == LV_GROUP_KEY_DOWN) {
if(ext->ddlist.sel_opt_id + 1 < ext->ddlist.option_cnt) {
lv_roller_set_selected(roller, ext->ddlist.sel_opt_id + 1, true);
}
} else if(c == LV_GROUP_KEY_LEFT || c == LV_GROUP_KEY_UP) {
if(ext->ddlist.sel_opt_id > 0) {
lv_roller_set_selected(roller, ext->ddlist.sel_opt_id - 1, true);
}
} else if(c == LV_GROUP_KEY_ENTER) {
ext->ddlist.sel_opt_id_ori = ext->ddlist.sel_opt_id; /*Set the entered value as default*/
if(ext->ddlist.action) ext->ddlist.action(roller);
#if USE_LV_GROUP
lv_group_t * g = lv_obj_get_group(roller);
bool editing = lv_group_get_editing(g);
if(editing) lv_group_set_editing(g, false); /*In edit mode go to navigate mode if an option is selected*/
#endif
}
} else if(sign == LV_SIGNAL_GET_TYPE) {
lv_obj_type_t * buf = param;
uint8_t i;
for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/
if(buf->type[i] == NULL) break;
}
buf->type[i] = "lv_roller";
}
return res;
}
/**
* Signal function of the scrollable part of the roller.
* @param roller_scrl ointer to the scrollable part of roller (page)
* @param sign a signal type from lv_signal_t enum
* @param param pointer to a signal specific variable
* @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
*/
static lv_res_t lv_roller_scrl_signal(lv_obj_t * roller_scrl, lv_signal_t sign, void * param)
{
lv_res_t res;
/* Include the ancient signal function */
res = ancestor_scrl_signal(roller_scrl, sign, param);
if(res != LV_RES_OK) return res;
lv_indev_t * indev = lv_indev_get_act();
int32_t id = -1;
lv_obj_t * roller = lv_obj_get_parent(roller_scrl);
lv_roller_ext_t * ext = lv_obj_get_ext_attr(roller);
if(ext->ddlist.label == NULL) return LV_RES_INV; /*On delete the ddlist signal deletes the label so nothing left to do here*/
lv_style_t * style_label = lv_obj_get_style(ext->ddlist.label);
const lv_font_t * font = style_label->text.font;
lv_coord_t font_h = lv_font_get_height(font);
if(sign == LV_SIGNAL_DRAG_END) {
/*If dragged then align the list to there be an element in the middle*/
lv_coord_t label_y1 = ext->ddlist.label->coords.y1 - roller->coords.y1;
lv_coord_t label_unit = font_h + style_label->text.line_space;
lv_coord_t mid = (roller->coords.y2 - roller->coords.y1) / 2;
id = (mid - label_y1 + style_label->text.line_space / 2) / label_unit;
if(id < 0) id = 0;
if(id >= ext->ddlist.option_cnt) id = ext->ddlist.option_cnt - 1;
ext->ddlist.sel_opt_id = id;
if(ext->ddlist.action) ext->ddlist.action(roller);
} else if(sign == LV_SIGNAL_RELEASED) {
/*If picked an option by clicking then set it*/
if(!lv_indev_is_dragging(indev)) {
lv_point_t p;
lv_indev_get_point(indev, &p);
p.y = p.y - ext->ddlist.label->coords.y1;
id = p.y / (font_h + style_label->text.line_space);
if(id < 0) id = 0;
if(id >= ext->ddlist.option_cnt) id = ext->ddlist.option_cnt - 1;
ext->ddlist.sel_opt_id = id;
if(ext->ddlist.action) ext->ddlist.action(roller);
}
}
/*Position the scrollable according to the new selected option*/
if(id != -1) {
refr_position(roller, true);
}
return res;
}
/**
* Draw a rectangle which has gradient on its top and bottom
* @param roller pointer to a roller object
* @param mask pointer to the current mask (from the design function)
*/
static void draw_bg(lv_obj_t * roller, const lv_area_t * mask)
{
lv_style_t * style = lv_roller_get_style(roller, LV_ROLLER_STYLE_BG);
lv_area_t half_mask;
lv_area_t half_roller;
lv_coord_t h = lv_obj_get_height(roller);
bool union_ok;
lv_area_copy(&half_roller, &roller->coords);
half_roller.x1 -= roller->ext_size; /*Add ext size too (e.g. because of shadow draw) */
half_roller.x2 += roller->ext_size;
half_roller.y1 -= roller->ext_size;
half_roller.y2 = roller->coords.y1 + h / 2;
union_ok = lv_area_intersect(&half_mask, &half_roller, mask);
half_roller.x1 += roller->ext_size; /*Revert ext. size adding*/
half_roller.x2 -= roller->ext_size;
half_roller.y1 += roller->ext_size;
half_roller.y2 += style->body.radius;
if(union_ok) {
lv_draw_rect(&half_roller, &half_mask, style, lv_obj_get_opa_scale(roller));
}
half_roller.x1 -= roller->ext_size; /*Add ext size too (e.g. because of shadow draw) */
half_roller.x2 += roller->ext_size;
half_roller.y2 = roller->coords.y2 + roller->ext_size;
half_roller.y1 = roller->coords.y1 + h / 2;
if((h & 0x1) == 0) half_roller.y1++; /*With even height the pixels in the middle would be drawn twice*/
union_ok = lv_area_intersect(&half_mask, &half_roller, mask);
half_roller.x1 += roller->ext_size; /*Revert ext. size adding*/
half_roller.x2 -= roller->ext_size;
half_roller.y2 -= roller->ext_size;
half_roller.y1 -= style->body.radius;
if(union_ok) {
lv_color_t main_tmp = style->body.main_color;
lv_color_t grad_tmp = style->body.grad_color;
style->body.main_color = grad_tmp;
style->body.grad_color = main_tmp;
lv_draw_rect(&half_roller, &half_mask, style, lv_obj_get_opa_scale(roller));
style->body.main_color = main_tmp;
style->body.grad_color = grad_tmp;
}
}
/**
* Refresh the position of the roller. It uses the id stored in: ext->ddlist.selected_option_id
* @param roller pointer to a roller object
* @param anim_en true: refresh with animation; false: without animation
*/
static void refr_position(lv_obj_t * roller, bool anim_en)
{
#if USE_LV_ANIMATION == 0
anim_en = false;
#endif
lv_obj_t * roller_scrl = lv_page_get_scrl(roller);
lv_roller_ext_t * ext = lv_obj_get_ext_attr(roller);
lv_style_t * style_label = lv_obj_get_style(ext->ddlist.label);
const lv_font_t * font = style_label->text.font;
lv_coord_t font_h = lv_font_get_height(font);
lv_coord_t h = lv_obj_get_height(roller);
int32_t id = ext->ddlist.sel_opt_id;
lv_coord_t line_y1 = id * (font_h + style_label->text.line_space) + ext->ddlist.label->coords.y1 - roller_scrl->coords.y1;
lv_coord_t new_y = - line_y1 + (h - font_h) / 2;
if(ext->ddlist.anim_time == 0 || anim_en == false) {
lv_obj_set_y(roller_scrl, new_y);
} else {
#if USE_LV_ANIMATION
lv_anim_t a;
a.var = roller_scrl;
a.start = lv_obj_get_y(roller_scrl);
a.end = new_y;
a.fp = (lv_anim_fp_t)lv_obj_set_y;
a.path = lv_anim_path_linear;
a.end_cb = NULL;
a.act_time = 0;
a.time = ext->ddlist.anim_time;
a.playback = 0;
a.playback_pause = 0;
a.repeat = 0;
a.repeat_pause = 0;
lv_anim_create(&a);
#endif
}
}
#endif

View File

@@ -0,0 +1,224 @@
/**
* @file lv_roller.h
*
*/
#ifndef LV_ROLLER_H
#define LV_ROLLER_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#ifdef LV_CONF_INCLUDE_SIMPLE
#include "lv_conf.h"
#else
#include "../../lv_conf.h"
#endif
#if USE_LV_ROLLER != 0
/*Testing of dependencies*/
#if USE_LV_DDLIST == 0
#error "lv_roller: lv_ddlist is required. Enable it in lv_conf.h (USE_LV_DDLIST 1) "
#endif
#include "../lv_core/lv_obj.h"
#include "lv_ddlist.h"
#include "lv_label.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/*Data of roller*/
typedef struct {
lv_ddlist_ext_t ddlist; /*Ext. of ancestor*/
/*New data for this type */
} lv_roller_ext_t;
enum {
LV_ROLLER_STYLE_BG,
LV_ROLLER_STYLE_SEL,
};
typedef uint8_t lv_roller_style_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a roller object
* @param par pointer to an object, it will be the parent of the new roller
* @param copy pointer to a roller object, if not NULL then the new object will be copied from it
* @return pointer to the created roller
*/
lv_obj_t * lv_roller_create(lv_obj_t * par, const lv_obj_t * copy);
/*=====================
* Setter functions
*====================*/
/**
* Set the align of the roller's options (left, right or center[default])
* @param roller - pointer to a roller object
* @param align - one of lv_label_align_t values (left, right, center)
*/
void lv_roller_set_align(lv_obj_t * roller, lv_label_align_t align);
/**
* 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"
*/
static inline void lv_roller_set_options(lv_obj_t * roller, const char * options)
{
lv_ddlist_set_options(roller, options);
}
/**
* 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 true: set with animation; false set immediately
*/
void lv_roller_set_selected(lv_obj_t *roller, uint16_t sel_opt, bool anim_en);
/**
* Set a function to call when a new option is chosen
* @param roller pointer to a roller
* @param action pointer to a callback function
*/
static inline void lv_roller_set_action(lv_obj_t * roller, lv_action_t action)
{
lv_ddlist_set_action(roller, action);
}
/**
* 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 *roller, uint8_t row_cnt);
/**
* Enable or disable the horizontal fit to the content
* @param roller pointer to a roller
* @param en true: enable auto fit; false: disable auto fit
*/
static inline void lv_roller_set_hor_fit(lv_obj_t * roller, bool en)
{
lv_ddlist_set_hor_fit(roller, en);
}
/**
* Set the open/close animation time.
* @param roller pointer to a roller object
* @param anim_time: open/close animation time [ms]
*/
static inline void lv_roller_set_anim_time(lv_obj_t *roller, uint16_t anim_time)
{
lv_ddlist_set_anim_time(roller, anim_time);
}
/**
* Set a style of a roller
* @param roller pointer to a roller object
* @param type which style should be set
* @param style pointer to a style
*/
void lv_roller_set_style(lv_obj_t *roller, lv_roller_style_t type, lv_style_t *style);
/*=====================
* Getter functions
*====================*/
/**
* Get the align attribute. Default alignment after _create is LV_LABEL_ALIGN_CENTER
* @param roller pointer to a roller object
* @return LV_LABEL_ALIGN_LEFT, LV_LABEL_ALIGN_RIGHT or LV_LABEL_ALIGN_CENTER
*/
lv_label_align_t lv_roller_get_align(const lv_obj_t * roller);
/**
* Get the options of a roller
* @param roller pointer to roller object
* @return the options separated by '\n'-s (E.g. "Option1\nOption2\nOption3")
*/
static inline const char * lv_roller_get_options(const lv_obj_t *roller)
{
return lv_ddlist_get_options(roller);
}
/**
* 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);
*/
static inline uint16_t lv_roller_get_selected(const lv_obj_t *roller)
{
return lv_ddlist_get_selected(roller);
}
/**
* Get the current selected option as a string
* @param roller pointer to roller object
* @param buf pointer to an array to store the string
*/
static inline void lv_roller_get_selected_str(const lv_obj_t * roller, char * buf)
{
lv_ddlist_get_selected_str(roller, buf);
}
/**
* Get the "option selected" callback function
* @param roller pointer to a roller
* @return pointer to the call back function
*/
static inline lv_action_t lv_roller_get_action(const lv_obj_t * roller)
{
return lv_ddlist_get_action(roller);
}
/**
* Get the open/close animation time.
* @param roller pointer to a roller
* @return open/close animation time [ms]
*/
static inline uint16_t lv_roller_get_anim_time(const lv_obj_t * roller)
{
return lv_ddlist_get_anim_time(roller);
}
/**
* Get the auto width set attribute
* @param roller pointer to a roller object
* @return true: auto size enabled; false: manual width settings enabled
*/
bool lv_roller_get_hor_fit(const lv_obj_t *roller);
/**
* Get a style of a roller
* @param roller pointer to a roller object
* @param type which style should be get
* @return style pointer to a style
* */
lv_style_t * lv_roller_get_style(const lv_obj_t *roller, lv_roller_style_t type);
/**********************
* MACROS
**********************/
#endif /*USE_LV_ROLLER*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_ROLLER_H*/

View File

@@ -0,0 +1,520 @@
/**
* @file lv_slider.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_slider.h"
#if USE_LV_SLIDER != 0
#include "../lv_core/lv_group.h"
#include "../lv_draw/lv_draw.h"
#include "../lv_themes/lv_theme.h"
#include "../lv_misc/lv_math.h"
/*********************
* DEFINES
*********************/
#define LV_SLIDER_SIZE_MIN 4 /*hor. pad and ver. pad cannot make the bar or indicator smaller then this [px]*/
#define LV_SLIDER_NOT_PRESSED INT16_MIN
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static bool lv_slider_design(lv_obj_t * slider, const lv_area_t * mask, lv_design_mode_t mode);
static lv_res_t lv_slider_signal(lv_obj_t * slider, lv_signal_t sign, void * param);
/**********************
* STATIC VARIABLES
**********************/
static lv_design_func_t ancestor_design_f;
static lv_signal_func_t ancestor_signal;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Create a slider objects
* @param par pointer to an object, it will be the parent of the new slider
* @param copy pointer to a slider object, if not NULL then the new object will be copied from it
* @return pointer to the created slider
*/
lv_obj_t * lv_slider_create(lv_obj_t * par, const lv_obj_t * copy)
{
LV_LOG_TRACE("slider create started");
/*Create the ancestor slider*/
lv_obj_t * new_slider = lv_bar_create(par, copy);
lv_mem_assert(new_slider);
if(new_slider == NULL) return NULL;
if(ancestor_design_f == NULL) ancestor_design_f = lv_obj_get_design_func(new_slider);
if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_slider);
/*Allocate the slider type specific extended data*/
lv_slider_ext_t * ext = lv_obj_allocate_ext_attr(new_slider, sizeof(lv_slider_ext_t));
lv_mem_assert(ext);
if(ext == NULL) return NULL;
/*Initialize the allocated 'ext' */
ext->action = NULL;
ext->drag_value = LV_SLIDER_NOT_PRESSED;
ext->style_knob = &lv_style_pretty;
ext->knob_in = 0;
/*The signal and design functions are not copied so set them here*/
lv_obj_set_signal_func(new_slider, lv_slider_signal);
lv_obj_set_design_func(new_slider, lv_slider_design);
/*Init the new slider slider*/
if(copy == NULL) {
lv_obj_set_click(new_slider, true);
lv_obj_set_protect(new_slider, LV_PROTECT_PRESS_LOST);
/*Set the default styles*/
lv_theme_t * th = lv_theme_get_current();
if(th) {
lv_slider_set_style(new_slider, LV_SLIDER_STYLE_BG, th->slider.bg);
lv_slider_set_style(new_slider, LV_SLIDER_STYLE_INDIC, th->slider.indic);
lv_slider_set_style(new_slider, LV_SLIDER_STYLE_KNOB, th->slider.knob);
} else {
lv_slider_set_style(new_slider, LV_SLIDER_STYLE_KNOB, ext->style_knob);
}
}
/*Copy an existing slider*/
else {
lv_slider_ext_t * copy_ext = lv_obj_get_ext_attr(copy);
ext->style_knob = copy_ext->style_knob;
ext->action = copy_ext->action;
ext->knob_in = copy_ext->knob_in;
/*Refresh the style with new signal function*/
lv_obj_refresh_style(new_slider);
}
LV_LOG_INFO("slider created");
return new_slider;
}
/*=====================
* Setter functions
*====================*/
/**
* Set a function which will be called when a new value is set on the slider
* @param slider pointer to slider object
* @param action a callback function
*/
void lv_slider_set_action(lv_obj_t * slider, lv_action_t action)
{
lv_slider_ext_t * ext = lv_obj_get_ext_attr(slider);
ext->action = action;
}
/**
* Set the 'knob in' attribute of a slider
* @param slider pointer to slider object
* @param in true: the knob is drawn always in the slider;
* false: the knob can be out on the edges
*/
void lv_slider_set_knob_in(lv_obj_t * slider, bool in)
{
lv_slider_ext_t * ext = lv_obj_get_ext_attr(slider);
if(ext->knob_in == in) return;
ext->knob_in = in == false ? 0 : 1;
lv_obj_invalidate(slider);
}
/**
* Set a style of a slider
* @param slider pointer to a slider object
* @param type which style should be set
* @param style pointer to a style
*/
void lv_slider_set_style(lv_obj_t * slider, lv_slider_style_t type, lv_style_t * style)
{
lv_slider_ext_t * ext = lv_obj_get_ext_attr(slider);
switch(type) {
case LV_SLIDER_STYLE_BG:
lv_bar_set_style(slider, LV_BAR_STYLE_BG, style);
break;
case LV_SLIDER_STYLE_INDIC:
lv_bar_set_style(slider, LV_BAR_STYLE_INDIC, style);
break;
case LV_SLIDER_STYLE_KNOB:
ext->style_knob = style;
lv_obj_refresh_ext_size(slider);
break;
}
}
/*=====================
* Getter functions
*====================*/
/**
* Get the value of a slider
* @param slider pointer to a slider object
* @return the value of the slider
*/
int16_t lv_slider_get_value(const lv_obj_t * slider)
{
lv_slider_ext_t * ext = lv_obj_get_ext_attr(slider);
if(ext->drag_value != LV_SLIDER_NOT_PRESSED) return ext->drag_value;
else return lv_bar_get_value(slider);
}
/**
* Get the slider action function
* @param slider pointer to slider object
* @return the callback function
*/
lv_action_t lv_slider_get_action(const lv_obj_t * slider)
{
lv_slider_ext_t * ext = lv_obj_get_ext_attr(slider);
return ext->action;
}
/**
* Give the slider is being dragged or not
* @param slider pointer to a slider object
* @return true: drag in progress false: not dragged
*/
bool lv_slider_is_dragged(const lv_obj_t * slider)
{
lv_slider_ext_t * ext = lv_obj_get_ext_attr(slider);
return ext->drag_value == LV_SLIDER_NOT_PRESSED ? false : true;
}
/**
* Get the 'knob in' attribute of a slider
* @param slider pointer to slider object
* @return true: the knob is drawn always in the slider;
* false: the knob can be out on the edges
*/
bool lv_slider_get_knob_in(const lv_obj_t * slider)
{
lv_slider_ext_t * ext = lv_obj_get_ext_attr(slider);
return ext->knob_in == 0 ? false : true;
}
/**
* Get a style of a slider
* @param slider pointer to a slider object
* @param type which style should be get
* @return style pointer to a style
*/
lv_style_t * lv_slider_get_style(const lv_obj_t * slider, lv_slider_style_t type)
{
lv_style_t * style = NULL;
lv_slider_ext_t * ext = lv_obj_get_ext_attr(slider);
switch(type) {
case LV_SLIDER_STYLE_BG:
style = lv_bar_get_style(slider, LV_BAR_STYLE_BG);
break;
case LV_SLIDER_STYLE_INDIC:
style = lv_bar_get_style(slider, LV_BAR_STYLE_INDIC);
break;
case LV_SLIDER_STYLE_KNOB:
style = ext->style_knob;
break;
default:
style = NULL;
break;
}
return style;
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Handle the drawing related tasks of the sliders
* @param slider pointer to an object
* @param mask the object will be drawn only in this area
* @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area
* (return 'true' if yes)
* LV_DESIGN_DRAW: draw the object (always return 'true')
* LV_DESIGN_DRAW_POST: drawing after every children are drawn
* @param return true/false, depends on 'mode'
*/
static bool lv_slider_design(lv_obj_t * slider, const lv_area_t * mask, lv_design_mode_t mode)
{
/*Return false if the object is not covers the mask_p area*/
if(mode == LV_DESIGN_COVER_CHK) {
return false;
}
/*Draw the object*/
else if(mode == LV_DESIGN_DRAW_MAIN) {
lv_slider_ext_t * ext = lv_obj_get_ext_attr(slider);
lv_style_t * style_bg = lv_slider_get_style(slider, LV_SLIDER_STYLE_BG);
lv_style_t * style_knob = lv_slider_get_style(slider, LV_SLIDER_STYLE_KNOB);
lv_style_t * style_indic = lv_slider_get_style(slider, LV_SLIDER_STYLE_INDIC);
lv_opa_t opa_scale = lv_obj_get_opa_scale(slider);
lv_coord_t slider_w = lv_area_get_width(&slider->coords);
lv_coord_t slider_h = lv_area_get_height(&slider->coords);
/*Draw the bar*/
lv_area_t area_bg;
lv_area_copy(&area_bg, &slider->coords);
/*Be sure at least LV_SLIDER_SIZE_MIN size will remain*/
lv_coord_t pad_ver_bg = style_bg->body.padding.ver;
lv_coord_t pad_hor_bg = style_bg->body.padding.hor;
if(pad_ver_bg * 2 + LV_SLIDER_SIZE_MIN > lv_area_get_height(&area_bg)) {
pad_ver_bg = (lv_area_get_height(&area_bg) - LV_SLIDER_SIZE_MIN) >> 1;
}
if(pad_hor_bg * 2 + LV_SLIDER_SIZE_MIN > lv_area_get_width(&area_bg)) {
pad_hor_bg = (lv_area_get_width(&area_bg) - LV_SLIDER_SIZE_MIN) >> 1;
}
if(ext->knob_in) { /*Enable extra size if the knob is inside */
area_bg.x1 += pad_hor_bg;
area_bg.x2 -= pad_hor_bg;
area_bg.y1 += pad_hor_bg;
area_bg.y2 -= pad_hor_bg;
} else { /*Let space only in the perpendicular directions*/
area_bg.x1 += slider_w < slider_h ? pad_hor_bg : 0; /*Pad only for vertical slider*/
area_bg.x2 -= slider_w < slider_h ? pad_hor_bg : 0; /*Pad only for vertical slider*/
area_bg.y1 += slider_w > slider_h ? pad_ver_bg : 0; /*Pad only for horizontal slider*/
area_bg.y2 -= slider_w > slider_h ? pad_ver_bg : 0; /*Pad only for horizontal slider*/
}
#if USE_LV_GROUP == 0
lv_draw_rect(&area_bg, mask, style_bg, lv_obj_get_opa_scale(slider));
#else
/* Draw the borders later if the bar is focused.
* At value = 100% the indicator can cover to whole background and the focused style won't be visible*/
if(lv_obj_is_focused(slider)) {
lv_style_t style_tmp;
lv_style_copy(&style_tmp, style_bg);
style_tmp.body.border.width = 0;
lv_draw_rect(&area_bg, mask, &style_tmp, opa_scale);
} else {
lv_draw_rect(&area_bg, mask, style_bg, opa_scale);
}
#endif
/*Draw the indicator*/
lv_area_t area_indic;
lv_area_copy(&area_indic, &area_bg);
/*Be sure at least ver pad/hor pad width indicator will remain*/
lv_coord_t pad_ver_indic = style_indic->body.padding.ver;
lv_coord_t pad_hor_indic = style_indic->body.padding.hor;
if(pad_ver_indic * 2 + LV_SLIDER_SIZE_MIN > lv_area_get_height(&area_bg)) {
pad_ver_indic = (lv_area_get_height(&area_bg) - LV_SLIDER_SIZE_MIN) >> 1;
}
if(pad_hor_indic * 2 + LV_SLIDER_SIZE_MIN > lv_area_get_width(&area_bg)) {
pad_hor_indic = (lv_area_get_width(&area_bg) - LV_SLIDER_SIZE_MIN) >> 1;
}
area_indic.x1 += pad_hor_indic;
area_indic.x2 -= pad_hor_indic;
area_indic.y1 += pad_ver_indic;
area_indic.y2 -= pad_ver_indic;
lv_coord_t cur_value = lv_slider_get_value(slider);
lv_coord_t min_value = lv_slider_get_min_value(slider);
lv_coord_t max_value = lv_slider_get_max_value(slider);
/*If dragged draw to the drag position*/
if(ext->drag_value != LV_SLIDER_NOT_PRESSED) cur_value = ext->drag_value;
if(slider_w >= slider_h) {
area_indic.x2 = (int32_t)((int32_t)(lv_area_get_width(&area_indic)) * (cur_value - min_value)) / (max_value - min_value);
area_indic.x2 = area_indic.x1 + area_indic.x2 - 1;
} else {
area_indic.y1 = (int32_t)((int32_t)(lv_area_get_height(&area_indic)) * (cur_value - min_value)) / (max_value - min_value);
area_indic.y1 = area_indic.y2 - area_indic.y1 + 1;
}
if(cur_value != min_value) lv_draw_rect(&area_indic, mask, style_indic, opa_scale);
/*Before the knob add the border if required*/
#if USE_LV_GROUP
/* Draw the borders later if the bar is focused.
* At value = 100% the indicator can cover to whole background and the focused style won't be visible*/
if(lv_obj_is_focused(slider)) {
lv_style_t style_tmp;
lv_style_copy(&style_tmp, style_bg);
style_tmp.body.empty = 1;
style_tmp.body.shadow.width = 0;
lv_draw_rect(&area_bg, mask, &style_tmp, opa_scale);
}
#endif
/*Draw the knob*/
lv_area_t knob_area;
lv_area_copy(&knob_area, &slider->coords);
if(slider_w >= slider_h) {
if(ext->knob_in == 0) {
knob_area.x1 = area_indic.x2 - slider_h / 2;
knob_area.x2 = knob_area.x1 + slider_h - 1;
} else {
knob_area.x1 = (int32_t)((int32_t)(slider_w - slider_h - 1) * (cur_value - min_value)) / (max_value - min_value);
knob_area.x1 += slider->coords.x1;
knob_area.x2 = knob_area.x1 + slider_h - 1;
}
knob_area.y1 = slider->coords.y1;
knob_area.y2 = slider->coords.y2;
} else {
if(ext->knob_in == 0) {
knob_area.y1 = area_indic.y1 - slider_w / 2;
knob_area.y2 = knob_area.y1 + slider_w - 1;
} else {
knob_area.y2 = (int32_t)((int32_t)(slider_h - slider_w - 1) * (cur_value - min_value)) / (max_value - min_value);
knob_area.y2 = slider->coords.y2 - knob_area.y2;
knob_area.y1 = knob_area.y2 - slider_w - 1;
}
knob_area.x1 = slider->coords.x1;
knob_area.x2 = slider->coords.x2;
}
lv_draw_rect(&knob_area, mask, style_knob, opa_scale);
}
/*Post draw when the children are drawn*/
else if(mode == LV_DESIGN_DRAW_POST) {
}
return true;
}
/**
* Signal function of the slider
* @param slider pointer to a slider object
* @param sign a signal type from lv_signal_t enum
* @param param pointer to a signal specific variable
* @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
*/
static lv_res_t lv_slider_signal(lv_obj_t * slider, lv_signal_t sign, void * param)
{
lv_res_t res;
/* Include the ancient signal function */
res = ancestor_signal(slider, sign, param);
if(res != LV_RES_OK) return res;
lv_slider_ext_t * ext = lv_obj_get_ext_attr(slider);
lv_point_t p;
lv_coord_t w = lv_obj_get_width(slider);
lv_coord_t h = lv_obj_get_height(slider);
if(sign == LV_SIGNAL_PRESSED) {
ext->drag_value = lv_slider_get_value(slider);
} else if(sign == LV_SIGNAL_PRESSING) {
lv_indev_get_point(param, &p);
int16_t tmp = 0;
if(w > h) {
lv_coord_t knob_w = h;
p.x -= slider->coords.x1 + h / 2; /*Modify the point to shift with half knob (important on the start and end)*/
tmp = (int32_t)((int32_t) p.x * (ext->bar.max_value - ext->bar.min_value + 1)) / (w - knob_w);
tmp += ext->bar.min_value;
} else {
lv_coord_t knob_h = w;
p.y -= slider->coords.y1 + w / 2; /*Modify the point to shift with half knob (important on the start and end)*/
tmp = (int32_t)((int32_t) p.y * (ext->bar.max_value - ext->bar.min_value + 1)) / (h - knob_h);
tmp = ext->bar.max_value - tmp; /*Invert the value: smaller value means higher y*/
}
if(tmp < ext->bar.min_value) tmp = ext->bar.min_value;
else if(tmp > ext->bar.max_value) tmp = ext->bar.max_value;
if(tmp != ext->drag_value) {
ext->drag_value = tmp;
lv_obj_invalidate(slider);
if(ext->action != NULL) res = ext->action(slider);
}
} else if(sign == LV_SIGNAL_RELEASED || sign == LV_SIGNAL_PRESS_LOST) {
lv_slider_set_value(slider, ext->drag_value);
ext->drag_value = LV_SLIDER_NOT_PRESSED;
if(ext->action != NULL) res = ext->action(slider);
} else if(sign == LV_SIGNAL_CORD_CHG) {
/* The knob size depends on slider size.
* During the drawing method the ext. size is used by the knob so refresh the ext. size.*/
if(lv_obj_get_width(slider) != lv_area_get_width(param) ||
lv_obj_get_height(slider) != lv_area_get_height(param)) {
slider->signal_func(slider, LV_SIGNAL_REFR_EXT_SIZE, NULL);
}
} else if(sign == LV_SIGNAL_REFR_EXT_SIZE) {
lv_style_t * style = lv_slider_get_style(slider, LV_SLIDER_STYLE_BG);
lv_style_t * knob_style = lv_slider_get_style(slider, LV_SLIDER_STYLE_KNOB);
lv_coord_t shadow_w = knob_style->body.shadow.width;
if(ext->knob_in == 0) {
/* The smaller size is the knob diameter*/
lv_coord_t x = LV_MATH_MIN(w / 2 + 1 + shadow_w, h / 2 + 1 + shadow_w);
if(slider->ext_size < x) slider->ext_size = x;
} else {
lv_coord_t pad = LV_MATH_MIN(style->body.padding.hor, style->body.padding.ver);
if(pad < 0) pad = -pad;
if(slider->ext_size < pad) slider->ext_size = pad;
if(slider->ext_size < shadow_w) slider->ext_size = shadow_w;
}
} else if(sign == LV_SIGNAL_CONTROLL) {
char c = *((char *)param);
ext->drag_value = LV_SLIDER_NOT_PRESSED;
#if USE_LV_GROUP
lv_group_t * g = lv_obj_get_group(slider);
bool editing = lv_group_get_editing(g);
lv_hal_indev_type_t indev_type = lv_indev_get_type(lv_indev_get_act());
/*Encoders need special handling*/
if(indev_type == LV_INDEV_TYPE_ENCODER && c == LV_GROUP_KEY_ENTER) {
if(editing) lv_group_set_editing(g, false);
}
#endif
if(c == LV_GROUP_KEY_RIGHT || c == LV_GROUP_KEY_UP) {
lv_slider_set_value(slider, lv_slider_get_value(slider) + 1);
if(ext->action != NULL) res = ext->action(slider);
} else if(c == LV_GROUP_KEY_LEFT || c == LV_GROUP_KEY_DOWN) {
lv_slider_set_value(slider, lv_slider_get_value(slider) - 1);
if(ext->action != NULL) res = ext->action(slider);
}
} else if(sign == LV_SIGNAL_GET_EDITABLE) {
bool * editable = (bool *)param;
*editable = true;
} else if(sign == LV_SIGNAL_GET_TYPE) {
lv_obj_type_t * buf = param;
uint8_t i;
for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/
if(buf->type[i] == NULL) break;
}
buf->type[i] = "lv_slider";
}
return res;
}
#endif

View File

@@ -0,0 +1,202 @@
/**
* @file lv_slider.h
*
*/
#ifndef LV_SLIDER_H
#define LV_SLIDER_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#ifdef LV_CONF_INCLUDE_SIMPLE
#include "lv_conf.h"
#else
#include "../../lv_conf.h"
#endif
#if USE_LV_SLIDER != 0
/*Testing of dependencies*/
#if USE_LV_BAR == 0
#error "lv_slider: lv_bar is required. Enable it in lv_conf.h (USE_LV_BAR 1) "
#endif
#include "../lv_core/lv_obj.h"
#include "lv_bar.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/*Data of slider*/
typedef struct
{
lv_bar_ext_t bar; /*Ext. of ancestor*/
/*New data for this type */
lv_action_t action; /*Function to call when a new value is set*/
lv_style_t *style_knob; /*Style of the knob*/
int16_t drag_value; /*Store a temporal value during press until release (Handled by the library)*/
uint8_t knob_in :1; /*1: Draw the knob inside the bar*/
} lv_slider_ext_t;
/*Built-in styles of slider*/
enum
{
LV_SLIDER_STYLE_BG,
LV_SLIDER_STYLE_INDIC,
LV_SLIDER_STYLE_KNOB,
};
typedef uint8_t lv_slider_style_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a slider objects
* @param par pointer to an object, it will be the parent of the new slider
* @param copy pointer to a slider object, if not NULL then the new object will be copied from it
* @return pointer to the created slider
*/
lv_obj_t * lv_slider_create(lv_obj_t * par, const lv_obj_t * copy);
/*=====================
* Setter functions
*====================*/
/**
* Set a new value on the slider
* @param slider pointer to a slider object
* @param value new value
*/
static inline void lv_slider_set_value(lv_obj_t * slider, int16_t value)
{
lv_bar_set_value(slider, value);
}
/**
* Set a new value with animation on a slider
* @param slider pointer to a slider object
* @param value new value
* @param anim_time animation time in milliseconds
*/
static inline void lv_slider_set_value_anim(lv_obj_t * slider, int16_t value, uint16_t anim_time)
{
lv_bar_set_value_anim(slider, value, anim_time);
}
/**
* Set minimum and the maximum values of a bar
* @param slider pointer to the slider object
* @param min minimum value
* @param max maximum value
*/
static inline void lv_slider_set_range(lv_obj_t *slider, int16_t min, int16_t max)
{
lv_bar_set_range(slider, min, max);
}
/**
* Set a function which will be called when a new value is set on the slider
* @param slider pointer to slider object
* @param action a callback function
*/
void lv_slider_set_action(lv_obj_t * slider, lv_action_t action);
/**
* Set the 'knob in' attribute of a slider
* @param slider pointer to slider object
* @param in true: the knob is drawn always in the slider;
* false: the knob can be out on the edges
*/
void lv_slider_set_knob_in(lv_obj_t * slider, bool in);
/**
* Set a style of a slider
* @param slider pointer to a slider object
* @param type which style should be set
* @param style pointer to a style
*/
void lv_slider_set_style(lv_obj_t *slider, lv_slider_style_t type, lv_style_t *style);
/*=====================
* Getter functions
*====================*/
/**
* Get the value of a slider
* @param slider pointer to a slider object
* @return the value of the slider
*/
int16_t lv_slider_get_value(const lv_obj_t * slider);
/**
* Get the minimum value of a slider
* @param slider pointer to a slider object
* @return the minimum value of the slider
*/
static inline int16_t lv_slider_get_min_value(const lv_obj_t * slider)
{
return lv_bar_get_min_value(slider);
}
/**
* Get the maximum value of a slider
* @param slider pointer to a slider object
* @return the maximum value of the slider
*/
static inline int16_t lv_slider_get_max_value(const lv_obj_t * slider)
{
return lv_bar_get_max_value(slider);
}
/**
* Get the slider action function
* @param slider pointer to slider object
* @return the callback function
*/
lv_action_t lv_slider_get_action(const lv_obj_t * slider);
/**
* Give the slider is being dragged or not
* @param slider pointer to a slider object
* @return true: drag in progress false: not dragged
*/
bool lv_slider_is_dragged(const lv_obj_t * slider);
/**
* Get the 'knob in' attribute of a slider
* @param slider pointer to slider object
* @return true: the knob is drawn always in the slider;
* false: the knob can be out on the edges
*/
bool lv_slider_get_knob_in(const lv_obj_t * slider);
/**
* Get a style of a slider
* @param slider pointer to a slider object
* @param type which style should be get
* @return style pointer to a style
*/
lv_style_t * lv_slider_get_style(const lv_obj_t *slider, lv_slider_style_t type);
/**********************
* MACROS
**********************/
#endif /*USE_LV_SLIDER*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_SLIDER_H*/

View File

@@ -0,0 +1,471 @@
/**
* @file lv_spinbox.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_spinbox.h"
#if USE_LV_SPINBOX != 0
#include "../lv_themes/lv_theme.h"
#include "../lv_misc/lv_math.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static lv_res_t lv_spinbox_signal(lv_obj_t * spinbox, lv_signal_t sign, void * param);
static void lv_spinbox_updatevalue(lv_obj_t * spinbox);
/**********************
* STATIC VARIABLES
**********************/
static lv_signal_func_t ancestor_signal;
static lv_design_func_t ancestor_design;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Create a spinbox object
* @param par pointer to an object, it will be the parent of the new spinbox
* @param copy pointer to a spinbox object, if not NULL then the new object will be copied from it
* @return pointer to the created spinbox
*/
lv_obj_t * lv_spinbox_create(lv_obj_t * par, const lv_obj_t * copy)
{
LV_LOG_TRACE("spinbox create started");
/*Create the ancestor of spinbox*/
lv_obj_t * new_spinbox = lv_ta_create(par, copy);
lv_mem_assert(new_spinbox);
if(new_spinbox == NULL) return NULL;
/*Allocate the spinbox type specific extended data*/
lv_spinbox_ext_t * ext = lv_obj_allocate_ext_attr(new_spinbox, sizeof(lv_spinbox_ext_t));
lv_mem_assert(ext);
if(ext == NULL) return NULL;
if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_spinbox);
if(ancestor_design == NULL) ancestor_design = lv_obj_get_design_func(new_spinbox);
/*Initialize the allocated 'ext'*/
ext->ta.one_line = 1;
ext->ta.pwd_mode = 0;
ext->ta.accapted_chars = "1234567890+-. ";
ext->value = 0;
ext->dec_point_pos = 0;
ext->digit_count = 5;
ext->digit_padding_left = 0;
ext->step = 1;
ext->range_max = 99999;
ext->range_min = -99999;
ext->value_changed_cb = NULL;
lv_ta_set_cursor_type(new_spinbox, LV_CURSOR_BLOCK | LV_CURSOR_HIDDEN); /*hidden by default*/
/*The signal and design functions are not copied so set them here*/
lv_obj_set_signal_func(new_spinbox, lv_spinbox_signal);
lv_obj_set_design_func(new_spinbox, ancestor_design); /*Leave the Text area's design function*/
/*Init the new spinbox spinbox*/
if(copy == NULL) {
/*Set the default styles*/
lv_theme_t * th = lv_theme_get_current();
if(th) {
lv_spinbox_set_style(new_spinbox, LV_SPINBOX_STYLE_BG, th->spinbox.bg);
lv_spinbox_set_style(new_spinbox, LV_SPINBOX_STYLE_CURSOR, th->spinbox.cursor);
lv_spinbox_set_style(new_spinbox, LV_SPINBOX_STYLE_SB, th->spinbox.sb);
}
}
/*Copy an existing spinbox*/
else {
lv_spinbox_ext_t * copy_ext = lv_obj_get_ext_attr(copy);
lv_spinbox_set_value(new_spinbox, copy_ext->value);
lv_spinbox_set_digit_format(new_spinbox, copy_ext->digit_count, copy_ext->dec_point_pos);
lv_spinbox_set_range(new_spinbox, copy_ext->range_min, copy_ext->range_max);
lv_spinbox_set_step(new_spinbox, copy_ext->step);
/*Refresh the style with new signal function*/
lv_obj_refresh_style(new_spinbox);
}
lv_spinbox_updatevalue(new_spinbox);
LV_LOG_INFO("spinbox created");
return new_spinbox;
}
/*=====================
* Setter functions
*====================*/
/**
* Set spinbox value
* @param spinbox pointer to spinbox
* @param i value to be set
*/
void lv_spinbox_set_value(lv_obj_t * spinbox, int32_t i)
{
lv_spinbox_ext_t * ext = lv_obj_get_ext_attr(spinbox);
if(ext == NULL)
return;
if(i > ext->range_max)
i = ext->range_max;
if(i < ext->range_min)
i = ext->range_min;
ext->value = i;
lv_spinbox_updatevalue(spinbox);
}
/**
* Set spinbox digit format (digit count and decimal format)
* @param spinbox pointer to spinbox
* @param digit_count number of digit excluding the decimal separator and the sign
* @param separator_position number of digit before the decimal point. If 0, decimal point is not shown
*/
void lv_spinbox_set_digit_format(lv_obj_t * spinbox, uint8_t digit_count, uint8_t separator_position)
{
lv_spinbox_ext_t * ext = lv_obj_get_ext_attr(spinbox);
if(ext == NULL)
return;
if(digit_count > LV_SPINBOX_MAX_DIGIT_COUNT)
digit_count = LV_SPINBOX_MAX_DIGIT_COUNT;
if(separator_position > LV_SPINBOX_MAX_DIGIT_COUNT)
separator_position = LV_SPINBOX_MAX_DIGIT_COUNT;
ext->digit_count = digit_count;
ext->dec_point_pos = separator_position;
lv_spinbox_updatevalue(spinbox);
}
/**
* Set spinbox step
* @param spinbox pointer to spinbox
* @param step steps on increment/decrement
*/
void lv_spinbox_set_step(lv_obj_t * spinbox, uint32_t step)
{
lv_spinbox_ext_t * ext = lv_obj_get_ext_attr(spinbox);
if(ext == NULL) return;
ext->step = step;
}
/**
* Set spinbox value range
* @param spinbox pointer to spinbox
* @param range_min maximum value, inclusive
* @param range_max minimum value, inclusive
*/
void lv_spinbox_set_range(lv_obj_t * spinbox, int32_t range_min, int32_t range_max)
{
lv_spinbox_ext_t * ext = lv_obj_get_ext_attr(spinbox);
if(ext == NULL) return;
ext->range_max = range_max;
ext->range_min = range_min;
if(ext->value > ext->range_max) {
ext->value = ext->range_max;
lv_obj_invalidate(spinbox);
}
if(ext->value < ext->range_min) {
ext->value = ext->range_min;
lv_obj_invalidate(spinbox);
}
}
/**
* Set spinbox callback on calue change
* @param spinbox pointer to spinbox
* @param cb Callback function called on value change event
*/
void lv_spinbox_set_value_changed_cb(lv_obj_t * spinbox, lv_spinbox_value_changed_cb_t cb)
{
lv_spinbox_ext_t * ext = lv_obj_get_ext_attr(spinbox);
ext->value_changed_cb = cb;
}
/**
* Set spinbox left padding in digits count (added between sign and first digit)
* @param spinbox pointer to spinbox
* @param cb Callback function called on value change event
*/
void lv_spinbox_set_padding_left(lv_obj_t * spinbox, uint8_t padding)
{
lv_spinbox_ext_t * ext = lv_obj_get_ext_attr(spinbox);
ext->digit_padding_left = padding;
lv_spinbox_updatevalue(spinbox);
}
/*=====================
* Getter functions
*====================*/
/**
* Get the spinbox numeral value (user has to convert to float according to its digit format)
* @param spinbox pointer to spinbox
* @return value integer value of the spinbox
*/
int32_t lv_spinbox_get_value(lv_obj_t * spinbox)
{
lv_spinbox_ext_t * ext = lv_obj_get_ext_attr(spinbox);
return ext->value;
}
/*=====================
* Other functions
*====================*/
/**
* Select next lower digit for edition by dividing the step by 10
* @param spinbox pointer to spinbox
*/
void lv_spinbox_step_next(lv_obj_t * spinbox)
{
lv_spinbox_ext_t * ext = lv_obj_get_ext_attr(spinbox);
int32_t new_step = ext->step / 10;
if((new_step) > 0) ext->step = new_step;
else ext->step = 1;
lv_spinbox_updatevalue(spinbox);
}
/**
* Select next higher digit for edition by multiplying the step by 10
* @param spinbox pointer to spinbox
*/
void lv_spinbox_step_previous(lv_obj_t * spinbox)
{
lv_spinbox_ext_t * ext = lv_obj_get_ext_attr(spinbox);
int32_t step_limit;
step_limit = LV_MATH_MAX(ext->range_max, (ext->range_min < 0 ? (-ext->range_min) : ext->range_min));
int32_t new_step = ext->step * 10;
if(new_step <= step_limit) ext->step = new_step;
lv_spinbox_updatevalue(spinbox);
}
/**
* Increment spinbox value by one step
* @param spinbox pointer to spinbox
*/
void lv_spinbox_increment(lv_obj_t * spinbox)
{
lv_spinbox_ext_t * ext = lv_obj_get_ext_attr(spinbox);
if(ext->value + ext->step <= ext->range_max) {
/*Special mode when zero crossing*/
if((ext->value + ext->step) > 0 && ext->value < 0) ext->value = -ext->value;
ext->value += ext->step;
} else {
ext->value = ext->range_max;
}
if(ext->value_changed_cb != NULL) ext->value_changed_cb(spinbox, ext->value);
lv_spinbox_updatevalue(spinbox);
}
/**
* Decrement spinbox value by one step
* @param spinbox pointer to spinbox
*/
void lv_spinbox_decrement(lv_obj_t * spinbox)
{
lv_spinbox_ext_t * ext = lv_obj_get_ext_attr(spinbox);
if(ext->value - ext->step >= ext->range_min) {
/*Special mode when zero crossing*/
if((ext->value - ext->step) < 0 && ext->value > 0) ext->value = -ext->value;
ext->value -= ext->step;
} else {
ext->value = ext->range_min;
}
if(ext->value_changed_cb != NULL) ext->value_changed_cb(spinbox, ext->value);
lv_spinbox_updatevalue(spinbox);
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Signal function of the spinbox
* @param spinbox pointer to a spinbox object
* @param sign a signal type from lv_signal_t enum
* @param param pointer to a signal specific variable
* @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
*/
static lv_res_t lv_spinbox_signal(lv_obj_t * spinbox, lv_signal_t sign, void * param)
{
lv_spinbox_ext_t * ext = lv_obj_get_ext_attr(spinbox);
lv_res_t res = LV_RES_OK;
/* Include the ancient signal function */
if(sign != LV_SIGNAL_CONTROLL)
{
res = ancestor_signal(spinbox, sign, param);
if(res != LV_RES_OK) return res;
}
if(sign == LV_SIGNAL_CLEANUP) {
/*Nothing to cleanup. (No dynamically allocated memory in 'ext')*/
} else if(sign == LV_SIGNAL_GET_TYPE)
{
lv_obj_type_t * buf = param;
uint8_t i;
for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++)
{ /*Find the last set data*/
if(buf->type[i] == NULL) break;
}
buf->type[i] = "lv_spinbox";
}
else if(sign == LV_SIGNAL_CONTROLL) {
lv_hal_indev_type_t indev_type = lv_indev_get_type(lv_indev_get_act());
uint32_t c = *((uint32_t *)param); /*uint32_t because can be UTF-8*/
if(c == LV_GROUP_KEY_RIGHT) {
if(indev_type == LV_INDEV_TYPE_ENCODER) lv_spinbox_increment(spinbox);
else lv_spinbox_step_next(spinbox);
}
else if(c == LV_GROUP_KEY_LEFT) {
if(indev_type == LV_INDEV_TYPE_ENCODER) lv_spinbox_decrement(spinbox);
else lv_spinbox_step_previous(spinbox);
}
else if(c == LV_GROUP_KEY_UP) {
lv_spinbox_increment(spinbox);
}
else if(c == LV_GROUP_KEY_DOWN) {
lv_spinbox_decrement(spinbox);
}
else if(c == LV_GROUP_KEY_ENTER) {
if(ext->step > 1) {
lv_spinbox_step_next(spinbox);
} else {
/*Restart from the MSB*/
ext->step = 1;
uint32_t i;
for(i = 0; i < ext->digit_count; i++) {
int32_t new_step = ext->step * 10;
if(new_step >= ext->range_max) break;
ext->step = new_step;
}
lv_spinbox_step_previous(spinbox);
}
}
else {
lv_ta_add_char(spinbox, c);
}
}
return res;
}
static void lv_spinbox_updatevalue(lv_obj_t * spinbox)
{
lv_spinbox_ext_t * ext = lv_obj_get_ext_attr(spinbox);
char buf[LV_SPINBOX_MAX_DIGIT_COUNT + 8];
memset(buf, 0, sizeof(buf));
char * buf_p = buf;
/*Add the sign*/
(*buf_p) = ext->value >= 0 ? '+' : '-';
buf_p++;
int i;
/*padding left*/
for(i = 0; i < ext->digit_padding_left; i++) {
(*buf_p) = ' ';
buf_p++;
}
char digits[64];
/*Convert the numbers to string (the sign is already handled so always covert positive number)*/
lv_math_num_to_str(ext->value < 0 ? -ext->value : ext->value, digits);
/*Add leading zeros*/
int lz_cnt = ext->digit_count - (int)strlen(digits);
if(lz_cnt > 0) {
for(i = strlen(digits); i >= 0; i--) {
digits[i + lz_cnt] = digits[i];
}
for(i = 0; i < lz_cnt; i++) {
digits[i] = '0';
}
}
int32_t intDigits;
intDigits = (ext->dec_point_pos == 0) ? ext->digit_count : ext->dec_point_pos;
/*Add the decimal part*/
for(i = 0; i < intDigits && digits[i] != '\0'; i++) {
(*buf_p) = digits[i];
buf_p++;
}
if(ext->dec_point_pos != 0) {
/*Insert the decimal point*/
(*buf_p) = '.';
buf_p++;
for(/*Leave i*/ ;i < ext->digit_count && digits[i] != '\0'; i++) {
(*buf_p) = digits[i];
buf_p++;
}
}
/*Refresh the text*/
lv_ta_set_text(spinbox, (char*)buf);
/*Set the cursor position*/
int32_t step = ext->step;
uint8_t cur_pos = ext->digit_count;
while(step >= 10)
{
step /= 10;
cur_pos--;
}
if(cur_pos > intDigits ) cur_pos ++; /*Skip teh decimal point*/
cur_pos += ext->digit_padding_left;
lv_ta_set_cursor_pos(spinbox, cur_pos);
}
#endif

View File

@@ -0,0 +1,201 @@
/**
* @file lv_spinbox.h
*
*/
#ifndef LV_SPINBOX_H
#define LV_SPINBOX_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#ifdef LV_CONF_INCLUDE_SIMPLE
#include "lv_conf.h"
#else
#include "../../lv_conf.h"
#endif
#if USE_LV_SPINBOX != 0
/*Testing of dependencies*/
#if USE_LV_TA == 0
#error "lv_spinbox: lv_ta is required. Enable it in lv_conf.h (USE_LV_TA 1) "
#endif
#include "../lv_core/lv_obj.h"
#include "../lv_objx/lv_ta.h"
/*********************
* DEFINES
*********************/
#define LV_SPINBOX_MAX_DIGIT_COUNT 16
/**********************
* TYPEDEFS
**********************/
/*callback on value change*/
typedef void (*lv_spinbox_value_changed_cb_t)(lv_obj_t * spinbox, int32_t new_value);
/*Data of spinbox*/
typedef struct {
lv_ta_ext_t ta; /*Ext. of ancestor*/
/*New data for this type */
int32_t value;
int32_t range_max;
int32_t range_min;
int32_t step;
uint16_t digit_count:4;
uint16_t dec_point_pos:4; /*if 0, there is no separator and the number is an integer*/
uint16_t digit_padding_left:4;
lv_spinbox_value_changed_cb_t value_changed_cb;
} lv_spinbox_ext_t;
/*Styles*/
enum {
LV_SPINBOX_STYLE_BG,
LV_SPINBOX_STYLE_SB,
LV_SPINBOX_STYLE_CURSOR,
};
typedef uint8_t lv_spinbox_style_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a spinbox objects
* @param par pointer to an object, it will be the parent of the new spinbox
* @param copy pointer to a spinbox object, if not NULL then the new object will be copied from it
* @return pointer to the created spinbox
*/
lv_obj_t * lv_spinbox_create(lv_obj_t * par, const lv_obj_t * copy);
/*=====================
* Setter functions
*====================*/
/**
* Set a style of a spinbox.
* @param templ pointer to template object
* @param type which style should be set
* @param style pointer to a style
*/
static inline void lv_spinbox_set_style(lv_obj_t * spinbox, lv_spinbox_style_t type, lv_style_t *style)
{
lv_ta_set_style(spinbox, type, style);
}
/**
* Set spinbox value
* @param spinbox pointer to spinbox
* @param i value to be set
*/
void lv_spinbox_set_value(lv_obj_t * spinbox, int32_t i);
/**
* Set spinbox digit format (digit count and decimal format)
* @param spinbox pointer to spinbox
* @param digit_count number of digit excluding the decimal separator and the sign
* @param separator_position number of digit before the decimal point. If 0, decimal point is not shown
*/
void lv_spinbox_set_digit_format(lv_obj_t * spinbox, uint8_t digit_count, uint8_t separator_position);
/**
* Set spinbox step
* @param spinbox pointer to spinbox
* @param step steps on increment/decrement
*/
void lv_spinbox_set_step(lv_obj_t * spinbox, uint32_t step);
/**
* Set spinbox value range
* @param spinbox pointer to spinbox
* @param range_min maximum value, inclusive
* @param range_max minimum value, inclusive
*/
void lv_spinbox_set_range(lv_obj_t * spinbox, int32_t range_min, int32_t range_max);
/**
* Set spinbox callback on calue change
* @param spinbox pointer to spinbox
* @param cb Callback function called on value change event
*/
void lv_spinbox_set_value_changed_cb(lv_obj_t * spinbox, lv_spinbox_value_changed_cb_t cb);
/**
* Set spinbox left padding in digits count (added between sign and first digit)
* @param spinbox pointer to spinbox
* @param cb Callback function called on value change event
*/
void lv_spinbox_set_padding_left(lv_obj_t * spinbox, uint8_t padding);
/*=====================
* Getter functions
*====================*/
/**
* Get style of a spinbox.
* @param templ pointer to template object
* @param type which style should be get
* @return style pointer to the style
*/
static inline lv_style_t * lv_spinbox_get_style(lv_obj_t * spinbox, lv_spinbox_style_t type)
{
return lv_ta_get_style(spinbox, type);
}
/**
* Get the spinbox numeral value (user has to convert to float according to its digit format)
* @param spinbox pointer to spinbox
* @return value integer value of the spinbox
*/
int32_t lv_spinbox_get_value(lv_obj_t * spinbox);
/*=====================
* Other functions
*====================*/
/**
* Select next lower digit for edition by dividing the step by 10
* @param spinbox pointer to spinbox
*/
void lv_spinbox_step_next(lv_obj_t * spinbox);
/**
* Select next higher digit for edition by multiplying the step by 10
* @param spinbox pointer to spinbox
*/
void lv_spinbox_step_previous(lv_obj_t * spinbox);
/**
* Increment spinbox value by one step
* @param spinbox pointer to spinbox
*/
void lv_spinbox_increment(lv_obj_t * spinbox);
/**
* Decrement spinbox value by one step
* @param spinbox pointer to spinbox
*/
void lv_spinbox_decrement(lv_obj_t * spinbox);
/**********************
* MACROS
**********************/
#endif /*USE_LV_SPINBOX*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_SPINBOX_H*/

View File

@@ -0,0 +1,445 @@
/**
* @file lv_sw.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_sw.h"
#if USE_LV_SW != 0
/*Testing of dependencies*/
#if USE_LV_SLIDER == 0
#error "lv_sw: lv_slider is required. Enable it in lv_conf.h (USE_LV_SLIDER 1) "
#endif
#include "../lv_themes/lv_theme.h"
#include "../lv_misc/lv_math.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static lv_res_t lv_sw_signal(lv_obj_t * sw, lv_signal_t sign, void * param);
static void lv_sw_anim_to_value(lv_obj_t * sw, int16_t value);
/**********************
* STATIC VARIABLES
**********************/
static lv_signal_func_t ancestor_signal;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Create a switch objects
* @param par pointer to an object, it will be the parent of the new switch
* @param copy pointer to a switch object, if not NULL then the new object will be copied from it
* @return pointer to the created switch
*/
lv_obj_t * lv_sw_create(lv_obj_t * par, const lv_obj_t * copy)
{
LV_LOG_TRACE("switch create started");
/*Create the ancestor of switch*/
lv_obj_t * new_sw = lv_slider_create(par, copy);
lv_mem_assert(new_sw);
if(new_sw == NULL) return NULL;
if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_sw);
/*Allocate the switch type specific extended data*/
lv_sw_ext_t * ext = lv_obj_allocate_ext_attr(new_sw, sizeof(lv_sw_ext_t));
lv_mem_assert(ext);
if(ext == NULL) return NULL;
/*Initialize the allocated 'ext' */
ext->changed = 0;
#if USE_LV_ANIMATION
ext->anim_time = 0;
#endif
ext->style_knob_off = ext->slider.style_knob;
ext->style_knob_on = ext->slider.style_knob;
/*The signal and design functions are not copied so set them here*/
lv_obj_set_signal_func(new_sw, lv_sw_signal);
/*Init the new switch switch*/
if(copy == NULL) {
lv_slider_set_range(new_sw, 0, 1);
lv_obj_set_size(new_sw, 2 * LV_DPI / 3, LV_DPI / 3);
lv_slider_set_knob_in(new_sw, true);
lv_slider_set_range(new_sw, 0, LV_SWITCH_SLIDER_ANIM_MAX);
/*Set the default styles*/
lv_theme_t * th = lv_theme_get_current();
if(th) {
lv_sw_set_style(new_sw, LV_SW_STYLE_BG, th->sw.bg);
lv_sw_set_style(new_sw, LV_SW_STYLE_INDIC, th->sw.indic);
lv_sw_set_style(new_sw, LV_SW_STYLE_KNOB_OFF, th->sw.knob_off);
lv_sw_set_style(new_sw, LV_SW_STYLE_KNOB_ON, th->sw.knob_on);
} else {
/*Let the slider' style*/
}
}
/*Copy an existing switch*/
else {
lv_sw_ext_t * copy_ext = lv_obj_get_ext_attr(copy);
ext->style_knob_off = copy_ext->style_knob_off;
ext->style_knob_on = copy_ext->style_knob_on;
#if USE_LV_ANIMATION
ext->anim_time = copy_ext->anim_time;
#endif
if(lv_sw_get_state(new_sw)) lv_slider_set_style(new_sw, LV_SLIDER_STYLE_KNOB, ext->style_knob_on);
else lv_slider_set_style(new_sw, LV_SLIDER_STYLE_KNOB, ext->style_knob_off);
/*Refresh the style with new signal function*/
lv_obj_refresh_style(new_sw);
}
LV_LOG_INFO("switch created");
return new_sw;
}
/*=====================
* Setter functions
*====================*/
/**
* Turn ON the switch
* @param sw pointer to a switch object
*/
void lv_sw_on(lv_obj_t * sw)
{
lv_sw_ext_t * ext = lv_obj_get_ext_attr(sw);
lv_slider_set_value(sw, LV_SWITCH_SLIDER_ANIM_MAX);
lv_slider_set_style(sw, LV_SLIDER_STYLE_KNOB, ext->style_knob_on);
}
/**
* Turn OFF the switch
* @param sw pointer to a switch object
*/
void lv_sw_off(lv_obj_t * sw)
{
lv_sw_ext_t * ext = lv_obj_get_ext_attr(sw);
lv_slider_set_value(sw, 0);
lv_slider_set_style(sw, LV_SLIDER_STYLE_KNOB, ext->style_knob_off);
}
/**
* Toggle the position of the switch
* @param sw pointer to a switch object
* @return resulting state of the switch.
*/
bool lv_sw_toggle(lv_obj_t *sw) {
bool state = lv_sw_get_state(sw);
if(state) {
lv_sw_off(sw);
}
else {
lv_sw_on(sw);
}
return !state;
}
/**
* Turn ON the switch with an animation
* @param sw pointer to a switch object
*/
void lv_sw_on_anim(lv_obj_t * sw)
{
lv_sw_ext_t * ext = lv_obj_get_ext_attr(sw);
if(lv_sw_get_anim_time(sw) > 0)lv_sw_anim_to_value(sw, LV_SWITCH_SLIDER_ANIM_MAX);
else lv_slider_set_value(sw, LV_SWITCH_SLIDER_ANIM_MAX);
lv_slider_set_style(sw, LV_SLIDER_STYLE_KNOB, ext->style_knob_on);
}
/**
* Turn OFF the switch with an animation
* @param sw pointer to a switch object
*/
void lv_sw_off_anim(lv_obj_t * sw)
{
lv_sw_ext_t * ext = lv_obj_get_ext_attr(sw);
if(lv_sw_get_anim_time(sw) > 0) lv_sw_anim_to_value(sw, 0);
else lv_slider_set_value(sw, 0);
lv_slider_set_style(sw, LV_SLIDER_STYLE_KNOB, ext->style_knob_off);
}
/**
* Toggle the position of the switch with an animation
* @param sw pointer to a switch object
* @return resulting state of the switch.
*/
bool lv_sw_toggle_anim(lv_obj_t *sw) {
bool state = lv_sw_get_state(sw);
if(state) {
lv_sw_off_anim(sw);
}
else {
lv_sw_on_anim(sw);
}
return !state;
}
/**
* Set a style of a switch
* @param sw pointer to a switch object
* @param type which style should be set
* @param style pointer to a style
*/
void lv_sw_set_style(lv_obj_t * sw, lv_sw_style_t type, lv_style_t * style)
{
lv_sw_ext_t * ext = lv_obj_get_ext_attr(sw);
switch(type) {
case LV_SLIDER_STYLE_BG:
lv_slider_set_style(sw, LV_SLIDER_STYLE_BG, style);
break;
case LV_SLIDER_STYLE_INDIC:
lv_bar_set_style(sw, LV_SLIDER_STYLE_INDIC, style);
break;
case LV_SW_STYLE_KNOB_OFF:
ext->style_knob_off = style;
if(lv_sw_get_state(sw) == 0) lv_slider_set_style(sw, LV_SLIDER_STYLE_KNOB, style);
break;
case LV_SW_STYLE_KNOB_ON:
ext->style_knob_on = style;
if(lv_sw_get_state(sw) != 0) lv_slider_set_style(sw, LV_SLIDER_STYLE_KNOB, style);
break;
}
}
void lv_sw_set_anim_time(lv_obj_t *sw, uint16_t anim_time)
{
#if USE_LV_ANIMATION
lv_sw_ext_t * ext = lv_obj_get_ext_attr(sw);
ext->anim_time = anim_time;
#endif
}
/*=====================
* Getter functions
*====================*/
/**
* Get a style of a switch
* @param sw pointer to a switch object
* @param type which style should be get
* @return style pointer to a style
*/
lv_style_t * lv_sw_get_style(const lv_obj_t * sw, lv_sw_style_t type)
{
lv_style_t * style = NULL;
lv_sw_ext_t * ext = lv_obj_get_ext_attr(sw);
switch(type) {
case LV_SW_STYLE_BG:
style = lv_slider_get_style(sw, LV_SLIDER_STYLE_BG);
break;
case LV_SW_STYLE_INDIC:
style = lv_slider_get_style(sw, LV_SLIDER_STYLE_INDIC);
break;
case LV_SW_STYLE_KNOB_OFF:
style = ext->style_knob_off;
break;
case LV_SW_STYLE_KNOB_ON:
style = ext->style_knob_on;
break;
default:
style = NULL;
break;
}
return style;
}
uint16_t lv_sw_get_anim_time(const lv_obj_t *sw)
{
#if USE_LV_ANIMATION
lv_sw_ext_t * ext = lv_obj_get_ext_attr(sw);
return ext->anim_time;
#else
return 0;
#endif
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Signal function of the switch
* @param sw pointer to a switch object
* @param sign a signal type from lv_signal_t enum
* @param param pointer to a signal specific variable
* @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
*/
static lv_res_t lv_sw_signal(lv_obj_t * sw, lv_signal_t sign, void * param)
{
lv_sw_ext_t * ext = lv_obj_get_ext_attr(sw);
/*Save the current (old) value before slider signal modifies it*/
int16_t old_val;
if(sign == LV_SIGNAL_PRESSING) old_val = ext->slider.drag_value;
else old_val = lv_slider_get_value(sw);
/*Do not let the slider to call the callback. The Switch will do it if required*/
lv_action_t slider_action = ext->slider.action;
ext->slider.action = NULL;
lv_res_t res;
/* Include the ancient signal function */
res = ancestor_signal(sw, sign, param);
if(res != LV_RES_OK) return res;
if(sign == LV_SIGNAL_CLEANUP) {
/*Nothing to cleanup. (No dynamically allocated memory in 'ext')*/
}
else if(sign == LV_SIGNAL_PRESSED) {
/*Save the x coordinate of the pressed point to see if the switch was slid*/
lv_indev_t * indev = lv_indev_get_act();
if(indev) {
lv_point_t p;
lv_indev_get_point(indev, &p);
ext->start_x = p.x;
}
ext->slided = 0;
ext->changed = 0;
}
else if(sign == LV_SIGNAL_PRESSING) {
/*See if the switch was slid*/
lv_indev_t * indev = lv_indev_get_act();
if(indev) {
lv_point_t p = {0,0};
lv_indev_get_point(indev, &p);
if(LV_MATH_ABS(p.x - ext->start_x) > LV_INDEV_DRAG_LIMIT) ext->slided = 1;
}
/*If didn't slide then revert the min/max value. So click without slide won't move the switch as a slider*/
if(ext->slided == 0) {
if(lv_sw_get_state(sw)) ext->slider.drag_value = LV_SWITCH_SLIDER_ANIM_MAX;
else ext->slider.drag_value = 0;
}
/*If explicitly changed (by slide) don't need to be toggled on release*/
int16_t threshold = LV_SWITCH_SLIDER_ANIM_MAX / 2;
if((old_val < threshold && ext->slider.drag_value > threshold) ||
(old_val > threshold && ext->slider.drag_value < threshold))
{
ext->changed = 1;
}
}
else if(sign == LV_SIGNAL_PRESS_LOST) {
if(lv_sw_get_state(sw)) {
lv_slider_set_style(sw, LV_SLIDER_STYLE_KNOB, ext->style_knob_on);
#if USE_LV_ANIMATION
lv_sw_anim_to_value(sw, LV_SWITCH_SLIDER_ANIM_MAX);
#endif
}
else {
lv_slider_set_style(sw, LV_SLIDER_STYLE_KNOB, ext->style_knob_off);
#if USE_LV_ANIMATION
lv_sw_anim_to_value(sw, 0);
#endif
}
}
else if(sign == LV_SIGNAL_RELEASED) {
/*If not dragged then toggle the switch*/
if(ext->changed == 0) {
if(lv_sw_get_state(sw)) lv_sw_off_anim(sw);
else lv_sw_on_anim(sw);
if(slider_action != NULL) res = slider_action(sw);
}
/*If the switch was dragged then calculate the new state based on the current position*/
else {
int16_t v = lv_slider_get_value(sw);
if(v > LV_SWITCH_SLIDER_ANIM_MAX / 2) lv_sw_on_anim(sw);
else lv_sw_off_anim(sw);
if(slider_action != NULL) res = slider_action(sw);
}
} else if(sign == LV_SIGNAL_CONTROLL) {
char c = *((char *)param);
if(c == LV_GROUP_KEY_ENTER) {
if(old_val) lv_sw_off_anim(sw);
else lv_sw_on_anim(sw);
if(slider_action) res = slider_action(sw);
} else if(c == LV_GROUP_KEY_UP || c == LV_GROUP_KEY_RIGHT) {
lv_sw_on_anim(sw);
if(slider_action) res = slider_action(sw);
} else if(c == LV_GROUP_KEY_DOWN || c == LV_GROUP_KEY_LEFT) {
lv_sw_off_anim(sw);
if(slider_action) res = slider_action(sw);
}
} else if(sign == LV_SIGNAL_GET_EDITABLE) {
bool * editable = (bool *)param;
*editable = false; /*The ancestor slider is editable the switch is not*/
} else if(sign == LV_SIGNAL_GET_TYPE) {
lv_obj_type_t * buf = param;
uint8_t i;
for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/
if(buf->type[i] == NULL) break;
}
buf->type[i] = "lv_sw";
}
/*Restore the callback*/
if(res == LV_RES_OK) ext->slider.action = slider_action;
return res;
}
static void lv_sw_anim_to_value(lv_obj_t * sw, int16_t value)
{
#if USE_LV_ANIMATION
lv_anim_t a;
lv_sw_ext_t * ext = lv_obj_get_ext_attr(sw);
a.var = sw;
a.start = ext->slider.bar.cur_value;
a.end = value;
a.fp = (lv_anim_fp_t)lv_slider_set_value;
a.path = lv_anim_path_linear;
a.end_cb = NULL;
a.act_time = 0;
a.time = lv_sw_get_anim_time(sw);
a.playback = 0;
a.playback_pause = 0;
a.repeat = 0;
a.repeat_pause = 0;
lv_anim_create(&a);
#endif
}
#endif

View File

@@ -0,0 +1,194 @@
/**
* @file lv_sw.h
*
*/
#ifndef LV_SW_H
#define LV_SW_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#ifdef LV_CONF_INCLUDE_SIMPLE
#include "lv_conf.h"
#else
#include "../../lv_conf.h"
#endif
#if USE_LV_SW != 0
/*Testing of dependencies*/
#if USE_LV_SLIDER == 0
#error "lv_sw: lv_slider is required. Enable it in lv_conf.h (USE_LV_SLIDER 1)"
#endif
#include "../lv_core/lv_obj.h"
#include "lv_slider.h"
/*********************
* DEFINES
*********************/
#define LV_SWITCH_SLIDER_ANIM_MAX 1000
/**********************
* TYPEDEFS
**********************/
/*Data of switch*/
typedef struct
{
lv_slider_ext_t slider; /*Ext. of ancestor*/
/*New data for this type */
lv_style_t *style_knob_off; /*Style of the knob when the switch is OFF*/
lv_style_t *style_knob_on; /*Style of the knob when the switch is ON (NULL to use the same as OFF)*/
lv_coord_t start_x;
uint8_t changed :1; /*Indicates the switch state explicitly changed by drag*/
uint8_t slided :1;
#if USE_LV_ANIMATION
uint16_t anim_time; /*switch animation time */
#endif
} lv_sw_ext_t;
enum {
LV_SW_STYLE_BG,
LV_SW_STYLE_INDIC,
LV_SW_STYLE_KNOB_OFF,
LV_SW_STYLE_KNOB_ON,
};
typedef uint8_t lv_sw_style_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a switch objects
* @param par pointer to an object, it will be the parent of the new switch
* @param copy pointer to a switch object, if not NULL then the new object will be copied from it
* @return pointer to the created switch
*/
lv_obj_t * lv_sw_create(lv_obj_t * par, const lv_obj_t * copy);
/*=====================
* Setter functions
*====================*/
/**
* Turn ON the switch
* @param sw pointer to a switch object
*/
void lv_sw_on(lv_obj_t *sw);
/**
* Turn OFF the switch
* @param sw pointer to a switch object
*/
void lv_sw_off(lv_obj_t *sw);
/**
* Toggle the position of the switch
* @param sw pointer to a switch object
* @return resulting state of the switch.
*/
bool lv_sw_toggle(lv_obj_t *sw);
/**
* Turn ON the switch with an animation
* @param sw pointer to a switch object
*/
void lv_sw_on_anim(lv_obj_t * sw);
/**
* Turn OFF the switch with an animation
* @param sw pointer to a switch object
*/
void lv_sw_off_anim(lv_obj_t * sw);
/**
* Toggle the position of the switch with an animation
* @param sw pointer to a switch object
* @return resulting state of the switch.
*/
bool lv_sw_toggle_anim(lv_obj_t *sw);
/**
* Set a function which will be called when the switch is toggled by the user
* @param sw pointer to switch object
* @param action a callback function
*/
static inline void lv_sw_set_action(lv_obj_t * sw, lv_action_t action)
{
lv_slider_set_action(sw, action);
}
/**
* Set a style of a switch
* @param sw pointer to a switch object
* @param type which style should be set
* @param style pointer to a style
*/
void lv_sw_set_style(lv_obj_t *sw, lv_sw_style_t type, lv_style_t *style);
#if USE_LV_ANIMATION
/**
* Set the animation time of the switch
* @param sw pointer to a switch object
* @param anim_time animation time
* @return style pointer to a style
*/
void lv_sw_set_anim_time(lv_obj_t *sw, uint16_t anim_time);
#endif
/*=====================
* Getter functions
*====================*/
/**
* Get the state of a switch
* @param sw pointer to a switch object
* @return false: OFF; true: ON
*/
static inline bool lv_sw_get_state(const lv_obj_t *sw)
{
return lv_bar_get_value(sw) < LV_SWITCH_SLIDER_ANIM_MAX / 2 ? false : true;
}
/**
* Get the switch action function
* @param slider pointer to a switch object
* @return the callback function
*/
static inline lv_action_t lv_sw_get_action(const lv_obj_t * slider)
{
return lv_slider_get_action(slider);
}
/**
* Get a style of a switch
* @param sw pointer to a switch object
* @param type which style should be get
* @return style pointer to a style
*/
lv_style_t * lv_sw_get_style(const lv_obj_t *sw, lv_sw_style_t type);
/**
* Get the animation time of the switch
* @param sw pointer to a switch object
* @return style pointer to a style
*/
uint16_t lv_sw_get_anim_time(const lv_obj_t *sw);
/**********************
* MACROS
**********************/
#endif /*USE_LV_SW*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_SW_H*/

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,390 @@
/**
* @file lv_ta.h
*
*/
#ifndef LV_TA_H
#define LV_TA_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#ifdef LV_CONF_INCLUDE_SIMPLE
#include "lv_conf.h"
#else
#include "../../lv_conf.h"
#endif
#if USE_LV_TA != 0
/*Testing of dependencies*/
#if USE_LV_PAGE == 0
#error "lv_ta: lv_page is required. Enable it in lv_conf.h (USE_LV_PAGE 1) "
#endif
#if USE_LV_LABEL == 0
#error "lv_ta: lv_label is required. Enable it in lv_conf.h (USE_LV_LABEL 1) "
#endif
#include "../lv_core/lv_obj.h"
#include "lv_page.h"
#include "lv_label.h"
/*********************
* DEFINES
*********************/
#define LV_TA_CURSOR_LAST (0x7FFF) /*Put the cursor after the last character*/
/**********************
* TYPEDEFS
**********************/
enum {
LV_CURSOR_NONE,
LV_CURSOR_LINE,
LV_CURSOR_BLOCK,
LV_CURSOR_OUTLINE,
LV_CURSOR_UNDERLINE,
LV_CURSOR_HIDDEN = 0x08, /*Or it to any value to hide the cursor temporally*/
};
typedef uint8_t lv_cursor_type_t;
/*Data of text area*/
typedef struct
{
lv_page_ext_t page; /*Ext. of ancestor*/
/*New data for this type */
lv_obj_t * label; /*Label of the text area*/
char * pwd_tmp; /*Used to store the original text in password mode*/
const char * accapted_chars;/*Only these characters will be accepted. NULL: accept all*/
uint16_t max_length; /*The max. number of characters. 0: no limit*/
uint8_t pwd_mode :1; /*Replace characters with '*' */
uint8_t one_line :1; /*One line mode (ignore line breaks)*/
struct {
lv_style_t *style; /*Style of the cursor (NULL to use label's style)*/
lv_coord_t valid_x; /*Used when stepping up/down in text area when stepping to a shorter line. (Handled by the library)*/
uint16_t pos; /*The current cursor position (0: before 1. letter; 1: before 2. letter etc.)*/
lv_area_t area; /*Cursor area relative to the Text Area*/
uint16_t txt_byte_pos; /*Byte index of the letter after (on) the cursor*/
lv_cursor_type_t type:4; /*Shape of the cursor*/
uint8_t state :1; /*Indicates that the cursor is visible now or not (Handled by the library)*/
} cursor;
} lv_ta_ext_t;
enum {
LV_TA_STYLE_BG,
LV_TA_STYLE_SB,
LV_TA_STYLE_EDGE_FLASH,
LV_TA_STYLE_CURSOR,
};
typedef uint8_t lv_ta_style_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a text area objects
* @param par pointer to an object, it will be the parent of the new text area
* @param copy pointer to a text area object, if not NULL then the new object will be copied from it
* @return pointer to the created text area
*/
lv_obj_t * lv_ta_create(lv_obj_t * par, const lv_obj_t * copy);
/*======================
* 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 ta pointer to a text area object
* @param c a character (e.g. 'a')
*/
void lv_ta_add_char(lv_obj_t * ta, uint32_t c);
/**
* Insert a text to the current cursor position
* @param ta pointer to a text area object
* @param txt a '\0' terminated string to insert
*/
void lv_ta_add_text(lv_obj_t * ta, const char * txt);
/**
* Delete a the left character from the current cursor position
* @param ta pointer to a text area object
*/
void lv_ta_del_char(lv_obj_t * ta);
/*=====================
* Setter functions
*====================*/
/**
* Set the text of a text area
* @param ta pointer to a text area
* @param txt pointer to the text
*/
void lv_ta_set_text(lv_obj_t * ta, 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_TA_CURSOR_LAST: go after the last character
*/
void lv_ta_set_cursor_pos(lv_obj_t * ta, int16_t pos);
/**
* Set the cursor type.
* @param ta pointer to a text area object
* @param cur_type: element of 'lv_cursor_type_t'
*/
void lv_ta_set_cursor_type(lv_obj_t * ta, lv_cursor_type_t cur_type);
/**
* Enable/Disable password mode
* @param ta pointer to a text area object
* @param en true: enable, false: disable
*/
void lv_ta_set_pwd_mode(lv_obj_t * ta, bool en);
/**
* Configure the text area to one line or back to normal
* @param ta pointer to a Text area object
* @param en true: one line, false: normal
*/
void lv_ta_set_one_line(lv_obj_t * ta, bool en);
/**
* Set the alignment of the text area.
* In one line mode the text can be scrolled only with `LV_LABEL_ALIGN_LEFT`.
* This function should be called if the size of text area changes.
* @param ta pointer to a text are object
* @param align the desired alignment from `lv_label_align_t`. (LV_LABEL_ALIGN_LEFT/CENTER/RIGHT)
*/
void lv_ta_set_text_align(lv_obj_t * ta, lv_label_align_t align);
/**
* Set a list of characters. Only these characters will be accepted by the text area
* @param ta pointer to Text Area
* @param list list of characters. Only the pointer is saved. E.g. "+-.,0123456789"
*/
void lv_ta_set_accepted_chars(lv_obj_t * ta, const char * list);
/**
* Set max length of a Text Area.
* @param ta pointer to Text Area
* @param num the maximal number of characters can be added (`lv_ta_set_text` ignores it)
*/
void lv_ta_set_max_length(lv_obj_t * ta, uint16_t num);
/**
* Set an action to call when the Text area is clicked
* @param ta pointer to a Text area
* @param action a function pointer
*/
static inline void lv_ta_set_action(lv_obj_t * ta, lv_action_t action)
{
lv_page_set_rel_action(ta, action);
}
/**
* Set the scroll bar mode of a text area
* @param ta pointer to a text area object
* @param sb_mode the new mode from 'lv_page_sb_mode_t' enum
*/
static inline void lv_ta_set_sb_mode(lv_obj_t * ta, lv_sb_mode_t mode)
{
lv_page_set_sb_mode(ta, mode);
}
/**
* Enable the scroll propagation feature. If enabled then the Text area will move its parent if there is no more space to scroll.
* @param ta pointer to a Text area
* @param en true or false to enable/disable scroll propagation
*/
static inline void lv_ta_set_scroll_propagation(lv_obj_t * ta, bool en)
{
lv_page_set_scroll_propagation(ta, en);
}
/**
* Enable the edge flash effect. (Show an arc when the an edge is reached)
* @param page pointer to a Text Area
* @param en true or false to enable/disable end flash
*/
static inline void lv_ta_set_edge_flash(lv_obj_t * ta, bool en)
{
lv_page_set_edge_flash(ta, en);
}
/**
* Set a style of a text area
* @param ta pointer to a text area object
* @param type which style should be set
* @param style pointer to a style
*/
void lv_ta_set_style(lv_obj_t *ta, lv_ta_style_t type, lv_style_t *style);
/*=====================
* Getter functions
*====================*/
/**
* Get the text of a text area. In password mode it gives the real text (not '*'s).
* @param ta pointer to a text area object
* @return pointer to the text
*/
const char * lv_ta_get_text(const lv_obj_t * ta);
/**
* Get the label of a text area
* @param ta pointer to a text area object
* @return pointer to the label object
*/
lv_obj_t * lv_ta_get_label(const lv_obj_t * ta);
/**
* Get the current cursor position in character index
* @param ta pointer to a text area object
* @return the cursor position
*/
uint16_t lv_ta_get_cursor_pos(const lv_obj_t * ta);
/**
* Get the current cursor visibility.
* @param ta pointer to a text area object
* @return true: the cursor is drawn, false: the cursor is hidden
*/
//bool lv_ta_get_cursor_show(const lv_obj_t * ta);
/**
* Get the current cursor type.
* @param ta pointer to a text area object
* @return element of 'lv_cursor_type_t'
*/
lv_cursor_type_t lv_ta_get_cursor_type(const lv_obj_t * ta);
/**
* Get the password mode attribute
* @param ta pointer to a text area object
* @return true: password mode is enabled, false: disabled
*/
bool lv_ta_get_pwd_mode(const lv_obj_t * ta);
/**
* Get the one line configuration attribute
* @param ta pointer to a text area object
* @return true: one line configuration is enabled, false: disabled
*/
bool lv_ta_get_one_line(const lv_obj_t * ta);
/**
* Get a list of accepted characters.
* @param ta pointer to Text Area
* @return list of accented characters.
*/
const char * lv_ta_get_accepted_chars(lv_obj_t * ta);
/**
* Set max length of a Text Area.
* @param ta pointer to Text Area
* @return the maximal number of characters to be add
*/
uint16_t lv_ta_get_max_length(lv_obj_t * ta);
/**
* Set an action to call when the Text area is clicked
* @param ta pointer to a Text area
* @param action a function pointer
*/
static inline lv_action_t lv_ta_get_action(lv_obj_t * ta)
{
return lv_page_get_rel_action(ta);
}
/**
* Get the scroll bar mode of a text area
* @param ta pointer to a text area object
* @return scrollbar mode from 'lv_page_sb_mode_t' enum
*/
static inline lv_sb_mode_t lv_ta_get_sb_mode(const lv_obj_t * ta)
{
return lv_page_get_sb_mode(ta);
}
/**
* Get the scroll propagation property
* @param ta pointer to a Text area
* @return true or false
*/
static inline bool lv_ta_get_scroll_propagation(lv_obj_t * ta)
{
return lv_page_get_scroll_propagation(ta);
}
/**
* Get the scroll propagation property
* @param ta pointer to a Text area
* @return true or false
*/
static inline bool lv_ta_get_edge_flash(lv_obj_t * ta)
{
return lv_page_get_edge_flash(ta);
}
/**
* Get a style of a text area
* @param ta pointer to a text area object
* @param type which style should be get
* @return style pointer to a style
*/
lv_style_t * lv_ta_get_style(const lv_obj_t *ta, lv_ta_style_t type);
/*=====================
* Other functions
*====================*/
/**
* Move the cursor one character right
* @param ta pointer to a text area object
*/
void lv_ta_cursor_right(lv_obj_t * ta);
/**
* Move the cursor one character left
* @param ta pointer to a text area object
*/
void lv_ta_cursor_left(lv_obj_t * ta);
/**
* Move the cursor one line down
* @param ta pointer to a text area object
*/
void lv_ta_cursor_down(lv_obj_t * ta);
/**
* Move the cursor one line up
* @param ta pointer to a text area object
*/
void lv_ta_cursor_up(lv_obj_t * ta);
/**********************
* MACROS
**********************/
#endif /*USE_LV_TA_H*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_TA_H*/

View File

@@ -0,0 +1,855 @@
/**
* @file lv_table.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_table.h"
#if USE_LV_TABLE != 0
#include "../lv_misc/lv_txt.h"
#include "../lv_misc/lv_math.h"
#include "../lv_draw/lv_draw_label.h"
#include "../lv_themes/lv_theme.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static bool lv_table_design(lv_obj_t * table, const lv_area_t * mask, lv_design_mode_t mode);
static lv_res_t lv_table_signal(lv_obj_t * table, lv_signal_t sign, void * param);
static lv_coord_t get_row_height(lv_obj_t * table, uint16_t row_id);
static void refr_size(lv_obj_t * table);
/**********************
* STATIC VARIABLES
**********************/
static lv_signal_func_t ancestor_signal;
static lv_design_func_t ancestor_scrl_design;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Create a table object
* @param par pointer to an object, it will be the parent of the new table
* @param copy pointer to a table object, if not NULL then the new object will be copied from it
* @return pointer to the created table
*/
lv_obj_t * lv_table_create(lv_obj_t * par, const lv_obj_t * copy)
{
LV_LOG_TRACE("table create started");
/*Create the ancestor of table*/
lv_obj_t * new_table = lv_obj_create(par, copy);
lv_mem_assert(new_table);
if(new_table == NULL) return NULL;
/*Allocate the table type specific extended data*/
lv_table_ext_t * ext = lv_obj_allocate_ext_attr(new_table, sizeof(lv_table_ext_t));
lv_mem_assert(ext);
if(ext == NULL) return NULL;
if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_table);
if(ancestor_scrl_design == NULL) ancestor_scrl_design = lv_obj_get_design_func(new_table);
/*Initialize the allocated 'ext' */
ext->cell_data = NULL;
ext->cell_style[0] = &lv_style_plain;
ext->cell_style[1] = &lv_style_plain;
ext->cell_style[2] = &lv_style_plain;
ext->cell_style[3] = &lv_style_plain;
ext->col_cnt = 0;
ext->row_cnt = 0;
uint16_t i;
for(i = 0; i < LV_TABLE_COL_MAX; i++) {
ext->col_w[i] = LV_DPI;
}
/*The signal and design functions are not copied so set them here*/
lv_obj_set_signal_func(new_table, lv_table_signal);
lv_obj_set_design_func(new_table, lv_table_design);
/*Init the new table table*/
if(copy == NULL) {
/*Set the default styles*/
lv_theme_t * th = lv_theme_get_current();
if(th) {
lv_table_set_style(new_table, LV_TABLE_STYLE_BG, th->table.bg);
lv_table_set_style(new_table, LV_TABLE_STYLE_CELL1, th->table.cell);
lv_table_set_style(new_table, LV_TABLE_STYLE_CELL2, th->table.cell);
lv_table_set_style(new_table, LV_TABLE_STYLE_CELL3, th->table.cell);
lv_table_set_style(new_table, LV_TABLE_STYLE_CELL4, th->table.cell);
} else {
lv_table_set_style(new_table, LV_TABLE_STYLE_BG, &lv_style_plain_color);
}
}
/*Copy an existing table*/
else {
lv_table_ext_t * copy_ext = lv_obj_get_ext_attr(copy);
ext->cell_style[0] = copy_ext->cell_style[0];
ext->cell_style[1] = copy_ext->cell_style[1];
ext->cell_style[2] = copy_ext->cell_style[2];
ext->cell_style[3] = copy_ext->cell_style[3];
ext->col_cnt = copy_ext->col_cnt;
ext->row_cnt = copy_ext->row_cnt;
/*Refresh the style with new signal function*/
lv_obj_refresh_style(new_table);
}
LV_LOG_INFO("table created");
return new_table;
}
/*=====================
* Setter functions
*====================*/
/**
* Set the value of a cell.
* @param table 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.
*/
void lv_table_set_cell_value(lv_obj_t * table, uint16_t row, uint16_t col, const char * txt)
{
lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
if(row >= ext->row_cnt || col >= ext->col_cnt) {
LV_LOG_WARN("lv_table_set_cell_value: invalid row or column");
return;
}
uint32_t cell = row * ext->col_cnt + col;
lv_table_cell_format_t format;
/*Save the format byte*/
if(ext->cell_data[cell]) {
format.format_byte = ext->cell_data[cell][0];
}
/*Initialize the format byte*/
else {
format.align = LV_LABEL_ALIGN_LEFT;
format.right_merge = 0;
format.type = 0;
format.crop = 0;
}
ext->cell_data[cell] = lv_mem_realloc(ext->cell_data[cell], strlen(txt) + 2); /*+1: trailing '\0; +1: format byte*/
strcpy(ext->cell_data[cell] + 1, txt); /*Leave the format byte*/
ext->cell_data[cell][0] = format.format_byte;
refr_size(table);
}
/**
* Set the number of rows
* @param table table pointer to a Table object
* @param row_cnt number of rows
*/
void lv_table_set_row_cnt(lv_obj_t * table, uint16_t row_cnt)
{
lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
uint16_t old_row_cnt = ext->row_cnt;
ext->row_cnt = row_cnt;
if(ext->row_cnt > 0 && ext->col_cnt > 0) {
ext->cell_data = lv_mem_realloc(ext->cell_data, ext->row_cnt * ext->col_cnt * sizeof(char*));
/*Initilize the new fields*/
if(old_row_cnt < row_cnt) {
uint16_t old_cell_cnt = old_row_cnt * ext->col_cnt;
uint32_t new_cell_cnt = ext->col_cnt * ext->row_cnt;
memset(&ext->cell_data[old_cell_cnt], 0, (new_cell_cnt - old_cell_cnt) * sizeof(ext->cell_data[0]));
}
}
else {
lv_mem_free(ext->cell_data);
ext->cell_data = NULL;
}
refr_size(table);
}
/**
* Set the number of columns
* @param table table pointer to a Table object
* @param col_cnt number of columns. Must be < LV_TABLE_COL_MAX
*/
void lv_table_set_col_cnt(lv_obj_t * table, uint16_t col_cnt)
{
if(col_cnt >= LV_TABLE_COL_MAX) {
LV_LOG_WARN("lv_table_set_col_cnt: too many columns. Must be < LV_TABLE_COL_MAX.");
return;
}
lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
uint16_t old_col_cnt = ext->col_cnt;
ext->col_cnt = col_cnt;
if(ext->row_cnt > 0 && ext->col_cnt > 0) {
ext->cell_data = lv_mem_realloc(ext->cell_data, ext->row_cnt * ext->col_cnt * sizeof(char*));
/*Initilize the new fields*/
if(old_col_cnt < col_cnt) {
uint16_t old_cell_cnt = old_col_cnt * ext->row_cnt;
uint32_t new_cell_cnt = ext->col_cnt * ext->row_cnt;
memset(&ext->cell_data[old_cell_cnt], 0, (new_cell_cnt - old_cell_cnt) * sizeof(ext->cell_data[0]));
}
}
else {
lv_mem_free(ext->cell_data);
ext->cell_data = NULL;
}
refr_size(table);
}
/**
* Set the width of a column
* @param table 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 * table, uint16_t col_id, lv_coord_t w)
{
if(col_id >= LV_TABLE_COL_MAX) {
LV_LOG_WARN("lv_table_set_col_width: too big 'col_id'. Must be < LV_TABLE_COL_MAX.");
return;
}
lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
ext->col_w[col_id] = w;
refr_size(table);
}
/**
* Set the text align in a cell
* @param table 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 align LV_LABEL_ALIGN_LEFT or LV_LABEL_ALIGN_CENTER or LV_LABEL_ALIGN_RIGHT
*/
void lv_table_set_cell_align(lv_obj_t * table, uint16_t row, uint16_t col, lv_label_align_t align)
{
lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
if(row >= ext->row_cnt || col >= ext->col_cnt) {
LV_LOG_WARN("lv_table_set_cell_align: invalid row or column");
return;
}
uint32_t cell = row * ext->col_cnt + col;
if(ext->cell_data[cell] == NULL) {
ext->cell_data[cell] = lv_mem_alloc(2); /*+1: trailing '\0; +1: format byte*/
ext->cell_data[cell][0] = 0;
ext->cell_data[cell][1] = '\0';
}
lv_table_cell_format_t format;
format.format_byte = ext->cell_data[cell][0];
format.align = align;
ext->cell_data[cell][0] = format.format_byte;
}
/**
* Set the type of a cell.
* @param table 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 type 1,2,3 or 4. The cell style will be chosen accordingly.
*/
void lv_table_set_cell_type(lv_obj_t * table, uint16_t row, uint16_t col, uint8_t type)
{
lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
if(row >= ext->row_cnt || col >= ext->col_cnt) {
LV_LOG_WARN("lv_table_set_cell_type: invalid row or column");
return;
}
uint32_t cell = row * ext->col_cnt + col;
if(ext->cell_data[cell] == NULL) {
ext->cell_data[cell] = lv_mem_alloc(2); /*+1: trailing '\0; +1: format byte*/
ext->cell_data[cell][0] = 0;
ext->cell_data[cell][1] = '\0';
}
if(type > 0) type--; /*User gives 1,2,3,4 but easier to handle 0, 1, 2, 3*/
if(type >= LV_TABLE_CELL_STYLE_CNT) type = LV_TABLE_CELL_STYLE_CNT - 1;
lv_table_cell_format_t format;
format.format_byte = ext->cell_data[cell][0];
format.type = type;
ext->cell_data[cell][0] = format.format_byte;
}
/**
* Set the cell crop. (Don't adjust the height of the cell according to its content)
* @param table 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 crop true: crop the cell content; false: set the cell height to the content.
*/
void lv_table_set_cell_crop(lv_obj_t * table, uint16_t row, uint16_t col, bool crop)
{
lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
if(row >= ext->row_cnt || col >= ext->col_cnt) {
LV_LOG_WARN("lv_table_set_cell_crop: invalid row or column");
return;
}
uint32_t cell = row * ext->col_cnt + col;
if(ext->cell_data[cell] == NULL) {
ext->cell_data[cell] = lv_mem_alloc(2); /*+1: trailing '\0; +1: format byte*/
ext->cell_data[cell][0] = 0;
ext->cell_data[cell][1] = '\0';
}
lv_table_cell_format_t format;
format.format_byte = ext->cell_data[cell][0];
format.crop = crop;
ext->cell_data[cell][0] = format.format_byte;
}
/**
* Merge a cell with the right neighbor. The value of the cell to the right won't be displayed.
* @param table table 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 en true: merge right; false: don't merge right
*/
void lv_table_set_cell_merge_right(lv_obj_t * table, uint16_t row, uint16_t col, bool en)
{
lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
if(row >= ext->row_cnt || col >= ext->col_cnt) {
LV_LOG_WARN("lv_table_set_cell_merge_right: invalid row or column");
return;
}
uint32_t cell = row * ext->col_cnt + col;
if(ext->cell_data[cell] == NULL) {
ext->cell_data[cell] = lv_mem_alloc(2); /*+1: trailing '\0; +1: format byte*/
ext->cell_data[cell][0] = 0;
ext->cell_data[cell][1] = '\0';
}
lv_table_cell_format_t format;
format.format_byte = ext->cell_data[cell][0];
format.right_merge = en ? 1 : 0;
ext->cell_data[cell][0] = format.format_byte;
refr_size(table);
}
/**
* Set a style of a table.
* @param table pointer to table object
* @param type which style should be set
* @param style pointer to a style
*/
void lv_table_set_style(lv_obj_t * table, lv_table_style_t type, lv_style_t * style)
{
lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
switch(type) {
case LV_TABLE_STYLE_BG:
lv_obj_set_style(table, style);
refr_size(table);
break;
case LV_TABLE_STYLE_CELL1:
ext->cell_style[0] = style;
refr_size(table);
break;
case LV_TABLE_STYLE_CELL2:
ext->cell_style[1] = style;
refr_size(table);
break;
case LV_TABLE_STYLE_CELL3:
ext->cell_style[2] = style;
refr_size(table);
break;
case LV_TABLE_STYLE_CELL4:
ext->cell_style[3] = style;
refr_size(table);
break;
}
}
/*=====================
* Getter functions
*====================*/
/**
* Get the value of a cell.
* @param table 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 * table, uint16_t row, uint16_t col)
{
lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
if(row >= ext->row_cnt || col >= ext->col_cnt) {
LV_LOG_WARN("lv_table_set_cell_value: invalid row or column");
return "";
}
uint32_t cell = row * ext->col_cnt + col;
if(ext->cell_data[cell] == NULL) return "";
return &ext->cell_data[cell][1]; /*Skip the format byte*/
}
/**
* Get the number of rows.
* @param table table pointer to a Table object
* @return number of rows.
*/
uint16_t lv_table_get_row_cnt(lv_obj_t * table)
{
lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
return ext->row_cnt;
}
/**
* Get the number of columns.
* @param table table pointer to a Table object
* @return number of columns.
*/
uint16_t lv_table_get_col_cnt(lv_obj_t * table)
{
lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
return ext->col_cnt;
}
/**
* Get the width of a column
* @param table table pointer to a Table object
* @param col_id 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 * table, uint16_t col_id)
{
if(col_id >= LV_TABLE_COL_MAX) {
LV_LOG_WARN("lv_table_set_col_width: too big 'col_id'. Must be < LV_TABLE_COL_MAX.");
return 0;
}
lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
return ext->col_w[col_id];
}
/**
* Get the text align of a cell
* @param table 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 LV_LABEL_ALIGN_LEFT (default in case of error) or LV_LABEL_ALIGN_CENTER or LV_LABEL_ALIGN_RIGHT
*/
lv_label_align_t lv_table_get_cell_align(lv_obj_t * table, uint16_t row, uint16_t col)
{
lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
if(row >= ext->row_cnt || col >= ext->col_cnt) {
LV_LOG_WARN("lv_table_set_cell_align: invalid row or column");
return LV_LABEL_ALIGN_LEFT; /*Just return with something*/
}
uint32_t cell = row * ext->col_cnt + col;
if(ext->cell_data[cell] == NULL) return LV_LABEL_ALIGN_LEFT; /*Just return with something*/
else {
lv_table_cell_format_t format;
format.format_byte = ext->cell_data[cell][0];
return format.align;
}
}
/**
* Get the type of a cell
* @param table 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 1,2,3 or 4
*/
lv_label_align_t lv_table_get_cell_type(lv_obj_t * table, uint16_t row, uint16_t col)
{
lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
if(row >= ext->row_cnt || col >= ext->col_cnt) {
LV_LOG_WARN("lv_table_get_cell_type: invalid row or column");
return 1; /*Just return with something*/
}
uint32_t cell = row * ext->col_cnt + col;
if(ext->cell_data[cell] == NULL) return 1; /*Just return with something*/
else {
lv_table_cell_format_t format;
format.format_byte = ext->cell_data[cell][0];
return format.type + 1; /*0,1,2,3 is stored but user sees 1,2,3,4*/
}
}
/**
* Get the crop property of a cell
* @param table 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 true: text crop enabled; false: disabled
*/
lv_label_align_t lv_table_get_cell_crop(lv_obj_t * table, uint16_t row, uint16_t col)
{
lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
if(row >= ext->row_cnt || col >= ext->col_cnt) {
LV_LOG_WARN("lv_table_get_cell_crop: invalid row or column");
return false; /*Just return with something*/
}
uint32_t cell = row * ext->col_cnt + col;
if(ext->cell_data[cell] == NULL) return false; /*Just return with something*/
else {
lv_table_cell_format_t format;
format.format_byte = ext->cell_data[cell][0];
return format.crop;
}
}
/**
* Get the cell merge attribute.
* @param table table 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 true: merge right; false: don't merge right
*/
bool lv_table_get_cell_merge_right(lv_obj_t * table, uint16_t row, uint16_t col)
{
lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
if(row >= ext->row_cnt || col >= ext->col_cnt) {
LV_LOG_WARN("lv_table_get_cell_merge_right: invalid row or column");
return false;
}
uint32_t cell = row * ext->col_cnt + col;
if(ext->cell_data[cell] == NULL) return false;
else {
lv_table_cell_format_t format;
format.format_byte = ext->cell_data[cell][0];
return format.right_merge ? true : false;
}
}
/**
* Get style of a table.
* @param table pointer to table object
* @param type which style should be get
* @return style pointer to the style
*/
lv_style_t * lv_table_get_style(const lv_obj_t * table, lv_table_style_t type)
{
lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
lv_style_t * style = NULL;
switch(type) {
case LV_TABLE_STYLE_BG:
style = lv_obj_get_style(table);
break;
case LV_TABLE_STYLE_CELL1:
style = ext->cell_style[0];
break;
case LV_TABLE_STYLE_CELL2:
style = ext->cell_style[1];
break;
case LV_TABLE_STYLE_CELL3:
style = ext->cell_style[2];
break;
case LV_TABLE_STYLE_CELL4:
style = ext->cell_style[3];
break;
default:
return NULL;
}
return style;
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Handle the drawing related tasks of the tables
* @param table pointer to an object
* @param mask the object will be drawn only in this area
* @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area
* (return 'true' if yes)
* LV_DESIGN_DRAW: draw the object (always return 'true')
* LV_DESIGN_DRAW_POST: drawing after every children are drawn
* @param return true/false, depends on 'mode'
*/
static bool lv_table_design(lv_obj_t * table, const lv_area_t * mask, lv_design_mode_t mode)
{
/*Return false if the object is not covers the mask_p area*/
if(mode == LV_DESIGN_COVER_CHK) {
return false;
}
/*Draw the object*/
else if(mode == LV_DESIGN_DRAW_MAIN) {
ancestor_scrl_design(table, mask, mode);
lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
lv_style_t * bg_style = lv_obj_get_style(table);
lv_style_t * cell_style;
lv_coord_t h_row;
lv_point_t txt_size;
lv_area_t cell_area;
lv_area_t txt_area;
lv_txt_flag_t txt_flags;
lv_opa_t opa_scale = lv_obj_get_opa_scale(table);
uint16_t col;
uint16_t row;
uint16_t cell = 0;
cell_area.y2 = table->coords.y1 + bg_style->body.padding.ver;
for(row = 0; row < ext->row_cnt; row++) {
h_row = get_row_height(table, row);
cell_area.y1 = cell_area.y2;
cell_area.y2 = cell_area.y1 + h_row;
cell_area.x2 = table->coords.x1 + bg_style->body.padding.hor;
for(col = 0; col < ext->col_cnt; col++) {
lv_table_cell_format_t format;
if(ext->cell_data[cell]) {
format.format_byte = ext->cell_data[cell][0];
} else {
format.right_merge = 0;
format.align = LV_LABEL_ALIGN_LEFT;
format.type = 0;
format.crop = 1;
}
cell_style = ext->cell_style[format.type];
cell_area.x1 = cell_area.x2;
cell_area.x2 = cell_area.x1 + ext->col_w[col];
uint16_t col_merge = 0;
for(col_merge = 0; col_merge + col < ext->col_cnt - 1; col_merge ++) {
if(ext->cell_data[cell + col_merge] != NULL) {
format.format_byte = ext->cell_data[cell + col_merge][0];
if(format.right_merge) cell_area.x2 += ext->col_w[col + col_merge + 1];
else break;
} else {
break;
}
}
lv_draw_rect(&cell_area, mask, cell_style, opa_scale);
if(ext->cell_data[cell]) {
txt_area.x1 = cell_area.x1 + cell_style->body.padding.hor;
txt_area.x2 = cell_area.x2 - cell_style->body.padding.hor;
txt_area.y1 = cell_area.y1 + cell_style->body.padding.ver;
txt_area.y2 = cell_area.y2 - cell_style->body.padding.ver;
/*Align the content to the middle if not cropped*/
if(format.crop == 0) {
txt_flags = LV_TXT_FLAG_NONE;
} else {
txt_flags = LV_TXT_FLAG_EXPAND;
}
lv_txt_get_size(&txt_size, ext->cell_data[cell] + 1, cell_style->text.font,
cell_style->text.letter_space, cell_style->text.line_space, lv_area_get_width(&txt_area), txt_flags);
/*Align the content to the middle if not cropped*/
if(format.crop == 0) {
txt_area.y1 = cell_area.y1 + h_row / 2 - txt_size.y / 2;
txt_area.y2 = cell_area.y1 + h_row / 2 + txt_size.y / 2;
}
switch(format.align) {
default:
case LV_LABEL_ALIGN_LEFT:
txt_flags |= LV_TXT_FLAG_NONE;
break;
case LV_LABEL_ALIGN_RIGHT:
txt_flags |= LV_TXT_FLAG_RIGHT;
break;
case LV_LABEL_ALIGN_CENTER:
txt_flags |= LV_TXT_FLAG_CENTER;
break;
}
lv_area_t label_mask;
bool label_mask_ok;
label_mask_ok = lv_area_intersect(&label_mask, mask, &cell_area);
if(label_mask_ok) {
lv_draw_label(&txt_area, &label_mask, cell_style, opa_scale, ext->cell_data[cell] + 1, txt_flags, NULL);
}
/*Draw lines after '\n's*/
lv_point_t p1;
lv_point_t p2;
p1.x = cell_area.x1;
p2.x = cell_area.x2;
uint16_t i;
for(i = 1; ext->cell_data[cell][i] != '\0'; i++) {
if(ext->cell_data[cell][i] == '\n') {
ext->cell_data[cell][i] = '\0';
lv_txt_get_size(&txt_size, ext->cell_data[cell] + 1, cell_style->text.font,
cell_style->text.letter_space, cell_style->text.line_space, lv_area_get_width(&txt_area), txt_flags);
p1.y = txt_area.y1 + txt_size.y + cell_style->text.line_space / 2;
p2.y = txt_area.y1 + txt_size.y + cell_style->text.line_space / 2;
lv_draw_line(&p1, &p2, mask, cell_style, opa_scale);
ext->cell_data[cell][i] = '\n';
}
}
}
cell += col_merge + 1;
col += col_merge;
}
}
}
/*Post draw when the children are drawn*/
else if(mode == LV_DESIGN_DRAW_POST) {
}
return true;
}
/**
* Signal function of the table
* @param table pointer to a table object
* @param sign a signal type from lv_signal_t enum
* @param param pointer to a signal specific variable
* @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
*/
static lv_res_t lv_table_signal(lv_obj_t * table, lv_signal_t sign, void * param)
{
lv_res_t res;
/* Include the ancient signal function */
res = ancestor_signal(table, sign, param);
if(res != LV_RES_OK) return res;
if(sign == LV_SIGNAL_CLEANUP) {
/*Free the cell texts*/
lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
uint16_t cell;
for(cell = 0; cell < ext->col_cnt * ext->row_cnt; cell++) {
if(ext->cell_data[cell]) {
lv_mem_free(ext->cell_data[cell]);
ext->cell_data[cell] = NULL;
}
}
} else if(sign == LV_SIGNAL_GET_TYPE) {
lv_obj_type_t * buf = param;
uint8_t i;
for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/
if(buf->type[i] == NULL) break;
}
buf->type[i] = "lv_table";
}
return res;
}
static void refr_size(lv_obj_t * table)
{
lv_coord_t h = 0;
lv_coord_t w = 0;
lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
uint16_t i;
for(i= 0; i < ext->col_cnt; i++) {
w += ext->col_w[i];
}
for(i= 0; i < ext->row_cnt; i++) {
h += get_row_height(table, i);
}
lv_style_t * bg_style = lv_obj_get_style(table);
w += bg_style->body.padding.hor * 2;
h += bg_style->body.padding.ver * 2;
lv_obj_set_size(table, w + 1, h + 1);
lv_obj_invalidate(table);
}
static lv_coord_t get_row_height(lv_obj_t * table, uint16_t row_id)
{
lv_table_ext_t * ext = lv_obj_get_ext_attr(table);
lv_point_t txt_size;
lv_coord_t txt_w;
lv_style_t * cell_style;
uint16_t row_start = row_id * ext->col_cnt;
uint16_t cell;
uint16_t col;
lv_coord_t h_max = lv_font_get_height(ext->cell_style[0]->text.font) + 2 * ext->cell_style[0]->body.padding.ver;
for(cell = row_start, col = 0; cell < row_start + ext->col_cnt; cell++, col ++) {
if(ext->cell_data[cell] != NULL) {
txt_w = ext->col_w[col];
uint16_t col_merge = 0;
for(col_merge = 0; col_merge + col < ext->col_cnt - 1; col_merge ++) {
if(ext->cell_data[cell + col_merge] != NULL) {
lv_table_cell_format_t format;
format.format_byte = ext->cell_data[cell + col_merge][0];
if(format.right_merge) txt_w += ext->col_w[col + col_merge + 1];
else break;
} else {
break;
}
}
lv_table_cell_format_t format;
format.format_byte = ext->cell_data[cell][0];
cell_style = ext->cell_style[format.type];
/*With text crop assume 1 line*/
if(format.crop) {
h_max = LV_MATH_MAX(lv_font_get_height(cell_style->text.font) + 2 * cell_style->body.padding.ver, h_max);
}
/*Without text crop calculate the height of the text in the cell*/
else {
txt_w -= 2 * cell_style->body.padding.hor;
lv_txt_get_size(&txt_size, ext->cell_data[cell] + 1, cell_style->text.font,
cell_style->text.letter_space, cell_style->text.line_space, txt_w, LV_TXT_FLAG_NONE);
h_max = LV_MATH_MAX(txt_size.y + 2 * cell_style->body.padding.ver, h_max);
cell += col_merge;
col += col_merge;
}
}
}
return h_max;
}
#endif

View File

@@ -0,0 +1,261 @@
/**
* @file lv_table.h
*
*/
#ifndef LV_TABLE_H
#define LV_TABLE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#ifdef LV_CONF_INCLUDE_SIMPLE
#include "lv_conf.h"
#else
#include "../../lv_conf.h"
#endif
#if USE_LV_TABLE != 0
/*Testing of dependencies*/
#if USE_LV_LABEL == 0
#error "lv_table: lv_label is required. Enable it in lv_conf.h (USE_LV_LABEL 1) "
#endif
#include "../lv_core/lv_obj.h"
#include "lv_label.h"
/*********************
* DEFINES
*********************/
#ifndef LV_TABLE_COL_MAX
#define LV_TABLE_COL_MAX 12
#endif
#define LV_TABLE_CELL_STYLE_CNT 4
/**********************
* TYPEDEFS
**********************/
typedef union {
struct {
uint8_t align:2;
uint8_t right_merge:1;
uint8_t type:2;
uint8_t crop:1;
};
uint8_t format_byte;
}lv_table_cell_format_t;
/*Data of table*/
typedef struct {
/*New data for this type */
uint16_t col_cnt;
uint16_t row_cnt;
char ** cell_data;
lv_style_t * cell_style[LV_TABLE_CELL_STYLE_CNT];
lv_coord_t col_w[LV_TABLE_COL_MAX];
} lv_table_ext_t;
/*Styles*/
enum {
LV_TABLE_STYLE_BG,
LV_TABLE_STYLE_CELL1,
LV_TABLE_STYLE_CELL2,
LV_TABLE_STYLE_CELL3,
LV_TABLE_STYLE_CELL4,
};
typedef uint8_t lv_table_style_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a table object
* @param par pointer to an object, it will be the parent of the new table
* @param copy pointer to a table object, if not NULL then the new object will be copied from it
* @return pointer to the created table
*/
lv_obj_t * lv_table_create(lv_obj_t * par, const lv_obj_t * copy);
/*=====================
* Setter functions
*====================*/
/**
* Set the value of a cell.
* @param table 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.
*/
void lv_table_set_cell_value(lv_obj_t * table, uint16_t row, uint16_t col, const char * txt);
/**
* Set the number of rows
* @param table table pointer to a Table object
* @param row_cnt number of rows
*/
void lv_table_set_row_cnt(lv_obj_t * table, uint16_t row_cnt);
/**
* Set the number of columns
* @param table table pointer to a Table object
* @param col_cnt number of columns. Must be < LV_TABLE_COL_MAX
*/
void lv_table_set_col_cnt(lv_obj_t * table, uint16_t col_cnt);
/**
* Set the width of a column
* @param table 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 * table, uint16_t col_id, lv_coord_t w);
/**
* Set the text align in a cell
* @param table 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 align LV_LABEL_ALIGN_LEFT or LV_LABEL_ALIGN_CENTER or LV_LABEL_ALIGN_RIGHT
*/
void lv_table_set_cell_align(lv_obj_t * table, uint16_t row, uint16_t col, lv_label_align_t align);
/**
* Set the type of a cell.
* @param table 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 type 1,2,3 or 4. The cell style will be chosen accordingly.
*/
void lv_table_set_cell_type(lv_obj_t * table, uint16_t row, uint16_t col, uint8_t type);
/**
* Set the cell crop. (Don't adjust the height of the cell according to its content)
* @param table 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 crop true: crop the cell content; false: set the cell height to the content.
*/
void lv_table_set_cell_crop(lv_obj_t * table, uint16_t row, uint16_t col, bool crop);
/**
* Merge a cell with the right neighbor. The value of the cell to the right won't be displayed.
* @param table table 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 en true: merge right; false: don't merge right
*/
void lv_table_set_cell_merge_right(lv_obj_t * table, uint16_t row, uint16_t col, bool en);
/**
* Set a style of a table.
* @param table pointer to table object
* @param type which style should be set
* @param style pointer to a style
*/
void lv_table_set_style(lv_obj_t * table, lv_table_style_t type, lv_style_t * style);
/*=====================
* Getter functions
*====================*/
/**
* Get the value of a cell.
* @param table 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 * table, uint16_t row, uint16_t col);
/**
* Get the number of rows.
* @param table table pointer to a Table object
* @return number of rows.
*/
uint16_t lv_table_get_row_cnt(lv_obj_t * table);
/**
* Get the number of columns.
* @param table table pointer to a Table object
* @return number of columns.
*/
uint16_t lv_table_get_col_cnt(lv_obj_t * table);
/**
* Get the width of a column
* @param table table pointer to a Table object
* @param col_id 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 * table, uint16_t col_id);
/**
* Get the text align of a cell
* @param table 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 LV_LABEL_ALIGN_LEFT (default in case of error) or LV_LABEL_ALIGN_CENTER or LV_LABEL_ALIGN_RIGHT
*/
lv_label_align_t lv_table_get_cell_align(lv_obj_t * table, uint16_t row, uint16_t col);
/**
* Get the type of a cell
* @param table 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 1,2,3 or 4
*/
lv_label_align_t lv_table_get_cell_type(lv_obj_t * table, uint16_t row, uint16_t col);
/**
* Get the crop property of a cell
* @param table 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 true: text crop enabled; false: disabled
*/
lv_label_align_t lv_table_get_cell_crop(lv_obj_t * table, uint16_t row, uint16_t col);
/**
* Get the cell merge attribute.
* @param table table 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 true: merge right; false: don't merge right
*/
bool lv_table_get_cell_merge_right(lv_obj_t * table, uint16_t row, uint16_t col);
/**
* Get style of a table.
* @param table pointer to table object
* @param type which style should be get
* @return style pointer to the style
*/
lv_style_t * lv_table_get_style(const lv_obj_t * table, lv_table_style_t type);
/*=====================
* Other functions
*====================*/
/**********************
* MACROS
**********************/
#endif /*USE_LV_TABLE*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_TABLE_H*/

View File

@@ -0,0 +1,909 @@
/*
* Copyright (c) 2019 CTCaer
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file lv_tab.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_tabview.h"
#if USE_LV_TABVIEW != 0
#include "lv_btnm.h"
#include "../lv_themes/lv_theme.h"
#include "../lv_misc/lv_anim.h"
/*********************
* DEFINES
*********************/
#if USE_LV_ANIMATION
# ifndef LV_TABVIEW_ANIM_TIME
# define LV_TABVIEW_ANIM_TIME 300 /*Animation time of focusing to the a list element [ms] (0: no animation) */
# endif
#else
# undef LV_TABVIEW_ANIM_TIME
# define LV_TABVIEW_ANIM_TIME 0 /*No animations*/
#endif
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static lv_res_t lv_tabview_signal(lv_obj_t * tabview, lv_signal_t sign, void * param);
static lv_res_t tabpage_signal(lv_obj_t * tab_page, lv_signal_t sign, void * param);
static lv_res_t tabpage_scrl_signal(lv_obj_t * tab_scrl, lv_signal_t sign, void * param);
static void tabpage_pressed_handler(lv_obj_t * tabview, lv_obj_t * tabpage);
static void tabpage_pressing_handler(lv_obj_t * tabview, lv_obj_t * tabpage);
static void tabpage_press_lost_handler(lv_obj_t * tabview, lv_obj_t * tabpage);
static lv_res_t tab_btnm_action(lv_obj_t * tab_btnm, const char * tab_name);
static void tabview_realign(lv_obj_t * tabview);
/**********************
* STATIC VARIABLES
**********************/
static lv_signal_func_t ancestor_signal;
static lv_signal_func_t page_signal;
static lv_signal_func_t page_scrl_signal;
static const char * tab_def[] = {""};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Create a Tab view object
* @param par pointer to an object, it will be the parent of the new tab
* @param copy pointer to a tab object, if not NULL then the new object will be copied from it
* @return pointer to the created tab
*/
lv_obj_t * lv_tabview_create(lv_obj_t * par, const lv_obj_t * copy)
{
LV_LOG_TRACE("tab view create started");
/*Create the ancestor of tab*/
lv_obj_t * new_tabview = lv_obj_create(par, copy);
lv_mem_assert(new_tabview);
if(new_tabview == NULL) return NULL;
if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_tabview);
/*Allocate the tab type specific extended data*/
lv_tabview_ext_t * ext = lv_obj_allocate_ext_attr(new_tabview, sizeof(lv_tabview_ext_t));
lv_mem_assert(ext);
if(ext == NULL) return NULL;
/*Initialize the allocated 'ext' */
ext->drag_hor = 0;
ext->draging = 0;
ext->slide_enable = 1;
ext->tab_cur = 0;
ext->point_last.x = 0;
ext->point_last.y = 0;
ext->content = NULL;
ext->indic = NULL;
ext->btns = NULL;
ext->tab_load_action = NULL;
ext->btns_pos = LV_TABVIEW_BTNS_POS_TOP;
ext->anim_time = LV_TABVIEW_ANIM_TIME;
ext->btns_hide = 0;
/*The signal and design functions are not copied so set them here*/
lv_obj_set_signal_func(new_tabview, lv_tabview_signal);
/*Init the new tab tab*/
if(copy == NULL) {
ext->tab_name_ptr = lv_mem_alloc(sizeof(char *));
lv_mem_assert(ext->tab_name_ptr);
if(ext->tab_name_ptr == NULL) return NULL;
ext->tab_name_ptr[0] = "";
ext->tab_cnt = 0;
lv_obj_set_size(new_tabview, LV_HOR_RES, LV_VER_RES);
ext->btns = lv_btnm_create(new_tabview, NULL);
lv_obj_set_height(ext->btns, 3 * LV_DPI / 4);
lv_btnm_set_map(ext->btns, tab_def);
lv_btnm_set_action(ext->btns, tab_btnm_action);
lv_btnm_set_toggle(ext->btns, true, 0);
ext->indic = lv_obj_create(ext->btns, NULL);
lv_obj_set_width(ext->indic, LV_DPI);
lv_obj_align(ext->indic, ext->btns, LV_ALIGN_IN_BOTTOM_LEFT, 0, 0);
lv_obj_set_click(ext->indic, false);
ext->content = lv_cont_create(new_tabview, NULL);
lv_cont_set_fit(ext->content, true, false);
lv_cont_set_layout(ext->content, LV_LAYOUT_ROW_T);
lv_cont_set_style(ext->content, &lv_style_transp_tight);
lv_obj_set_height(ext->content, LV_VER_RES - lv_obj_get_height(ext->btns));
lv_obj_align(ext->content, ext->btns, LV_ALIGN_OUT_BOTTOM_LEFT, 0, 0);
/*Set the default styles*/
lv_theme_t * th = lv_theme_get_current();
if(th) {
lv_tabview_set_style(new_tabview, LV_TABVIEW_STYLE_BG, th->tabview.bg);
lv_tabview_set_style(new_tabview, LV_TABVIEW_STYLE_INDIC, th->tabview.indic);
lv_tabview_set_style(new_tabview, LV_TABVIEW_STYLE_BTN_BG, th->tabview.btn.bg);
lv_tabview_set_style(new_tabview, LV_TABVIEW_STYLE_BTN_REL, th->tabview.btn.rel);
lv_tabview_set_style(new_tabview, LV_TABVIEW_STYLE_BTN_PR, th->tabview.btn.pr);
lv_tabview_set_style(new_tabview, LV_TABVIEW_STYLE_BTN_TGL_REL, th->tabview.btn.tgl_rel);
lv_tabview_set_style(new_tabview, LV_TABVIEW_STYLE_BTN_TGL_PR, th->tabview.btn.tgl_pr);
} else {
lv_tabview_set_style(new_tabview, LV_TABVIEW_STYLE_BG, &lv_style_plain);
lv_tabview_set_style(new_tabview, LV_TABVIEW_STYLE_BTN_BG, &lv_style_transp);
lv_tabview_set_style(new_tabview, LV_TABVIEW_STYLE_INDIC, &lv_style_plain_color);
}
}
/*Copy an existing tab view*/
else {
lv_tabview_ext_t * copy_ext = lv_obj_get_ext_attr(copy);
ext->point_last.x = 0;
ext->point_last.y = 0;
ext->btns = lv_btnm_create(new_tabview, copy_ext->btns);
ext->indic = lv_obj_create(ext->btns, copy_ext->indic);
ext->content = lv_cont_create(new_tabview, copy_ext->content);
ext->anim_time = copy_ext->anim_time;
ext->tab_load_action = copy_ext->tab_load_action;
ext->tab_name_ptr = lv_mem_alloc(sizeof(char *));
lv_mem_assert(ext->tab_name_ptr);
if(ext->tab_name_ptr == NULL) return NULL;
ext->tab_name_ptr[0] = "";
lv_btnm_set_map(ext->btns, ext->tab_name_ptr);
uint16_t i;
lv_obj_t * new_tab;
lv_obj_t * copy_tab;
for(i = 0; i < copy_ext->tab_cnt; i++) {
new_tab = lv_tabview_add_tab(new_tabview, copy_ext->tab_name_ptr[i]);
copy_tab = lv_tabview_get_tab(copy, i);
lv_page_set_style(new_tab, LV_PAGE_STYLE_BG, lv_page_get_style(copy_tab, LV_PAGE_STYLE_BG));
lv_page_set_style(new_tab, LV_PAGE_STYLE_SCRL, lv_page_get_style(copy_tab, LV_PAGE_STYLE_SCRL));
lv_page_set_style(new_tab, LV_PAGE_STYLE_SB, lv_page_get_style(copy_tab, LV_PAGE_STYLE_SB));
}
/*Refresh the style with new signal function*/
lv_obj_refresh_style(new_tabview);
}
LV_LOG_INFO("tab view created");
return new_tabview;
}
/**
* Delete all children of the scrl object, without deleting scrl child.
* @param obj pointer to an object
*/
void lv_tabview_clean(lv_obj_t * obj)
{
lv_obj_t * scrl = lv_page_get_scrl(obj);
lv_obj_clean(scrl);
}
/*======================
* Add/remove functions
*=====================*/
/**
* Add a new tab with the given name
* @param tabview pointer to Tab view object where to ass the new tab
* @param name the text on the tab button
* @return pointer to the created page object (lv_page). You can create your content here
*/
lv_obj_t * lv_tabview_add_tab(lv_obj_t * tabview, const char * name)
{
lv_tabview_ext_t * ext = lv_obj_get_ext_attr(tabview);
/*Create the container page*/
lv_obj_t * h = lv_page_create(ext->content, NULL);
lv_obj_set_size(h, lv_obj_get_width(tabview), lv_obj_get_height(ext->content));
lv_page_set_sb_mode(h, LV_SB_MODE_OFF); // Important!
lv_page_set_style(h, LV_PAGE_STYLE_BG, &lv_style_transp);
lv_page_set_style(h, LV_PAGE_STYLE_SCRL, &lv_style_transp);
if(page_signal == NULL) page_signal = lv_obj_get_signal_func(h);
if(page_scrl_signal == NULL) page_scrl_signal = lv_obj_get_signal_func(lv_page_get_scrl(h));
lv_obj_set_signal_func(h, tabpage_signal);
lv_obj_set_signal_func(lv_page_get_scrl(h), tabpage_scrl_signal);
/*Extend the button matrix map with the new name*/
char * name_dm;
if((name[0] & LV_BTNM_CTRL_MASK) == LV_BTNM_CTRL_CODE) { /*If control byte presented let is*/
name_dm = lv_mem_alloc(strlen(name) + 1); /*+1 for the the closing '\0' */
lv_mem_assert(name_dm);
if(name_dm == NULL) return NULL;
strcpy(name_dm, name);
} else { /*Set a no long press control byte is not presented*/
name_dm = lv_mem_alloc(strlen(name) + 2); /*+1 for the the closing '\0' and +1 for the control byte */
lv_mem_assert(name_dm);
if(name_dm == NULL) return NULL;
name_dm[0] = '\221';
strcpy(&name_dm[1], name);
}
ext->tab_cnt++;
ext->tab_name_ptr = lv_mem_realloc(ext->tab_name_ptr, sizeof(char *) * (ext->tab_cnt + 1));
lv_mem_assert(ext->tab_name_ptr);
if(ext->tab_name_ptr == NULL) return NULL;
ext->tab_name_ptr[ext->tab_cnt - 1] = name_dm;
ext->tab_name_ptr[ext->tab_cnt] = "";
lv_btnm_set_map(ext->btns, ext->tab_name_ptr);
/*Modify the indicator size*/
lv_style_t * style_tabs = lv_obj_get_style(ext->btns);
lv_coord_t indic_width = (lv_obj_get_width(tabview) - style_tabs->body.padding.inner * (ext->tab_cnt - 1) - 2 * style_tabs->body.padding.hor) / ext->tab_cnt;
lv_obj_set_width(ext->indic, indic_width);
lv_obj_set_x(ext->indic, indic_width * ext->tab_cur + style_tabs->body.padding.inner * ext->tab_cur + style_tabs->body.padding.hor);
/*Set the first btn as active*/
if(ext->tab_cnt == 1) {
ext->tab_cur = 0;
lv_tabview_set_tab_act(tabview, 0, false);
tabview_realign(tabview); /*To set the proper btns height*/
}
return h;
}
/*=====================
* Setter functions
*====================*/
/**
* Set a new tab
* @param tabview pointer to Tab view object
* @param id index of a tab to load
* @param anim_en true: set with sliding animation; false: set immediately
*/
void lv_tabview_set_tab_act(lv_obj_t * tabview, uint16_t id, bool anim_en)
{
#if USE_LV_ANIMATION == 0
anim_en = false;
#endif
lv_tabview_ext_t * ext = lv_obj_get_ext_attr(tabview);
lv_style_t * style = lv_obj_get_style(ext->content);
lv_res_t res = LV_RES_OK;
if(id >= ext->tab_cnt) id = ext->tab_cnt - 1;
if(ext->tab_load_action && id != ext->tab_cur) res = ext->tab_load_action(tabview, id);
if(res != LV_RES_OK) return; /*Prevent the tab loading*/
ext->tab_cur = id;
lv_coord_t cont_x = -(lv_obj_get_width(tabview) * id + style->body.padding.inner * id + style->body.padding.hor);
if(ext->anim_time == 0 || anim_en == false) {
lv_obj_set_x(ext->content, cont_x);
} else {
#if USE_LV_ANIMATION
lv_anim_t a;
a.var = ext->content;
a.start = lv_obj_get_x(ext->content);
a.end = cont_x;
a.fp = (lv_anim_fp_t)lv_obj_set_x;
a.path = lv_anim_path_linear;
a.end_cb = NULL;
a.act_time = 0;
a.time = ext->anim_time;
a.playback = 0;
a.playback_pause = 0;
a.repeat = 0;
a.repeat_pause = 0;
lv_anim_create(&a);
#endif
}
/*Move the indicator*/
lv_coord_t indic_width = lv_obj_get_width(ext->indic);
lv_style_t * tabs_style = lv_obj_get_style(ext->btns);
lv_coord_t indic_x = indic_width * id + tabs_style->body.padding.inner * id + tabs_style->body.padding.hor;
if(ext->anim_time == 0 || anim_en == false) {
lv_obj_set_x(ext->indic, indic_x);
} else {
#if USE_LV_ANIMATION
lv_anim_t a;
a.var = ext->indic;
a.start = lv_obj_get_x(ext->indic);
a.end = indic_x;
a.fp = (lv_anim_fp_t)lv_obj_set_x;
a.path = lv_anim_path_linear;
a.end_cb = NULL;
a.act_time = 0;
a.time = ext->anim_time;
a.playback = 0;
a.playback_pause = 0;
a.repeat = 0;
a.repeat_pause = 0;
lv_anim_create(&a);
#endif
}
lv_btnm_set_toggle(ext->btns, true, ext->tab_cur);
}
/**
* Set an action to call when a tab is loaded (Good to create content only if required)
* lv_tabview_get_act() still gives the current (old) tab (to remove content from here)
* @param tabview pointer to a tabview object
* @param action pointer to a function to call when a btn is loaded
*/
void lv_tabview_set_tab_load_action(lv_obj_t * tabview, lv_tabview_action_t action)
{
lv_tabview_ext_t * ext = lv_obj_get_ext_attr(tabview);
ext->tab_load_action = action;
}
/**
* Enable horizontal sliding with touch pad
* @param tabview pointer to Tab view object
* @param en true: enable sliding; false: disable sliding
*/
void lv_tabview_set_sliding(lv_obj_t * tabview, bool en)
{
lv_tabview_ext_t * ext = lv_obj_get_ext_attr(tabview);
ext->slide_enable = en == false ? 0 : 1;
}
/**
* Set the animation time of tab view when a new tab is loaded
* @param tabview pointer to Tab view object
* @param anim_time_ms time of animation in milliseconds
*/
void lv_tabview_set_anim_time(lv_obj_t * tabview, uint16_t anim_time)
{
lv_tabview_ext_t * ext = lv_obj_get_ext_attr(tabview);
#if USE_LV_ANIMATION == 0
anim_time = 0;
#endif
ext->anim_time = anim_time;
}
/**
* Set the style of a tab view
* @param tabview pointer to a tan view object
* @param type which style should be set
* @param style pointer to the new style
*/
void lv_tabview_set_style(lv_obj_t * tabview, lv_tabview_style_t type, lv_style_t * style)
{
lv_tabview_ext_t * ext = lv_obj_get_ext_attr(tabview);
switch(type) {
case LV_TABVIEW_STYLE_BG:
lv_obj_set_style(tabview, style);
break;
case LV_TABVIEW_STYLE_BTN_BG:
lv_btnm_set_style(ext->btns, LV_BTNM_STYLE_BG, style);
tabview_realign(tabview);
break;
case LV_TABVIEW_STYLE_BTN_REL:
lv_btnm_set_style(ext->btns, LV_BTNM_STYLE_BTN_REL, style);
tabview_realign(tabview);
break;
case LV_TABVIEW_STYLE_BTN_PR:
lv_btnm_set_style(ext->btns, LV_BTNM_STYLE_BTN_PR, style);
break;
case LV_TABVIEW_STYLE_BTN_TGL_REL:
lv_btnm_set_style(ext->btns, LV_BTNM_STYLE_BTN_TGL_REL, style);
break;
case LV_TABVIEW_STYLE_BTN_TGL_PR:
lv_btnm_set_style(ext->btns, LV_BTNM_STYLE_BTN_TGL_PR, style);
break;
case LV_TABVIEW_STYLE_INDIC:
lv_obj_set_style(ext->indic, style);
lv_obj_set_height(ext->indic, style->body.padding.inner);
tabview_realign(tabview);
break;
}
}
/**
* Set the position of tab select buttons
* @param tabview pointer to a tan view object
* @param btns_pos which button position
*/
void lv_tabview_set_btns_pos(lv_obj_t * tabview, lv_tabview_btns_pos_t btns_pos)
{
lv_tabview_ext_t * ext = lv_obj_get_ext_attr(tabview);
ext->btns_pos = btns_pos;
tabview_realign(tabview);
}
/**
* Set whether tab buttons are hidden
* @param tabview pointer to a tab view object
* @param en whether tab buttons are hidden
*/
void lv_tabview_set_btns_hidden(lv_obj_t *tabview, bool en)
{
lv_tabview_ext_t * ext = lv_obj_get_ext_attr(tabview);
ext->btns_hide = en;
tabview_realign(tabview);
}
/*=====================
* Getter functions
*====================*/
/**
* Get the index of the currently active tab
* @param tabview pointer to Tab view object
* @return the active btn index
*/
uint16_t lv_tabview_get_tab_act(const lv_obj_t * tabview)
{
lv_tabview_ext_t * ext = lv_obj_get_ext_attr(tabview);
return ext->tab_cur;
}
/**
* Get the number of tabs
* @param tabview pointer to Tab view object
* @return btn count
*/
uint16_t lv_tabview_get_tab_count(const lv_obj_t * tabview)
{
lv_tabview_ext_t * ext = lv_obj_get_ext_attr(tabview);
return ext->tab_cnt;
}
/**
* Get the page (content area) of a tab
* @param tabview pointer to Tab view object
* @param id index of the btn (>= 0)
* @return pointer to page (lv_page) object
*/
lv_obj_t * lv_tabview_get_tab(const lv_obj_t * tabview, uint16_t id)
{
lv_tabview_ext_t * ext = lv_obj_get_ext_attr(tabview);
uint16_t i = 0;
lv_obj_t * page = lv_obj_get_child_back(ext->content, NULL);
while(page != NULL && i != id) {
i++;
page = lv_obj_get_child_back(ext->content, page);
}
if(i == id) return page;
return NULL;
}
/**
* Get the tab load action
* @param tabview pointer to a tabview object
* @param return the current btn load action
*/
lv_tabview_action_t lv_tabview_get_tab_load_action(const lv_obj_t * tabview)
{
lv_tabview_ext_t * ext = lv_obj_get_ext_attr(tabview);
return ext->tab_load_action;
}
/**
* Get horizontal sliding is enabled or not
* @param tabview pointer to Tab view object
* @return true: enable sliding; false: disable sliding
*/
bool lv_tabview_get_sliding(const lv_obj_t * tabview)
{
lv_tabview_ext_t * ext = lv_obj_get_ext_attr(tabview);
return ext->slide_enable ? true : false;
}
/**
* Get the animation time of tab view when a new tab is loaded
* @param tabview pointer to Tab view object
* @return time of animation in milliseconds
*/
uint16_t lv_tabview_get_anim_time(const lv_obj_t * tabview)
{
lv_tabview_ext_t * ext = lv_obj_get_ext_attr(tabview);
return ext->anim_time;
}
/**
* Get a style of a tab view
* @param tabview pointer to a ab view object
* @param type which style should be get
* @return style pointer to a style
*/
lv_style_t * lv_tabview_get_style(const lv_obj_t * tabview, lv_tabview_style_t type)
{
lv_style_t * style = NULL;
lv_tabview_ext_t * ext = lv_obj_get_ext_attr(tabview);
switch(type) {
case LV_TABVIEW_STYLE_BG:
style = lv_obj_get_style(tabview);
break;
case LV_TABVIEW_STYLE_BTN_BG:
style = lv_btnm_get_style(ext->btns, LV_BTNM_STYLE_BG);
break;
case LV_TABVIEW_STYLE_BTN_REL:
style = lv_btnm_get_style(ext->btns, LV_BTNM_STYLE_BTN_REL);
break;
case LV_TABVIEW_STYLE_BTN_PR:
style = lv_btnm_get_style(ext->btns, LV_BTNM_STYLE_BTN_PR);
break;
case LV_TABVIEW_STYLE_BTN_TGL_REL:
style = lv_btnm_get_style(ext->btns, LV_BTNM_STYLE_BTN_TGL_REL);
break;
case LV_TABVIEW_STYLE_BTN_TGL_PR:
style = lv_btnm_get_style(ext->btns, LV_BTNM_STYLE_BTN_TGL_PR);
break;
default:
style = NULL;
break;
}
return style;
}
/**
* Get position of tab select buttons
* @param tabview pointer to a ab view object
*/
lv_tabview_btns_pos_t lv_tabview_get_btns_pos(const lv_obj_t * tabview)
{
lv_tabview_ext_t * ext = lv_obj_get_ext_attr(tabview);
return ext->btns_pos;
}
/**
* Get whether tab buttons are hidden
* @param tabview pointer to a tab view object
* @return whether tab buttons are hidden
*/
bool lv_tabview_get_btns_hidden(const lv_obj_t *tabview)
{
lv_tabview_ext_t * ext = lv_obj_get_ext_attr(tabview);
return ext->btns_hide;
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Signal function of the Tab view
* @param tabview pointer to a Tab view object
* @param sign a signal type from lv_signal_t enum
* @param param pointer to a signal specific variable
* @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
*/
static lv_res_t lv_tabview_signal(lv_obj_t * tabview, lv_signal_t sign, void * param)
{
lv_res_t res;
/* Include the ancient signal function */
res = ancestor_signal(tabview, sign, param);
if(res != LV_RES_OK) return res;
lv_tabview_ext_t * ext = lv_obj_get_ext_attr(tabview);
if(sign == LV_SIGNAL_CLEANUP) {
uint8_t i;
for(i = 0; ext->tab_name_ptr[i][0] != '\0'; i++) lv_mem_free(ext->tab_name_ptr[i]);
lv_mem_free(ext->tab_name_ptr);
ext->tab_name_ptr = NULL;
ext->btns = NULL; /*These objects were children so they are already invalid*/
ext->content = NULL;
} else if(sign == LV_SIGNAL_CORD_CHG) {
if(ext->content != NULL &&
(lv_obj_get_width(tabview) != lv_area_get_width(param) ||
lv_obj_get_height(tabview) != lv_area_get_height(param))) {
tabview_realign(tabview);
}
} else if(sign == LV_SIGNAL_FOCUS || sign == LV_SIGNAL_DEFOCUS || sign == LV_SIGNAL_CONTROLL) {
/* The button matrix is not in a group (the tab view is in it) but it should handle the group signals.
* So propagate the related signals to the button matrix manually*/
if(ext->btns) {
ext->btns->signal_func(ext->btns, sign, param);
}
if(sign == LV_SIGNAL_FOCUS) {
lv_hal_indev_type_t indev_type = lv_indev_get_type(lv_indev_get_act());
/*With ENCODER select the first button only in edit mode*/
if(indev_type == LV_INDEV_TYPE_ENCODER) {
#if USE_LV_GROUP
lv_group_t * g = lv_obj_get_group(tabview);
if(lv_group_get_editing(g)) {
lv_btnm_ext_t * btnm_ext = lv_obj_get_ext_attr(ext->btns);
btnm_ext->btn_id_pr = 0;
lv_obj_invalidate(ext->btns);
}
#endif
} else {
lv_btnm_ext_t * btnm_ext = lv_obj_get_ext_attr(ext->btns);
btnm_ext->btn_id_pr = 0;
lv_obj_invalidate(ext->btns);
}
}
} else if(sign == LV_SIGNAL_GET_EDITABLE) {
bool * editable = (bool *)param;
*editable = true;
} else if(sign == LV_SIGNAL_GET_TYPE) {
lv_obj_type_t * buf = param;
uint8_t i;
for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/
if(buf->type[i] == NULL) break;
}
buf->type[i] = "lv_tabview";
}
return res;
}
/**
* Signal function of a tab's page
* @param tab pointer to a tab page object
* @param sign a signal type from lv_signal_t enum
* @param param pointer to a signal specific variable
* @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
*/
static lv_res_t tabpage_signal(lv_obj_t * tab_page, lv_signal_t sign, void * param)
{
lv_res_t res;
/* Include the ancient signal function */
res = page_signal(tab_page, sign, param);
if(res != LV_RES_OK) return res;
lv_obj_t * cont = lv_obj_get_parent(tab_page);
lv_obj_t * tabview = lv_obj_get_parent(cont);
if(lv_tabview_get_sliding(tabview) == false) return res;
if(sign == LV_SIGNAL_PRESSED) {
tabpage_pressed_handler(tabview, tab_page);
} else if(sign == LV_SIGNAL_PRESSING) {
tabpage_pressing_handler(tabview, tab_page);
} else if(sign == LV_SIGNAL_RELEASED || sign == LV_SIGNAL_PRESS_LOST) {
tabpage_press_lost_handler(tabview, tab_page);
}
return res;
}
/**
* Signal function of the tab page's scrollable object
* @param tab_scrl pointer to a tab page's scrollable object
* @param sign a signal type from lv_signal_t enum
* @param param pointer to a signal specific variable
* @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
*/
static lv_res_t tabpage_scrl_signal(lv_obj_t * tab_scrl, lv_signal_t sign, void * param)
{
lv_res_t res;
/* Include the ancient signal function */
res = page_scrl_signal(tab_scrl, sign, param);
if(res != LV_RES_OK) return res;
lv_obj_t * tab_page = lv_obj_get_parent(tab_scrl);
lv_obj_t * cont = lv_obj_get_parent(tab_page);
lv_obj_t * tabview = lv_obj_get_parent(cont);
if(lv_tabview_get_sliding(tabview) == false) return res;
if(sign == LV_SIGNAL_PRESSED) {
tabpage_pressed_handler(tabview, tab_page);
} else if(sign == LV_SIGNAL_PRESSING) {
tabpage_pressing_handler(tabview, tab_page);
} else if(sign == LV_SIGNAL_RELEASED || sign == LV_SIGNAL_PRESS_LOST) {
tabpage_press_lost_handler(tabview, tab_page);
}
return res;
}
/**
* Called when a tab's page or scrollable object is pressed
* @param tabview pointer to the btn view object
* @param tabpage pointer to the page of a btn
*/
static void tabpage_pressed_handler(lv_obj_t * tabview, lv_obj_t * tabpage)
{
(void)tabpage;
lv_tabview_ext_t * ext = lv_obj_get_ext_attr(tabview);
lv_indev_t * indev = lv_indev_get_act();
lv_indev_get_point(indev, &ext->point_last);
}
/**
* Called when a tab's page or scrollable object is being pressed
* @param tabview pointer to the btn view object
* @param tabpage pointer to the page of a btn
*/
static void tabpage_pressing_handler(lv_obj_t * tabview, lv_obj_t * tabpage)
{
lv_tabview_ext_t * ext = lv_obj_get_ext_attr(tabview);
lv_indev_t * indev = lv_indev_get_act();
lv_point_t point_act;
lv_indev_get_point(indev, &point_act);
lv_coord_t x_diff = point_act.x - ext->point_last.x;
lv_coord_t y_diff = point_act.y - ext->point_last.y;
if(ext->draging == 0) {
if(x_diff >= LV_INDEV_DRAG_LIMIT || x_diff <= -LV_INDEV_DRAG_LIMIT) {
ext->drag_hor = 1;
ext->draging = 1;
lv_obj_set_drag(lv_page_get_scrl(tabpage), false);
} else if(y_diff >= LV_INDEV_DRAG_LIMIT || y_diff <= -LV_INDEV_DRAG_LIMIT) {
ext->drag_hor = 0;
ext->draging = 1;
}
}
if(ext->drag_hor) {
lv_obj_set_x(ext->content, lv_obj_get_x(ext->content) + point_act.x - ext->point_last.x);
ext->point_last.x = point_act.x;
ext->point_last.y = point_act.y;
/*Move the indicator*/
lv_coord_t indic_width = lv_obj_get_width(ext->indic);
lv_style_t * tabs_style = lv_obj_get_style(ext->btns);
lv_style_t * indic_style = lv_obj_get_style(ext->indic);
lv_coord_t p = ((tabpage->coords.x1 - tabview->coords.x1) * (indic_width + tabs_style->body.padding.inner)) / lv_obj_get_width(tabview);
lv_obj_set_x(ext->indic, indic_width * ext->tab_cur + tabs_style->body.padding.inner * ext->tab_cur + indic_style->body.padding.hor - p);
}
}
/**
* Called when a tab's page or scrollable object is released or the press id lost
* @param tabview pointer to the btn view object
* @param tabpage pointer to the page of a btn
*/
static void tabpage_press_lost_handler(lv_obj_t * tabview, lv_obj_t * tabpage)
{
lv_tabview_ext_t * ext = lv_obj_get_ext_attr(tabview);
ext->drag_hor = 0;
ext->draging = 0;
lv_obj_set_drag(lv_page_get_scrl(tabpage), true);
lv_indev_t * indev = lv_indev_get_act();
lv_point_t point_act;
lv_indev_get_point(indev, &point_act);
lv_point_t vect;
lv_indev_get_vect(indev, &vect);
lv_coord_t x_predict = 0;
while(vect.x != 0) {
x_predict += vect.x;
vect.x = vect.x * (100 - LV_INDEV_DRAG_THROW) / 100;
}
lv_coord_t page_x1 = tabpage->coords.x1 - tabview->coords.x1 + x_predict;
lv_coord_t page_x2 = page_x1 + lv_obj_get_width(tabpage);
lv_coord_t treshold = lv_obj_get_width(tabview) / 2;
uint16_t tab_cur = ext->tab_cur;
if(page_x1 > treshold) {
if(tab_cur != 0) tab_cur--;
} else if(page_x2 < treshold) {
if(tab_cur < ext->tab_cnt - 1) tab_cur++;
}
lv_tabview_set_tab_act(tabview, tab_cur, true);
}
/**
* Called when a tab button is released
* @param tab_btnm pointer to the tab's button matrix object
* @param id the id of the tab (>= 0)
* @return LV_ACTION_RES_OK because the button matrix in not deleted in the function
*/
static lv_res_t tab_btnm_action(lv_obj_t * tab_btnm, const char * tab_name)
{
lv_obj_t * tab = lv_obj_get_parent(tab_btnm);
const char ** tabs_map = lv_btnm_get_map(tab_btnm);
uint8_t i = 0;
while(tabs_map[i][0] != '\0') {
if(strcmp(&tabs_map[i][1], tab_name) == 0) break; /*[1] to skip the control byte*/
i++;
}
lv_tabview_set_tab_act(tab, i, true);
return LV_RES_OK;
}
/**
* Realign and resize the elements of Tab view
* @param tabview pointer to a Tab view object
*/
static void tabview_realign(lv_obj_t * tabview)
{
lv_tabview_ext_t * ext = lv_obj_get_ext_attr(tabview);
lv_obj_set_width(ext->btns, lv_obj_get_width(tabview));
if(ext->btns_hide) {
lv_obj_set_hidden(ext->btns, true);
lv_obj_set_hidden(ext->indic, true);
lv_obj_set_height(ext->content, lv_obj_get_height(tabview));
lv_obj_align(ext->content, NULL, LV_ALIGN_IN_TOP_LEFT, 0, 0);
}
else if(ext->tab_cnt != 0) {
lv_obj_set_hidden(ext->btns, false);
lv_obj_set_hidden(ext->indic, false);
lv_style_t * style_btn_bg = lv_tabview_get_style(tabview, LV_TABVIEW_STYLE_BTN_BG);
lv_style_t * style_btn_rel = lv_tabview_get_style(tabview, LV_TABVIEW_STYLE_BTN_REL);
/*Set the indicator widths*/
lv_coord_t indic_width = (lv_obj_get_width(tabview) - style_btn_bg->body.padding.inner * (ext->tab_cnt - 1) -
2 * style_btn_bg->body.padding.hor) / ext->tab_cnt;
lv_obj_set_width(ext->indic, indic_width);
/*Set the tabs height*/
lv_coord_t btns_height = lv_font_get_height(style_btn_rel->text.font) +
2 * style_btn_rel->body.padding.ver +
2 * style_btn_bg->body.padding.ver;
lv_obj_set_height(ext->btns, btns_height);
lv_obj_set_height(ext->content, lv_obj_get_height(tabview) - lv_obj_get_height(ext->btns));
switch(ext->btns_pos) {
case LV_TABVIEW_BTNS_POS_TOP:
lv_obj_align(ext->btns, NULL, LV_ALIGN_IN_TOP_LEFT, 0, 0);
lv_obj_align(ext->content, ext->btns, LV_ALIGN_OUT_BOTTOM_LEFT, 0, 0);
break;
case LV_TABVIEW_BTNS_POS_BOTTOM:
lv_obj_align(ext->content, NULL, LV_ALIGN_IN_TOP_LEFT, 0, 0);
lv_obj_align(ext->btns, ext->content, LV_ALIGN_OUT_BOTTOM_LEFT, 0, 0);
break;
}
}
lv_obj_t * pages = lv_obj_get_child(ext->content, NULL);
while(pages != NULL) {
if(lv_obj_get_signal_func(pages) == tabpage_signal) { /*Be sure adjust only the pages (user can other things)*/
lv_obj_set_size(pages, lv_obj_get_width(tabview), lv_obj_get_height(ext->content));
}
pages = lv_obj_get_child(ext->content, pages);
}
if(!ext->btns_hide) {
lv_obj_align(ext->indic, ext->btns, LV_ALIGN_IN_BOTTOM_LEFT, 0, 0);
}
lv_tabview_set_tab_act(tabview, ext->tab_cur, false);
}
#endif

View File

@@ -0,0 +1,252 @@
/**
* @file lv_tabview.h
*
*/
#ifndef LV_TABVIEW_H
#define LV_TABVIEW_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#ifdef LV_CONF_INCLUDE_SIMPLE
#include "lv_conf.h"
#else
#include "../../lv_conf.h"
#endif
#if USE_LV_TABVIEW != 0
/*Testing of dependencies*/
#if USE_LV_BTNM == 0
#error "lv_tabview: lv_btnm is required. Enable it in lv_conf.h (USE_LV_BTNM 1) "
#endif
#if USE_LV_PAGE == 0
#error "lv_tabview: lv_page is required. Enable it in lv_conf.h (USE_LV_PAGE 1) "
#endif
#include "../lv_core/lv_obj.h"
#include "../lv_objx/lv_win.h"
#include "../lv_objx/lv_page.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/* parametes: pointer to a tabview object, tab_id
* return: LV_RES_INV: to prevent the loading of the tab; LV_RES_OK: if everything is fine*/
typedef lv_res_t (*lv_tabview_action_t)(lv_obj_t *, uint16_t);
enum {
LV_TABVIEW_BTNS_POS_TOP,
LV_TABVIEW_BTNS_POS_BOTTOM,
};
typedef uint8_t lv_tabview_btns_pos_t;
/*Data of tab*/
typedef struct
{
/*Ext. of ancestor*/
/*New data for this type */
lv_obj_t * btns;
lv_obj_t * indic;
lv_obj_t * content; /*A rectangle to show the current tab*/
const char ** tab_name_ptr;
lv_point_t point_last;
uint16_t tab_cur;
uint16_t tab_cnt;
uint16_t anim_time;
uint8_t slide_enable :1; /*1: enable horizontal sliding by touch pad*/
uint8_t draging :1;
uint8_t drag_hor :1;
uint8_t btns_hide :1;
lv_tabview_btns_pos_t btns_pos :1;
lv_tabview_action_t tab_load_action;
} lv_tabview_ext_t;
enum {
LV_TABVIEW_STYLE_BG,
LV_TABVIEW_STYLE_INDIC,
LV_TABVIEW_STYLE_BTN_BG,
LV_TABVIEW_STYLE_BTN_REL,
LV_TABVIEW_STYLE_BTN_PR,
LV_TABVIEW_STYLE_BTN_TGL_REL,
LV_TABVIEW_STYLE_BTN_TGL_PR,
};
typedef uint8_t lv_tabview_style_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a Tab view object
* @param par pointer to an object, it will be the parent of the new tab
* @param copy pointer to a tab object, if not NULL then the new object will be copied from it
* @return pointer to the created tab
*/
lv_obj_t * lv_tabview_create(lv_obj_t * par, const lv_obj_t * copy);
/**
* Delete all children of the scrl object, without deleting scrl child.
* @param obj pointer to an object
*/
void lv_tabview_clean(lv_obj_t *obj);
/*======================
* Add/remove functions
*=====================*/
/**
* Add a new tab with the given name
* @param tabview pointer to Tab view object where to ass the new tab
* @param name the text on the tab button
* @return pointer to the created page object (lv_page). You can create your content here
*/
lv_obj_t * lv_tabview_add_tab(lv_obj_t * tabview, const char * name);
/*=====================
* Setter functions
*====================*/
/**
* Set a new tab
* @param tabview pointer to Tab view object
* @param id index of a tab to load
* @param anim_en true: set with sliding animation; false: set immediately
*/
void lv_tabview_set_tab_act(lv_obj_t * tabview, uint16_t id, bool anim_en);
/**
* Set an action to call when a tab is loaded (Good to create content only if required)
* lv_tabview_get_act() still gives the current (old) tab (to remove content from here)
* @param tabview pointer to a tabview object
* @param action pointer to a function to call when a tab is loaded
*/
void lv_tabview_set_tab_load_action(lv_obj_t *tabview, lv_tabview_action_t action);
/**
* Enable horizontal sliding with touch pad
* @param tabview pointer to Tab view object
* @param en true: enable sliding; false: disable sliding
*/
void lv_tabview_set_sliding(lv_obj_t * tabview, bool en);
/**
* Set the animation time of tab view when a new tab is loaded
* @param tabview pointer to Tab view object
* @param anim_time time of animation in milliseconds
*/
void lv_tabview_set_anim_time(lv_obj_t * tabview, uint16_t anim_time);
/**
* Set the style of a tab view
* @param tabview pointer to a tan view object
* @param type which style should be set
* @param style pointer to the new style
*/
void lv_tabview_set_style(lv_obj_t *tabview, lv_tabview_style_t type, lv_style_t *style);
/**
* Set the position of tab select buttons
* @param tabview pointer to a tab view object
* @param btns_pos which button position
*/
void lv_tabview_set_btns_pos(lv_obj_t *tabview, lv_tabview_btns_pos_t btns_pos);
/**
* Set whether tab buttons are hidden
* @param tabview pointer to a tab view object
* @param en whether tab buttons are hidden
*/
void lv_tabview_set_btns_hidden(lv_obj_t *tabview, bool en);
/*=====================
* Getter functions
*====================*/
/**
* Get the index of the currently active tab
* @param tabview pointer to Tab view object
* @return the active tab index
*/
uint16_t lv_tabview_get_tab_act(const lv_obj_t * tabview);
/**
* Get the number of tabs
* @param tabview pointer to Tab view object
* @return tab count
*/
uint16_t lv_tabview_get_tab_count(const lv_obj_t * tabview);
/**
* Get the page (content area) of a tab
* @param tabview pointer to Tab view object
* @param id index of the tab (>= 0)
* @return pointer to page (lv_page) object
*/
lv_obj_t * lv_tabview_get_tab(const lv_obj_t * tabview, uint16_t id);
/**
* Get the tab load action
* @param tabview pointer to a tabview object
* @param return the current tab load action
*/
lv_tabview_action_t lv_tabview_get_tab_load_action(const lv_obj_t *tabview);
/**
* Get horizontal sliding is enabled or not
* @param tabview pointer to Tab view object
* @return true: enable sliding; false: disable sliding
*/
bool lv_tabview_get_sliding(const lv_obj_t * tabview);
/**
* Get the animation time of tab view when a new tab is loaded
* @param tabview pointer to Tab view object
* @return time of animation in milliseconds
*/
uint16_t lv_tabview_get_anim_time(const lv_obj_t * tabview);
/**
* Get a style of a tab view
* @param tabview pointer to a ab view object
* @param type which style should be get
* @return style pointer to a style
*/
lv_style_t * lv_tabview_get_style(const lv_obj_t *tabview, lv_tabview_style_t type);
/**
* Get position of tab select buttons
* @param tabview pointer to a ab view object
*/
lv_tabview_btns_pos_t lv_tabview_get_btns_pos(const lv_obj_t *tabview);
/**
* Get whether tab buttons are hidden
* @param tabview pointer to a tab view object
* @return whether tab buttons are hidden
*/
bool lv_tabview_get_btns_hidden(const lv_obj_t *tabview);
/**********************
* MACROS
**********************/
#endif /*USE_LV_TABVIEW*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_TABVIEW_H*/

View File

@@ -0,0 +1,578 @@
/**
* @file lv_tileview.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_tileview.h"
#if USE_LV_TILEVIEW != 0
#include "lv_cont.h"
#include "../lv_themes/lv_theme.h"
/*********************
* DEFINES
*********************/
#if USE_LV_ANIMATION
# ifndef LV_TILEVIEW_ANIM_TIME
# define LV_TILEVIEW_ANIM_TIME 300 /*Animation time loading a tile [ms] (0: no animation) */
# endif
#else
# undef LV_TILEVIEW_ANIM_TIME
# define LV_TILEVIEW_ANIM_TIME 0 /*No animations*/
#endif
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static lv_res_t lv_tileview_signal(lv_obj_t * tileview, lv_signal_t sign, void * param);
static lv_res_t lv_tileview_scrl_signal(lv_obj_t * scrl, lv_signal_t sign, void * param);
static lv_res_t element_signal_func(lv_obj_t * element, lv_signal_t sign, void * param);
static void drag_end_handler(lv_obj_t * tileview);
static bool set_valid_drag_dirs(lv_obj_t * tileview);
/**********************
* STATIC VARIABLES
**********************/
static lv_signal_func_t ancestor_signal;
static lv_signal_func_t ancestor_scrl_signal;
static lv_design_func_t ancestor_design;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Create a tileview object
* @param par pointer to an object, it will be the parent of the new tileview
* @param copy pointer to a tileview object, if not NULL then the new object will be copied from it
* @return pointer to the created tileview
*/
lv_obj_t * lv_tileview_create(lv_obj_t * par, const lv_obj_t * copy)
{
LV_LOG_TRACE("tileview create started");
/*Create the ancestor of tileview*/
lv_obj_t * new_tileview = lv_page_create(par, copy);
lv_mem_assert(new_tileview);
if(new_tileview == NULL) return NULL;
/*Allocate the tileview type specific extended data*/
lv_tileview_ext_t * ext = lv_obj_allocate_ext_attr(new_tileview, sizeof(lv_tileview_ext_t));
lv_mem_assert(ext);
if(ext == NULL) return NULL;
if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_tileview);
if(ancestor_scrl_signal == NULL) ancestor_scrl_signal = lv_obj_get_signal_func(lv_page_get_scrl(new_tileview));
if(ancestor_design == NULL) ancestor_design = lv_obj_get_design_func(new_tileview);
/*Initialize the allocated 'ext' */
ext->anim_time = LV_TILEVIEW_ANIM_TIME;
ext->action = NULL;
ext->act_id.x = 0;
ext->act_id.y = 0;
ext->valid_pos = NULL;
/*The signal and design functions are not copied so set them here*/
lv_obj_set_signal_func(new_tileview, lv_tileview_signal);
lv_obj_set_signal_func(lv_page_get_scrl(new_tileview), lv_tileview_scrl_signal);
/*Init the new tileview*/
if(copy == NULL) {
lv_obj_set_size(new_tileview, LV_HOR_RES, LV_VER_RES);
lv_obj_set_drag_throw(lv_page_get_scrl(new_tileview), false);
lv_page_set_scrl_fit(new_tileview, true, true);
/*Set the default styles*/
lv_theme_t * th = lv_theme_get_current();
if(th) {
lv_page_set_style(new_tileview, LV_PAGE_STYLE_BG, th->tileview.bg);
lv_page_set_style(new_tileview, LV_PAGE_STYLE_SCRL, th->tileview.scrl);
lv_page_set_style(new_tileview, LV_PAGE_STYLE_SB, th->tileview.sb);
} else {
lv_page_set_style(new_tileview, LV_PAGE_STYLE_BG, &lv_style_transp_tight);
lv_page_set_style(new_tileview, LV_PAGE_STYLE_SCRL, &lv_style_transp_tight);
}
}
/*Copy an existing tileview*/
else {
lv_tileview_ext_t * copy_ext = lv_obj_get_ext_attr(copy);
ext->act_id.x = copy_ext->act_id.x;
ext->act_id.y = copy_ext->act_id.y;
ext->action = copy_ext->action;
ext->anim_time = copy_ext->anim_time;
/*Refresh the style with new signal function*/
lv_obj_refresh_style(new_tileview);
}
LV_LOG_INFO("tileview created");
return new_tileview;
}
/*======================
* Add/remove functions
*=====================*/
/**
* Register an object on the tileview. The register object will able to slide the tileview
* @param element pointer to an object
*/
void lv_tileview_add_element(lv_obj_t * element)
{
lv_obj_set_free_ptr(element, lv_obj_get_signal_func(element));
lv_obj_set_signal_func(element, element_signal_func);
lv_obj_set_drag_parent(element, true);
}
/*=====================
* Setter functions
*====================*/
/**
* Set the valid position's indices. The scrolling will be possible only to these positions.
* @param tileview pointer to a Tileview object
* @param valid_pos array width the indices. E.g. `lv_point_t p[] = {{0,0}, {1,0}, {1,1}, {LV_COORD_MIN, LV_COORD_MIN}};`
* Must be closed with `{LV_COORD_MIN, LV_COORD_MIN}`. Only the pointer is saved so can't be a local variable.
*/
void lv_tileview_set_valid_positions(lv_obj_t * tileview, const lv_point_t * valid_pos)
{
lv_tileview_ext_t * ext = lv_obj_get_ext_attr(tileview);
ext->valid_pos = valid_pos;
}
/**
* Set the tile to be shown
* @param tileview pointer to a tileview object
* @param x column id (0, 1, 2...)
* @param y line id (0, 1, 2...)
* @param anim_en true: move with animation
*/
void lv_tileview_set_tile_act(lv_obj_t * tileview, lv_coord_t x, lv_coord_t y, bool anim_en)
{
#if USE_LV_ANIMATION == 0
anim_en = false;
#endif
lv_tileview_ext_t * ext = lv_obj_get_ext_attr(tileview);
uint16_t i;
bool valid = false;
for(i = 0; ext->valid_pos[i].x != LV_COORD_MIN; i++) {
if(ext->valid_pos[i].x == x && ext->valid_pos[i].y == y) {
valid = true;
}
}
if(valid == false) return; /*Don't load not valid tiles*/
lv_res_t res = LV_RES_OK;
if(ext->action) res = ext->action(tileview, x, y);
if(res != LV_RES_OK) return; /*Prevent the tile loading*/
ext->act_id.x = x;
ext->act_id.y = y;
lv_coord_t x_coord = -x * lv_obj_get_width(tileview);
lv_coord_t y_coord = -y * lv_obj_get_height(tileview);
lv_obj_t * scrl = lv_page_get_scrl(tileview);
if(anim_en) {
#if USE_LV_ANIMATION
lv_coord_t x_act = lv_obj_get_x(scrl);
lv_coord_t y_act = lv_obj_get_y(scrl);
lv_anim_t a;
a.var = scrl;
a.fp = (lv_anim_fp_t)lv_obj_set_x;
a.path = lv_anim_path_linear;
a.end_cb = NULL;
a.act_time = 0;
a.time = ext->anim_time;
a.playback = 0;
a.playback_pause = 0;
a.repeat = 0;
a.repeat_pause = 0;
if(x_coord != x_act) {
a.start = x_act;
a.end = x_coord;
lv_anim_create(&a);
}
if(y_coord != y_act) {
a.start = y_act;
a.end = y_coord;
a.fp = (lv_anim_fp_t)lv_obj_set_y;
lv_anim_create(&a);
}
#endif
} else {
lv_obj_set_pos(scrl, x_coord, y_coord);
}
}
void lv_tileview_set_tile_load_action(lv_obj_t * tileview, lv_tileview_action_t action)
{
lv_tileview_ext_t * ext = lv_obj_get_ext_attr(tileview);
ext->action = action;
}
/**
* Set a style of a tileview.
* @param tileview pointer to tileview object
* @param type which style should be set
* @param style pointer to a style
*/
void lv_tileview_set_style(lv_obj_t * tileview, lv_tileview_style_t type, lv_style_t * style)
{
switch(type) {
case LV_TILEVIEW_STYLE_BG:
lv_obj_set_style(tileview, style);
break;
}
}
/*=====================
* Getter functions
*====================*/
/*
* New object specific "get" functions come here
*/
/**
* Get style of a tileview.
* @param tileview pointer to tileview object
* @param type which style should be get
* @return style pointer to the style
*/
lv_style_t * lv_tileview_get_style(const lv_obj_t * tileview, lv_tileview_style_t type)
{
lv_style_t * style = NULL;
switch(type) {
case LV_TILEVIEW_STYLE_BG:
style = lv_obj_get_style(tileview);
break;
default:
style = NULL;
}
return style;
}
/*=====================
* Other functions
*====================*/
/*
* New object specific "other" functions come here
*/
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Signal function of the tileview
* @param tileview pointer to a tileview object
* @param sign a signal type from lv_signal_t enum
* @param param pointer to a signal specific variable
* @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
*/
static lv_res_t lv_tileview_signal(lv_obj_t * tileview, lv_signal_t sign, void * param)
{
lv_res_t res;
/* Include the ancient signal function */
res = ancestor_signal(tileview, sign, param);
if(res != LV_RES_OK) return res;
if(sign == LV_SIGNAL_CLEANUP) {
/*Nothing to cleanup. (No dynamically allocated memory in 'ext')*/
} else if(sign == LV_SIGNAL_GET_TYPE) {
lv_obj_type_t * buf = param;
uint8_t i;
for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/
if(buf->type[i] == NULL) break;
}
buf->type[i] = "lv_tileview";
}
return res;
}
/**
* Signal function of the tileview scrollable
* @param tileview pointer to the scrollable part of the tileview object
* @param sign a signal type from lv_signal_t enum
* @param param pointer to a signal specific variable
* @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
*/
static lv_res_t lv_tileview_scrl_signal(lv_obj_t * scrl, lv_signal_t sign, void * param)
{
lv_res_t res;
/* Include the ancient signal function */
res = ancestor_scrl_signal(scrl, sign, param);
if(res != LV_RES_OK) return res;
lv_obj_t * tileview = lv_obj_get_parent(scrl);
lv_style_t * style_bg = lv_tileview_get_style(tileview, LV_TILEVIEW_STYLE_BG);
/*Apply constraint on moving of the tileview*/
if(sign == LV_SIGNAL_CORD_CHG) {
lv_indev_t * indev = lv_indev_get_act();
if(indev) {
lv_tileview_ext_t * ext = lv_obj_get_ext_attr(tileview);
/*Set horizontal drag constraint if no vertical constraint an dragged to valid x direction */
if(ext->drag_ver == 0 &&
((ext->drag_right_en && indev->proc.drag_sum.x <= -LV_INDEV_DRAG_LIMIT) ||
(ext->drag_left_en && indev->proc.drag_sum.x >= LV_INDEV_DRAG_LIMIT))) {
ext->drag_hor = 1;
}
/*Set vertical drag constraint if no horizontal constraint an dragged to valid y direction */
if(ext->drag_hor == 0 &&
((ext->drag_bottom_en && indev->proc.drag_sum.y <= -LV_INDEV_DRAG_LIMIT) ||
(ext->drag_top_en && indev->proc.drag_sum.y >= LV_INDEV_DRAG_LIMIT))) {
ext->drag_ver = 1;
}
if(ext->drag_hor) {
ext->page.edge_flash.top_ip = 0;
ext->page.edge_flash.bottom_ip = 0;
}
if(ext->drag_ver) {
ext->page.edge_flash.right_ip = 0;
ext->page.edge_flash.left_ip = 0;
}
lv_coord_t x = lv_obj_get_x(scrl);
lv_coord_t y = lv_obj_get_y(scrl);
lv_coord_t h = lv_obj_get_height(tileview);
lv_coord_t w = lv_obj_get_width(tileview);
if(ext->drag_top_en == 0) {
if(y > -(ext->act_id.y * h) && indev->proc.vect.y > 0 && ext->drag_hor == 0) {
if(ext->page.edge_flash.enabled &&
ext->page.edge_flash.left_ip == 0 && ext->page.edge_flash.right_ip == 0 &&
ext->page.edge_flash.top_ip == 0 && ext->page.edge_flash.bottom_ip == 0) {
ext->page.edge_flash.top_ip = 1;
lv_page_start_edge_flash(tileview);
}
lv_obj_set_y(scrl, -ext->act_id.y * h + style_bg->body.padding.ver);
}
}
if(ext->drag_bottom_en == 0 && indev->proc.vect.y < 0 && ext->drag_hor == 0) {
if(y < -(ext->act_id.y * h)) {
if(ext->page.edge_flash.enabled &&
ext->page.edge_flash.left_ip == 0 && ext->page.edge_flash.right_ip == 0 &&
ext->page.edge_flash.top_ip == 0 && ext->page.edge_flash.bottom_ip == 0) {
ext->page.edge_flash.bottom_ip = 1;
lv_page_start_edge_flash(tileview);
}
}
lv_obj_set_y(scrl, -ext->act_id.y * h + style_bg->body.padding.ver);
}
if(ext->drag_left_en == 0) {
if(x > -(ext->act_id.x * w) && indev->proc.vect.x > 0 && ext->drag_ver == 0) {
if(ext->page.edge_flash.enabled &&
ext->page.edge_flash.left_ip == 0 && ext->page.edge_flash.right_ip == 0 &&
ext->page.edge_flash.top_ip == 0 && ext->page.edge_flash.bottom_ip == 0) {
ext->page.edge_flash.left_ip = 1;
lv_page_start_edge_flash(tileview);
}
lv_obj_set_x(scrl, -ext->act_id.x * w + style_bg->body.padding.hor);
}
}
if(ext->drag_right_en == 0 && indev->proc.vect.x < 0 && ext->drag_ver == 0) {
if(x < -(ext->act_id.x * w)) {
if(ext->page.edge_flash.enabled &&
ext->page.edge_flash.left_ip == 0 && ext->page.edge_flash.right_ip == 0 &&
ext->page.edge_flash.top_ip == 0 && ext->page.edge_flash.bottom_ip == 0) {
ext->page.edge_flash.right_ip = 1;
lv_page_start_edge_flash(tileview);
}
}
lv_obj_set_x(scrl, -ext->act_id.x * w + style_bg->body.padding.hor);
}
/*Apply the drag constraints*/
if(ext->drag_ver == 0) lv_obj_set_y(scrl, - ext->act_id.y * lv_obj_get_height(tileview) + style_bg->body.padding.ver);
if(ext->drag_hor == 0) lv_obj_set_x(scrl, - ext->act_id.x * lv_obj_get_width(tileview) + style_bg->body.padding.hor);
}
}
return res;
}
/**
* This function is applied called for the elements of the tileview. Used when the element is
* @param element
* @param sign
* @param param
* @return
*/
static lv_res_t element_signal_func(lv_obj_t * element, lv_signal_t sign, void * param)
{
lv_res_t res;
/* Include the ancient signal function */
lv_signal_func_t sign_func = lv_obj_get_free_ptr(element);
res = sign_func(element, sign, param);
if(res != LV_RES_OK) return res;
/*Initialize some variables on PRESS*/
if(sign == LV_SIGNAL_PRESSED) {
/*Get the tileview from the element*/
lv_obj_t * tileview = lv_obj_get_parent(element);
while(tileview) {
if(lv_obj_get_signal_func(tileview) != lv_tileview_signal) tileview = lv_obj_get_parent(tileview);
else break;
}
if(tileview) {
lv_tileview_ext_t * ext = lv_obj_get_ext_attr(tileview);
ext->drag_hor = 0;
ext->drag_ver = 0;
set_valid_drag_dirs(tileview);
}
}
/*Animate the tabview to the correct location on RELEASE*/
else if(sign == LV_SIGNAL_PRESS_LOST || sign == LV_SIGNAL_RELEASED) {
/*Get the tileview from the element*/
lv_obj_t * tileview = lv_obj_get_parent(element);
while(tileview) {
if(lv_obj_get_signal_func(tileview) != lv_tileview_signal) tileview = lv_obj_get_parent(tileview);
else break;
}
if(tileview) {
/* If the element was dragged and it moved the tileview finish the drag manually to
* let the tileview to finish the move.*/
lv_indev_t * indev = lv_indev_get_act();
lv_tileview_ext_t * ext = lv_obj_get_ext_attr(tileview);
if(indev->proc.drag_in_prog && (ext->drag_hor || ext->drag_ver)) {
lv_obj_t * drag_obj = element;
while(lv_obj_get_drag_parent(drag_obj)) {
drag_obj = lv_obj_get_parent(drag_obj);
if(drag_obj == NULL) break;
}
indev->proc.drag_in_prog = 0;
if(drag_obj) drag_obj->signal_func(drag_obj, LV_SIGNAL_DRAG_END, NULL);
}
drag_end_handler(tileview);
}
}
return res;
}
/**
* Called when the user releases an element of the tileview after dragging it.
* @param tileview pointer to a tileview object
*/
static void drag_end_handler(lv_obj_t * tileview)
{
lv_tileview_ext_t * ext = lv_obj_get_ext_attr(tileview);
lv_indev_t * indev = lv_indev_get_act();
lv_point_t point_act;
lv_indev_get_point(indev, &point_act);
lv_obj_t * scrl = lv_page_get_scrl(tileview);
lv_point_t p;
p.x = - (scrl->coords.x1 - LV_HOR_RES / 2);
p.y = - (scrl->coords.y1 - LV_VER_RES / 2);
/*From the drag vector (drag throw) predict the end position*/
if(ext->drag_hor) {
lv_point_t vect;
lv_indev_get_vect(indev, &vect);
lv_coord_t predict = 0;
while(vect.x != 0) {
predict += vect.x;
vect.x = vect.x * (100 - LV_INDEV_DRAG_THROW) / 100;
}
p.x -= predict;
}
else if(ext->drag_ver) {
lv_point_t vect;
lv_indev_get_vect(indev, &vect);
lv_coord_t predict = 0;
while(vect.y != 0) {
predict += vect.y;
vect.y = vect.y * (100 - LV_INDEV_DRAG_THROW) / 100;
}
p.y -= predict;
}
/*Get the index of the tile*/
p.x = p.x / lv_obj_get_width(tileview);
p.y = p.y / lv_obj_get_height(tileview);
/*Max +- move*/
lv_coord_t x_move = p.x - ext->act_id.x;
lv_coord_t y_move = p.y - ext->act_id.y;
if(x_move < -1) x_move = -1;
if(x_move > 1) x_move = 1;
if(y_move < -1) y_move = -1;
if(y_move > 1) y_move = 1;
/*Set the new tile*/
lv_tileview_set_tile_act(tileview, ext->act_id.x + x_move, ext->act_id.y + y_move,true);
}
static bool set_valid_drag_dirs(lv_obj_t * tileview)
{
lv_tileview_ext_t * ext = lv_obj_get_ext_attr(tileview);
if(ext->valid_pos == NULL) return false;
ext->drag_bottom_en = 0;
ext->drag_top_en = 0;
ext->drag_left_en = 0;
ext->drag_right_en = 0;
uint16_t i;
for(i = 0; ext->valid_pos[i].x != LV_COORD_MIN; i++) {
if(ext->valid_pos[i].x == ext->act_id.x && ext->valid_pos[i].y == ext->act_id.y - 1) ext->drag_top_en = 1;
if(ext->valid_pos[i].x == ext->act_id.x && ext->valid_pos[i].y == ext->act_id.y + 1) ext->drag_bottom_en = 1;
if(ext->valid_pos[i].x == ext->act_id.x - 1 && ext->valid_pos[i].y == ext->act_id.y) ext->drag_left_en = 1;
if(ext->valid_pos[i].x == ext->act_id.x + 1 && ext->valid_pos[i].y == ext->act_id.y) ext->drag_right_en = 1;
}
return true;
}
#endif

View File

@@ -0,0 +1,163 @@
/**
* @file lv_tileview.h
*
*/
#ifndef LV_TILEVIEW_H
#define LV_TILEVIEW_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#ifdef LV_CONF_INCLUDE_SIMPLE
#include "lv_conf.h"
#else
#include "../../lv_conf.h"
#endif
#if USE_LV_TILEVIEW != 0
#include "../lv_objx/lv_page.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/* parametes: pointer to a tileview object, x, y (tile coordinates to load)
* return: LV_RES_INV: to prevent the loading of the tab; LV_RES_OK: if everything is fine*/
typedef lv_res_t (*lv_tileview_action_t)(lv_obj_t *, lv_coord_t, lv_coord_t);
/*Data of tileview*/
typedef struct {
lv_page_ext_t page;
/*New data for this type */
const lv_point_t * valid_pos;
uint16_t anim_time;
lv_tileview_action_t action;
lv_point_t act_id;
uint8_t drag_top_en :1;
uint8_t drag_bottom_en :1;
uint8_t drag_left_en :1;
uint8_t drag_right_en :1;
uint8_t drag_hor :1;
uint8_t drag_ver :1;
} lv_tileview_ext_t;
/*Styles*/
enum {
LV_TILEVIEW_STYLE_BG,
};
typedef uint8_t lv_tileview_style_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a tileview objects
* @param par pointer to an object, it will be the parent of the new tileview
* @param copy pointer to a tileview object, if not NULL then the new object will be copied from it
* @return pointer to the created tileview
*/
lv_obj_t * lv_tileview_create(lv_obj_t * par, const lv_obj_t * copy);
/*======================
* Add/remove functions
*=====================*/
/**
* Register an object on the tileview. The register object will able to slide the tileview
* @param element pointer to an object
*/
void lv_tileview_add_element(lv_obj_t * element);
/*=====================
* Setter functions
*====================*/
/**
* Set the valid position's indices. The scrolling will be possible only to these positions.
* @param tileview pointer to a Tileview object
* @param valid_pos array width the indices. E.g. `lv_point_t p[] = {{0,0}, {1,0}, {1,1}, {LV_COORD_MIN, LV_COORD_MIN}};`
* Must be closed with `{LV_COORD_MIN, LV_COORD_MIN}`. Only the pointer is saved so can't be a local variable.
*/
void lv_tileview_set_valid_positions(lv_obj_t * tileview, const lv_point_t * valid_pos);
/**
* Set the tile to be shown
* @param tileview pointer to a tileview object
* @param x column id (0, 1, 2...)
* @param y line id (0, 1, 2...)
* @param anim_en true: move with animation
*/
void lv_tileview_set_tile_act(lv_obj_t * tileview, lv_coord_t x, lv_coord_t y, bool anim_en);
/**
* Enable the edge flash effect. (Show an arc when the an edge is reached)
* @param tileview pointer to a Tileview
* @param en true or false to enable/disable end flash
*/
static inline void lv_tileview_set_edge_flash(lv_obj_t * tileview, bool en)
{
lv_page_set_edge_flash(tileview, en);
}
/**
* Set a style of a tileview.
* @param tileview pointer to tileview object
* @param type which style should be set
* @param style pointer to a style
*/
void lv_tileview_set_style(lv_obj_t * tileview, lv_tileview_style_t type, lv_style_t *style);
/*=====================
* Getter functions
*====================*/
/**
* Get the scroll propagation property
* @param tileview pointer to a Tileview
* @return true or false
*/
static inline bool lv_tileview_get_edge_flash(lv_obj_t * tileview)
{
return lv_page_get_edge_flash(tileview);
}
/**
* Get style of a tileview.
* @param tileview pointer to tileview object
* @param type which style should be get
* @return style pointer to the style
*/
lv_style_t * lv_tileview_get_style(const lv_obj_t * tileview, lv_tileview_style_t type);
/*=====================
* Other functions
*====================*/
/**********************
* MACROS
**********************/
#endif /*USE_LV_TILEVIEW*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_TILEVIEW_H*/

View File

@@ -0,0 +1,591 @@
/*
* Copyright (c) 2019 CTCaer
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file lv_win.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_win.h"
#if USE_LV_WIN != 0
#include "../lv_themes/lv_theme.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static lv_res_t lv_win_signal(lv_obj_t * win, lv_signal_t sign, void * param);
static void lv_win_realign(lv_obj_t * win);
/**********************
* STATIC VARIABLES
**********************/
static lv_signal_func_t ancestor_signal;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Create a window objects
* @param par pointer to an object, it will be the parent of the new window
* @param copy pointer to a window object, if not NULL then the new object will be copied from it
* @return pointer to the created window
*/
lv_obj_t * lv_win_create(lv_obj_t * par, const lv_obj_t * copy)
{
LV_LOG_TRACE("window create started");
/*Create the ancestor object*/
lv_obj_t * new_win = lv_obj_create(par, copy);
lv_mem_assert(new_win);
if(new_win == NULL) return NULL;
if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_win);
/*Allocate the object type specific extended data*/
lv_win_ext_t * ext = lv_obj_allocate_ext_attr(new_win, sizeof(lv_win_ext_t));
lv_mem_assert(ext);
if(ext == NULL) return NULL;
ext->page = NULL;
ext->header = NULL;
ext->title = NULL;
ext->style_header = &lv_style_plain_color;
ext->style_btn_rel = &lv_style_btn_rel;
ext->style_btn_pr = &lv_style_btn_pr;
ext->btn_size = (LV_DPI) / 2;
/*Init the new window object*/
if(copy == NULL) {
lv_obj_set_size(new_win, LV_HOR_RES, LV_VER_RES);
lv_obj_set_pos(new_win, 0, 0);
lv_obj_set_style(new_win, &lv_style_pretty);
ext->page = lv_page_create(new_win, NULL);
lv_obj_set_protect(ext->page, LV_PROTECT_PARENT);
lv_page_set_sb_mode(ext->page, LV_SB_MODE_AUTO);
lv_page_set_arrow_scroll(ext->page, true);
/*Create a holder for the header*/
ext->header = lv_obj_create(new_win, NULL);
/*Move back the header because it is automatically moved to the scrollable */
lv_obj_set_protect(ext->header, LV_PROTECT_PARENT);
lv_obj_set_parent(ext->header, new_win);
lv_obj_set_width(ext->header, LV_HOR_RES - 62);////
ext->btn_size = lv_obj_get_height(ext->header) - 3;////
/*Create a title on the header*/
ext->title = lv_label_create(ext->header, NULL);
lv_label_set_text(ext->title, "My title");
/*Set the default styles*/
lv_theme_t * th = lv_theme_get_current();
if(th) {
lv_win_set_style(new_win, LV_WIN_STYLE_BG, th->win.bg);
lv_win_set_style(new_win, LV_WIN_STYLE_SB, th->win.sb);
lv_win_set_style(new_win, LV_WIN_STYLE_HEADER, th->win.header);
lv_win_set_style(new_win, LV_WIN_STYLE_CONTENT_BG, th->win.content.bg);
lv_win_set_style(new_win, LV_WIN_STYLE_CONTENT_SCRL, th->win.content.scrl);
lv_win_set_style(new_win, LV_WIN_STYLE_BTN_REL, th->win.btn.rel);
lv_win_set_style(new_win, LV_WIN_STYLE_BTN_PR, th->win.btn.pr);
} else {
lv_win_set_style(new_win, LV_WIN_STYLE_BG, &lv_style_plain);
lv_win_set_style(new_win, LV_WIN_STYLE_CONTENT_BG, &lv_style_plain);
lv_win_set_style(new_win, LV_WIN_STYLE_CONTENT_SCRL, &lv_style_transp);
lv_win_set_style(new_win, LV_WIN_STYLE_HEADER, &lv_style_plain_color);
}
lv_obj_set_signal_func(new_win, lv_win_signal);
lv_obj_set_size(new_win, LV_HOR_RES, LV_VER_RES);
}
/*Copy an existing object*/
else {
lv_win_ext_t * copy_ext = lv_obj_get_ext_attr(copy);
/*Create the objects*/
ext->header = lv_obj_create(new_win, copy_ext->header);
ext->title = lv_label_create(ext->header, copy_ext->title);
ext->page = lv_page_create(new_win, copy_ext->page);
ext->btn_size = copy_ext->btn_size;
/*Copy the control buttons*/
lv_obj_t * child;
lv_obj_t * cbtn;
child = lv_obj_get_child_back(copy_ext->header, NULL);
child = lv_obj_get_child_back(copy_ext->header, child); /*Sip the title*/
while(child != NULL) {
cbtn = lv_btn_create(ext->header, child);
lv_img_create(cbtn, lv_obj_get_child(child, NULL));
child = lv_obj_get_child_back(copy_ext->header, child);
}
lv_obj_set_signal_func(new_win, lv_win_signal);
/*Refresh the style with new signal function*/
lv_obj_refresh_style(new_win);
}
lv_win_realign(new_win);
LV_LOG_INFO("window created");
return new_win;
}
/**
* Delete all children of the scrl object, without deleting scrl child.
* @param obj pointer to an object
*/
void lv_win_clean(lv_obj_t * obj)
{
lv_obj_t * scrl = lv_page_get_scrl(obj);
lv_obj_clean(scrl);
}
/*======================
* Add/remove functions
*=====================*/
/**
* Add control button to the header of the window
* @param win pointer to a window object
* @param img_src an image source ('lv_img_t' variable, path to file or a symbol)
* @param rel_action a function pointer to call when the button is released
* @return pointer to the created button object
*/
lv_obj_t * lv_win_add_btn(lv_obj_t * win, const void * img_src, const char * label_src, lv_action_t rel_action)
{
lv_win_ext_t * ext = lv_obj_get_ext_attr(win);
lv_obj_t * btn = lv_btn_create(ext->header, NULL);
lv_btn_set_style(btn, LV_BTN_STYLE_REL, ext->style_btn_rel);
lv_btn_set_style(btn, LV_BTN_STYLE_PR, ext->style_btn_pr);
lv_obj_set_size(btn, lv_obj_get_width(btn), ext->btn_size);
lv_btn_set_fit(btn, true, false);
lv_btn_set_action(btn, LV_BTN_ACTION_CLICK, rel_action);
if (img_src)
{
lv_obj_t * img = lv_img_create(btn, NULL);
lv_obj_set_click(img, false);
lv_img_set_src(img, img_src);
}
else if (label_src)
{
lv_obj_t *label = lv_label_create(btn, NULL);
lv_label_set_recolor(label, true);
lv_label_set_text(label, label_src);
}
lv_win_realign(win);
return btn;
}
/*=====================
* Setter functions
*====================*/
/**
* A release action which can be assigned to a window control button to close it
* @param btn pointer to the released button
* @return always LV_ACTION_RES_INV because the button is deleted with the window
*/
lv_res_t lv_win_close_action(lv_obj_t * btn)
{
lv_obj_t * win = lv_win_get_from_btn(btn);
lv_obj_del(win);
return LV_RES_INV;
}
/**
* Set the title of a window
* @param win pointer to a window object
* @param title string of the new title
*/
void lv_win_set_title(lv_obj_t * win, const char * title)
{
lv_win_ext_t * ext = lv_obj_get_ext_attr(win);
lv_label_set_text(ext->title, title);
lv_win_realign(win);
}
/**
* Set the control button size of a window
* @param win pointer to a window object
* @param size control button size
*/
void lv_win_set_btn_size(lv_obj_t * win, lv_coord_t size)
{
lv_win_ext_t * ext = lv_obj_get_ext_attr(win);
if(ext->btn_size == size) return;
ext->btn_size = size;
lv_win_realign(win);
}
/**
* Set the layout of the window
* @param win pointer to a window object
* @param layout the layout from 'lv_layout_t'
*/
void lv_win_set_layout(lv_obj_t * win, lv_layout_t layout)
{
lv_win_ext_t * ext = lv_obj_get_ext_attr(win);
lv_page_set_scrl_layout(ext->page, layout);
}
/**
* Set the scroll bar mode of a window
* @param win pointer to a window object
* @param sb_mode the new scroll bar mode from 'lv_sb_mode_t'
*/
void lv_win_set_sb_mode(lv_obj_t * win, lv_sb_mode_t sb_mode)
{
lv_win_ext_t * ext = lv_obj_get_ext_attr(win);
lv_page_set_sb_mode(ext->page, sb_mode);
}
/**
* Set a style of a window
* @param win pointer to a window object
* @param type which style should be set
* @param style pointer to a style
*/
void lv_win_set_style(lv_obj_t * win, lv_win_style_t type, lv_style_t * style)
{
lv_win_ext_t * ext = lv_obj_get_ext_attr(win);
switch(type) {
case LV_WIN_STYLE_BG:
lv_obj_set_style(win, style);
lv_win_realign(win);
break;
case LV_WIN_STYLE_CONTENT_BG:
lv_page_set_style(ext->page, LV_PAGE_STYLE_BG, style);
break;
case LV_WIN_STYLE_CONTENT_SCRL:
lv_page_set_style(ext->page, LV_PAGE_STYLE_SCRL, style);
break;
case LV_WIN_STYLE_SB:
lv_page_set_style(ext->page, LV_PAGE_STYLE_SB, style);
break;
case LV_WIN_STYLE_HEADER:
lv_obj_set_style(ext->header, style);
lv_win_realign(win);
break;
case LV_WIN_STYLE_BTN_REL:
ext->style_btn_rel = style;
break;
case LV_WIN_STYLE_BTN_PR:
ext->style_btn_pr = style;
break;
}
/*Refresh the existing buttons*/
if(type == LV_WIN_STYLE_BTN_REL || type == LV_WIN_STYLE_BTN_PR) {
lv_obj_t * btn;
btn = lv_obj_get_child_back(ext->header, NULL);
btn = lv_obj_get_child_back(ext->header, btn); /*Skip the title*/
while(btn != NULL) {
if(type == LV_WIN_STYLE_BTN_REL) lv_btn_set_style(btn, LV_BTN_STYLE_REL, style);
else lv_btn_set_style(btn, LV_BTN_STYLE_PR, style);
btn = lv_obj_get_child_back(ext->header, btn);
}
}
}
/**
* Set drag status of a window. If set to 'true' window can be dragged like on a PC.
* @param win pointer to a window object
* @param en whether dragging is enabled
*/
void lv_win_set_drag(lv_obj_t *win, bool en)
{
lv_win_ext_t * ext = lv_obj_get_ext_attr(win);
lv_obj_t * win_header = ext->header;
lv_obj_set_drag_parent(win_header, en);
lv_obj_set_drag(win, en);
}
/*=====================
* Getter functions
*====================*/
/**
* Get the title of a window
* @param win pointer to a window object
* @return title string of the window
*/
const char * lv_win_get_title(const lv_obj_t * win)
{
lv_win_ext_t * ext = lv_obj_get_ext_attr(win);
return lv_label_get_text(ext->title);
}
/**
* Get the content holder object of window (`lv_page`) to allow additional customization
* @param win pointer to a window object
* @return the Page object where the window's content is
*/
lv_obj_t * lv_win_get_content(const lv_obj_t * win)
{
lv_win_ext_t * ext = lv_obj_get_ext_attr(win);
return ext->page;
}
/**
* Get the control button size of a window
* @param win pointer to a window object
* @return control button size
*/
lv_coord_t lv_win_get_btn_size(const lv_obj_t * win)
{
lv_win_ext_t * ext = lv_obj_get_ext_attr(win);
return ext->btn_size;
}
/**
* Get the pointer of a widow from one of its control button.
* It is useful in the action of the control buttons where only button is known.
* @param ctrl_btn pointer to a control button of a window
* @return pointer to the window of 'ctrl_btn'
*/
lv_obj_t * lv_win_get_from_btn(const lv_obj_t * ctrl_btn)
{
lv_obj_t * header = lv_obj_get_parent(ctrl_btn);
lv_obj_t * win = lv_obj_get_parent(header);
return win;
}
/**
* Get the layout of a window
* @param win pointer to a window object
* @return the layout of the window (from 'lv_layout_t')
*/
lv_layout_t lv_win_get_layout(lv_obj_t * win)
{
lv_win_ext_t * ext = lv_obj_get_ext_attr(win);
return lv_page_get_scrl_layout(ext->page);
}
/**
* Get the scroll bar mode of a window
* @param win pointer to a window object
* @return the scroll bar mode of the window (from 'lv_sb_mode_t')
*/
lv_sb_mode_t lv_win_get_sb_mode(lv_obj_t * win)
{
lv_win_ext_t * ext = lv_obj_get_ext_attr(win);
return lv_page_get_sb_mode(ext->page);
}
/**
* Get width of the content area (page scrollable) of the window
* @param win pointer to a window object
* @return the width of the content_bg area
*/
lv_coord_t lv_win_get_width(lv_obj_t * win)
{
lv_win_ext_t * ext = lv_obj_get_ext_attr(win);
lv_obj_t * scrl = lv_page_get_scrl(ext->page);
lv_style_t * style_scrl = lv_obj_get_style(scrl);
return lv_obj_get_width(scrl) - 2 * style_scrl->body.padding.hor;
}
/**
* Get a style of a window
* @param win pointer to a button object
* @param type which style window be get
* @return style pointer to a style
*/
lv_style_t * lv_win_get_style(const lv_obj_t * win, lv_win_style_t type)
{
lv_style_t * style = NULL;
lv_win_ext_t * ext = lv_obj_get_ext_attr(win);
switch(type) {
case LV_WIN_STYLE_BG:
style = lv_obj_get_style(win);
break;
case LV_WIN_STYLE_CONTENT_BG:
style = lv_page_get_style(ext->page, LV_PAGE_STYLE_BG);
break;
case LV_WIN_STYLE_CONTENT_SCRL:
style = lv_page_get_style(ext->page, LV_PAGE_STYLE_SCRL);
break;
case LV_WIN_STYLE_SB:
style = lv_page_get_style(ext->page, LV_PAGE_STYLE_SB);
break;
case LV_WIN_STYLE_HEADER:
style = lv_obj_get_style(ext->header);
break;
case LV_WIN_STYLE_BTN_REL:
style = ext->style_btn_rel;
break;
case LV_WIN_STYLE_BTN_PR:
style = ext->style_btn_pr;
break;
default:
style = NULL;
break;
}
return style;
}
/*=====================
* Other functions
*====================*/
/**
* Focus on an object. It ensures that the object will be visible in the window.
* @param win pointer to a window object
* @param obj pointer to an object to focus (must be in the window)
* @param anim_time scroll animation time in milliseconds (0: no animation)
*/
void lv_win_focus(lv_obj_t * win, lv_obj_t * obj, uint16_t anim_time)
{
lv_win_ext_t * ext = lv_obj_get_ext_attr(win);
lv_page_focus(ext->page, obj, anim_time);
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Signal function of the window
* @param win pointer to a window object
* @param sign a signal type from lv_signal_t enum
* @param param pointer to a signal specific variable
* @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
*/
static lv_res_t lv_win_signal(lv_obj_t * win, lv_signal_t sign, void * param)
{
lv_res_t res;
/* Include the ancient signal function */
res = ancestor_signal(win, sign, param);
if(res != LV_RES_OK) return res;
lv_win_ext_t * ext = lv_obj_get_ext_attr(win);
if(sign == LV_SIGNAL_CHILD_CHG) { /*Move children to the page*/
lv_obj_t * page = ext->page;
if(page != NULL) {
lv_obj_t * child;
child = lv_obj_get_child(win, NULL);
while(child != NULL) {
if(lv_obj_is_protected(child, LV_PROTECT_PARENT) == false) {
lv_obj_t * tmp = child;
child = lv_obj_get_child(win, child); /*Get the next child before move this*/
lv_obj_set_parent(tmp, page);
} else {
child = lv_obj_get_child(win, child);
}
}
}
} else if(sign == LV_SIGNAL_STYLE_CHG) {
lv_win_realign(win);
} else if(sign == LV_SIGNAL_CORD_CHG) {
/*If the size is changed refresh the window*/
if(lv_area_get_width(param) != lv_obj_get_width(win) ||
lv_area_get_height(param) != lv_obj_get_height(win)) {
lv_win_realign(win);
}
} else if(sign == LV_SIGNAL_CLEANUP) {
ext->header = NULL; /*These objects were children so they are already invalid*/
ext->page = NULL;
ext->title = NULL;
} else if(sign == LV_SIGNAL_CONTROLL) {
/*Forward all the control signals to the page*/
ext->page->signal_func(ext->page, sign, param);
} else if(sign == LV_SIGNAL_GET_TYPE) {
lv_obj_type_t * buf = param;
uint8_t i;
for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/
if(buf->type[i] == NULL) break;
}
buf->type[i] = "lv_win";
}
return res;
}
/**
* Realign the building elements of a window
* @param win pointer to window objectker
*/
static void lv_win_realign(lv_obj_t * win)
{
lv_win_ext_t * ext = lv_obj_get_ext_attr(win);
if(ext->page == NULL || ext->header == NULL || ext->title == NULL) return;
lv_style_t * header_style = lv_win_get_style(win, LV_WIN_STYLE_HEADER);
lv_obj_set_size(ext->header, lv_obj_get_width(win) - 62, ext->btn_size + 2 * header_style->body.padding.ver);
bool first_btn = true;
lv_obj_t * btn;
lv_obj_t * btn_prev = NULL;
/*Refresh the size of all control buttons*/
btn = lv_obj_get_child_back(ext->header, NULL);
btn = lv_obj_get_child_back(ext->header, btn); /*Skip the title*/
while(btn != NULL) {
lv_obj_set_size(btn, lv_obj_get_width(btn), ext->btn_size);
if(first_btn) {
lv_obj_align(btn, ext->header, LV_ALIGN_IN_RIGHT_MID, - header_style->body.padding.hor, 0);
first_btn = false;
} else {
lv_obj_align(btn, btn_prev, LV_ALIGN_OUT_LEFT_MID, - header_style->body.padding.inner, 0);
}
btn_prev = btn;
btn = lv_obj_get_child_back(ext->header, btn);
}
lv_obj_align(ext->title, NULL, LV_ALIGN_IN_LEFT_MID, ext->style_header->body.padding.hor, 0);
lv_obj_set_pos(ext->header, 31, 0);
lv_obj_set_size(ext->page, lv_obj_get_width(win), lv_obj_get_height(win) - lv_obj_get_height(ext->header));
lv_obj_align(ext->page, ext->header, LV_ALIGN_OUT_BOTTOM_LEFT, 0, 0);
}
#endif

View File

@@ -0,0 +1,298 @@
/*
* Copyright (c) 2019 CTCaer
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file lv_win.h
*
*/
#ifndef LV_WIN_H
#define LV_WIN_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#ifdef LV_CONF_INCLUDE_SIMPLE
#include "lv_conf.h"
#else
#include "../../lv_conf.h"
#endif
#if USE_LV_WIN != 0
/*Testing of dependencies*/
#if USE_LV_BTN == 0
#error "lv_win: lv_btn is required. Enable it in lv_conf.h (USE_LV_BTN 1) "
#endif
#if USE_LV_LABEL == 0
#error "lv_win: lv_label is required. Enable it in lv_conf.h (USE_LV_LABEL 1) "
#endif
#if USE_LV_IMG == 0
#error "lv_win: lv_img is required. Enable it in lv_conf.h (USE_LV_IMG 1) "
#endif
#if USE_LV_PAGE == 0
#error "lv_win: lv_page is required. Enable it in lv_conf.h (USE_LV_PAGE 1) "
#endif
#include "../lv_core/lv_obj.h"
#include "lv_cont.h"
#include "lv_btn.h"
#include "lv_label.h"
#include "lv_img.h"
#include "lv_page.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/*Data of window*/
typedef struct
{
/*Ext. of ancestor*/
/*New data for this type */
lv_obj_t * page; /*Pointer to a page which holds the content*/
lv_obj_t * header; /*Pointer to the header container of the window*/
lv_obj_t * title; /*Pointer to the title label of the window*/
lv_style_t * style_header; /*Style of the header container*/
lv_style_t * style_btn_rel; /*Control button releases style*/
lv_style_t * style_btn_pr; /*Control button pressed style*/
lv_coord_t btn_size; /*Size of the control buttons (square)*/
} lv_win_ext_t;
enum {
LV_WIN_STYLE_BG,
LV_WIN_STYLE_CONTENT_BG,
LV_WIN_STYLE_CONTENT_SCRL,
LV_WIN_STYLE_SB,
LV_WIN_STYLE_HEADER,
LV_WIN_STYLE_BTN_REL,
LV_WIN_STYLE_BTN_PR,
};
typedef uint8_t lv_win_style_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a window objects
* @param par pointer to an object, it will be the parent of the new window
* @param copy pointer to a window object, if not NULL then the new object will be copied from it
* @return pointer to the created window
*/
lv_obj_t * lv_win_create(lv_obj_t * par, const lv_obj_t * copy);
/**
* Delete all children of the scrl object, without deleting scrl child.
* @param obj pointer to an object
*/
void lv_win_clean(lv_obj_t *obj);
/*======================
* Add/remove functions
*=====================*/
/**
* Add control button to the header of the window
* @param win pointer to a window object
* @param img_src an image source ('lv_img_t' variable, path to file or a symbol)
* @param rel_action a function pointer to call when the button is released
* @return pointer to the created button object
*/
lv_obj_t * lv_win_add_btn(lv_obj_t * win, const void * img_src, const char * label_src, lv_action_t rel_action);
/*=====================
* Setter functions
*====================*/
/**
* A release action which can be assigned to a window control button to close it
* @param btn pointer to the released button
* @return always LV_ACTION_RES_INV because the button is deleted with the window
*/
lv_res_t lv_win_close_action(lv_obj_t * btn);
/**
* Set the title of a window
* @param win pointer to a window object
* @param title string of the new title
*/
void lv_win_set_title(lv_obj_t * win, const char * title);
/**
* Set the control button size of a window
* @param win pointer to a window object
* @return control button size
*/
void lv_win_set_btn_size(lv_obj_t * win, lv_coord_t size);
/**
* Set the layout of the window
* @param win pointer to a window object
* @param layout the layout from 'lv_layout_t'
*/
void lv_win_set_layout(lv_obj_t *win, lv_layout_t layout);
/**
* Set the scroll bar mode of a window
* @param win pointer to a window object
* @param sb_mode the new scroll bar mode from 'lv_sb_mode_t'
*/
void lv_win_set_sb_mode(lv_obj_t *win, lv_sb_mode_t sb_mode);
/**
* Set a style of a window
* @param win pointer to a window object
* @param type which style should be set
* @param style pointer to a style
*/
void lv_win_set_style(lv_obj_t *win, lv_win_style_t type, lv_style_t *style);
/**
* Set drag status of a window. If set to 'true' window can be dragged like on a PC.
* @param win pointer to a window object
* @param en whether dragging is enabled
*/
void lv_win_set_drag(lv_obj_t *win, bool en);
/*=====================
* Getter functions
*====================*/
/**
* Get the title of a window
* @param win pointer to a window object
* @return title string of the window
*/
const char * lv_win_get_title(const lv_obj_t * win);
/**
* Get the content holder object of window (`lv_page`) to allow additional customization
* @param win pointer to a window object
* @return the Page object where the window's content is
*/
lv_obj_t * lv_win_get_content(const lv_obj_t * win);
/**
* Get the control button size of a window
* @param win pointer to a window object
* @return control button size
*/
lv_coord_t lv_win_get_btn_size(const lv_obj_t * win);
/**
* Get the pointer of a widow from one of its control button.
* It is useful in the action of the control buttons where only button is known.
* @param ctrl_btn pointer to a control button of a window
* @return pointer to the window of 'ctrl_btn'
*/
lv_obj_t * lv_win_get_from_btn(const lv_obj_t * ctrl_btn);
/**
* Get the layout of a window
* @param win pointer to a window object
* @return the layout of the window (from 'lv_layout_t')
*/
lv_layout_t lv_win_get_layout(lv_obj_t *win);
/**
* Get the scroll bar mode of a window
* @param win pointer to a window object
* @return the scroll bar mode of the window (from 'lv_sb_mode_t')
*/
lv_sb_mode_t lv_win_get_sb_mode(lv_obj_t *win);
/**
* Get width of the content area (page scrollable) of the window
* @param win pointer to a window object
* @return the width of the content area
*/
lv_coord_t lv_win_get_width(lv_obj_t * win);
/**
* Get a style of a window
* @param win pointer to a button object
* @param type which style window be get
* @return style pointer to a style
*/
lv_style_t * lv_win_get_style(const lv_obj_t *win, lv_win_style_t type);
/**
* Get drag status of a window. If set to 'true' window can be dragged like on a PC.
* @param win pointer to a window object
* @return whether window is draggable
*/
static inline bool lv_win_get_drag(const lv_obj_t *win)
{
return lv_obj_get_drag(win);
}
/*=====================
* Other functions
*====================*/
/**
* Focus on an object. It ensures that the object will be visible in the window.
* @param win pointer to a window object
* @param obj pointer to an object to focus (must be in the window)
* @param anim_time scroll animation time in milliseconds (0: no animation)
*/
void lv_win_focus(lv_obj_t * win, lv_obj_t * obj, uint16_t anim_time);
/**
* Scroll the window horizontally
* @param win pointer to a window object
* @param dist the distance to scroll (< 0: scroll right; > 0 scroll left)
*/
static inline void lv_win_scroll_hor(lv_obj_t * win, lv_coord_t dist)
{
lv_win_ext_t * ext = (lv_win_ext_t *)lv_obj_get_ext_attr(win);
lv_page_scroll_hor(ext->page, dist);
}
/**
* Scroll the window vertically
* @param win pointer to a window object
* @param dist the distance to scroll (< 0: scroll down; > 0 scroll up)
*/
static inline void lv_win_scroll_ver(lv_obj_t * win, lv_coord_t dist)
{
lv_win_ext_t * ext = (lv_win_ext_t *)lv_obj_get_ext_attr(win);
lv_page_scroll_ver(ext->page, dist);
}
/**********************
* MACROS
**********************/
#endif /*USE_LV_WIN*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_WIN_H*/