resubmit the lvgl code

This commit is contained in:
Wang_Weigen
2022-01-18 16:38:38 +08:00
parent a5066f8a36
commit 65a3861ea7
879 changed files with 304372 additions and 1 deletions

View File

@@ -0,0 +1,30 @@
# Extra components
This directory contains extra (optional) components to lvgl.
It's a good place for contributions as there are less strict expectations about the completeness and flexibility of the components here.
In other words, if you have created a complex widget from other widgets, or modified an existing widget with special events, styles or animations, or have a new feature that could work as a plugin to lvgl feel free to the share it here.
## How to contribute
- Create a [Pull request](https://docs.lvgl.io/8.0/CONTRIBUTING.html#pull-request) with your new content
- Please and follow the [Coding style](https://github.com/lvgl/lvgl/blob/master/docs/CODING_STYLE.md) of LVGL
- Add setter/getter functions in pair
- Update [lv_conf_template.h](https://github.com/lvgl/lvgl/blob/master/lv_conf_template.h)
- Add description in the [docs](https://github.com/lvgl/lvgl/tree/master/docs)
- Add [examples](https://github.com/lvgl/lvgl/tree/master/examples)
- Update the [changelog](https://github.com/lvgl/lvgl/tree/master/docs/CHANGELOG.md)
- Add yourself to the [Contributors](#contributors) section below.
## Ideas
Here some ideas as inspiration feel free to contribute with ideas too.
- New [Calendar headers](https://github.com/lvgl/lvgl/tree/master/src/extra/widgets/calendar)
- Color picker with RGB and or HSV bars
- Ruler, horizontal or vertical with major and minor ticks and labels
- New [List items types](https://github.com/lvgl/lvgl/tree/master/src/extra/widgets/list)
- [Preloaders](https://www.google.com/search?q=preloader&sxsrf=ALeKk01ddA4YB0WEgLLN1bZNSm8YER7pkg:1623080551559&source=lnms&tbm=isch&sa=X&ved=2ahUKEwiwoN6d7oXxAhVuw4sKHVedBB4Q_AUoAXoECAEQAw&biw=952&bih=940)
- Drop-down list with a container to which an content can be added
- 9 patch button: Similar to [lv_imgbtn](https://docs.lvgl.io/8.0/widgets/extra/imgbtn.html) but 9 images for 4 corner, 4 sides and the center
## Contributors
- lv_animimg: @ZhaoQiang-b45475
- lv_span: @guoweilkd

View File

@@ -0,0 +1 @@
SRC_FILES += $(shell find -L $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/extra -name \*.c)

View File

@@ -0,0 +1,595 @@
/**
* @file lv_flex.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../lv_layouts.h"
#if LV_USE_FLEX
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_flex_align_t main_place;
lv_flex_align_t cross_place;
lv_flex_align_t track_place;
uint8_t row : 1;
uint8_t wrap : 1;
uint8_t rev : 1;
} flex_t;
typedef struct {
lv_obj_t * item;
lv_coord_t min_size;
lv_coord_t max_size;
lv_coord_t final_size;
uint32_t grow_value;
uint32_t clamped : 1;
} grow_dsc_t;
typedef struct {
lv_coord_t track_cross_size;
lv_coord_t track_main_size; /*For all items*/
lv_coord_t track_fix_main_size; /*For non grow items*/
uint32_t item_cnt;
grow_dsc_t * grow_dsc;
uint32_t grow_item_cnt;
uint32_t grow_dsc_calc : 1;
} track_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void flex_update(lv_obj_t * cont, void * user_data);
static int32_t find_track_end(lv_obj_t * cont, flex_t * f, int32_t item_start_id, lv_coord_t item_gap,
lv_coord_t max_main_size, track_t * t);
static void children_repos(lv_obj_t * cont, flex_t * f, int32_t item_first_id, int32_t item_last_id, lv_coord_t abs_x,
lv_coord_t abs_y, lv_coord_t max_main_size, lv_coord_t item_gap, track_t * t);
static void place_content(lv_flex_align_t place, lv_coord_t max_size, lv_coord_t content_size, lv_coord_t item_cnt,
lv_coord_t * start_pos, lv_coord_t * gap);
static lv_obj_t * get_next_item(lv_obj_t * cont, bool rev, int32_t * item_id);
/**********************
* GLOBAL VARIABLES
**********************/
uint32_t LV_LAYOUT_FLEX;
lv_style_prop_t LV_STYLE_FLEX_FLOW;
lv_style_prop_t LV_STYLE_FLEX_MAIN_PLACE;
lv_style_prop_t LV_STYLE_FLEX_CROSS_PLACE;
lv_style_prop_t LV_STYLE_FLEX_TRACK_PLACE;
lv_style_prop_t LV_STYLE_FLEX_GROW;
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/*=====================
* Setter functions
*====================*/
void lv_flex_init(void)
{
LV_LAYOUT_FLEX = lv_layout_register(flex_update, NULL);
LV_STYLE_FLEX_FLOW = lv_style_register_prop();
LV_STYLE_FLEX_MAIN_PLACE = lv_style_register_prop() | LV_STYLE_PROP_LAYOUT_REFR;
LV_STYLE_FLEX_CROSS_PLACE = lv_style_register_prop() | LV_STYLE_PROP_LAYOUT_REFR;
LV_STYLE_FLEX_TRACK_PLACE = lv_style_register_prop() | LV_STYLE_PROP_LAYOUT_REFR;
}
void lv_obj_set_flex_flow(lv_obj_t * obj, lv_flex_flow_t flow)
{
lv_obj_set_style_flex_flow(obj, flow, 0);
lv_obj_set_style_layout(obj, LV_LAYOUT_FLEX, 0);
}
void lv_obj_set_flex_align(lv_obj_t * obj, lv_flex_align_t main_place, lv_flex_align_t cross_place,
lv_flex_align_t track_place)
{
lv_obj_set_style_flex_main_place(obj, main_place, 0);
lv_obj_set_style_flex_cross_place(obj, cross_place, 0);
lv_obj_set_style_flex_track_place(obj, track_place, 0);
lv_obj_set_style_layout(obj, LV_LAYOUT_FLEX, 0);
}
void lv_obj_set_flex_grow(lv_obj_t * obj, uint8_t grow)
{
lv_obj_set_style_flex_grow(obj, grow, 0);
lv_obj_mark_layout_as_dirty(lv_obj_get_parent(obj));
}
void lv_style_set_flex_flow(lv_style_t * style, lv_flex_flow_t value)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_style_set_prop(style, LV_STYLE_FLEX_FLOW, v);
}
void lv_style_set_flex_main_place(lv_style_t * style, lv_flex_align_t value)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_style_set_prop(style, LV_STYLE_FLEX_MAIN_PLACE, v);
}
void lv_style_set_flex_cross_place(lv_style_t * style, lv_flex_align_t value)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_style_set_prop(style, LV_STYLE_FLEX_CROSS_PLACE, v);
}
void lv_style_set_flex_track_place(lv_style_t * style, lv_flex_align_t value)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_style_set_prop(style, LV_STYLE_FLEX_TRACK_PLACE, v);
}
void lv_style_set_flex_grow(lv_style_t * style, uint8_t value)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_style_set_prop(style, LV_STYLE_FLEX_GROW, v);
}
void lv_obj_set_style_flex_flow(lv_obj_t * obj, lv_flex_flow_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t) value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_FLEX_FLOW, v, selector);
}
void lv_obj_set_style_flex_main_place(lv_obj_t * obj, lv_flex_align_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t) value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_FLEX_MAIN_PLACE, v, selector);
}
void lv_obj_set_style_flex_cross_place(lv_obj_t * obj, lv_flex_align_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t) value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_FLEX_CROSS_PLACE, v, selector);
}
void lv_obj_set_style_flex_track_place(lv_obj_t * obj, lv_flex_align_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t) value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_FLEX_TRACK_PLACE, v, selector);
}
void lv_obj_set_style_flex_grow(lv_obj_t * obj, uint8_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t) value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_FLEX_GROW, v, selector);
}
/**********************
* STATIC FUNCTIONS
**********************/
static void flex_update(lv_obj_t * cont, void * user_data)
{
LV_LOG_INFO("update %p container", (void *)cont);
LV_UNUSED(user_data);
flex_t f;
lv_flex_flow_t flow = lv_obj_get_style_flex_flow(cont, LV_PART_MAIN);
f.row = flow & _LV_FLEX_COLUMN ? 0 : 1;
f.wrap = flow & _LV_FLEX_WRAP ? 1 : 0;
f.rev = flow & _LV_FLEX_REVERSE ? 1 : 0;
f.main_place = lv_obj_get_style_flex_main_place(cont, LV_PART_MAIN);
f.cross_place = lv_obj_get_style_flex_cross_place(cont, LV_PART_MAIN);
f.track_place = lv_obj_get_style_flex_track_place(cont, LV_PART_MAIN);
bool rtl = lv_obj_get_style_base_dir(cont, LV_PART_MAIN) == LV_BASE_DIR_RTL ? true : false;
lv_coord_t track_gap = !f.row ? lv_obj_get_style_pad_column(cont, LV_PART_MAIN) : lv_obj_get_style_pad_row(cont,
LV_PART_MAIN);
lv_coord_t item_gap = f.row ? lv_obj_get_style_pad_column(cont, LV_PART_MAIN) : lv_obj_get_style_pad_row(cont,
LV_PART_MAIN);
lv_coord_t max_main_size = (f.row ? lv_obj_get_content_width(cont) : lv_obj_get_content_height(cont));
lv_coord_t border_width = lv_obj_get_style_border_width(cont, LV_PART_MAIN);
lv_coord_t abs_y = cont->coords.y1 + lv_obj_get_style_pad_top(cont,
LV_PART_MAIN) + border_width - lv_obj_get_scroll_y(cont);
lv_coord_t abs_x = cont->coords.x1 + lv_obj_get_style_pad_left(cont,
LV_PART_MAIN) + border_width - lv_obj_get_scroll_x(cont);
lv_flex_align_t track_cross_place = f.track_place;
lv_coord_t * cross_pos = (f.row ? &abs_y : &abs_x);
lv_coord_t w_set = lv_obj_get_style_width(cont, LV_PART_MAIN);
lv_coord_t h_set = lv_obj_get_style_height(cont, LV_PART_MAIN);
/*Content sized objects should squeezed the gap between the children, therefore any alignment will look like `START`*/
if((f.row && h_set == LV_SIZE_CONTENT && cont->h_layout == 0) ||
(!f.row && w_set == LV_SIZE_CONTENT && cont->w_layout == 0)) {
track_cross_place = LV_FLEX_ALIGN_START;
}
if(rtl && !f.row) {
if(track_cross_place == LV_FLEX_ALIGN_START) track_cross_place = LV_FLEX_ALIGN_END;
else if(track_cross_place == LV_FLEX_ALIGN_END) track_cross_place = LV_FLEX_ALIGN_START;
}
lv_coord_t total_track_cross_size = 0;
lv_coord_t gap = 0;
uint32_t track_cnt = 0;
int32_t track_first_item;
int32_t next_track_first_item;
if(track_cross_place != LV_FLEX_ALIGN_START) {
track_first_item = f.rev ? cont->spec_attr->child_cnt - 1 : 0;
track_t t;
while(track_first_item < (int32_t)cont->spec_attr->child_cnt && track_first_item >= 0) {
/*Search the first item of the next row*/
t.grow_dsc_calc = 0;
next_track_first_item = find_track_end(cont, &f, track_first_item, max_main_size, item_gap, &t);
total_track_cross_size += t.track_cross_size + track_gap;
track_cnt++;
track_first_item = next_track_first_item;
}
if(track_cnt) total_track_cross_size -= track_gap; /*No gap after the last track*/
/*Place the tracks to get the start position*/
lv_coord_t max_cross_size = (f.row ? lv_obj_get_content_height(cont) : lv_obj_get_content_width(cont));
place_content(track_cross_place, max_cross_size, total_track_cross_size, track_cnt, cross_pos, &gap);
}
track_first_item = f.rev ? cont->spec_attr->child_cnt - 1 : 0;
if(rtl && !f.row) {
*cross_pos += total_track_cross_size;
}
while(track_first_item < (int32_t)cont->spec_attr->child_cnt && track_first_item >= 0) {
track_t t;
t.grow_dsc_calc = 1;
/*Search the first item of the next row*/
next_track_first_item = find_track_end(cont, &f, track_first_item, max_main_size, item_gap, &t);
if(rtl && !f.row) {
*cross_pos -= t.track_cross_size;
}
children_repos(cont, &f, track_first_item, next_track_first_item, abs_x, abs_y, max_main_size, item_gap, &t);
track_first_item = next_track_first_item;
lv_mem_buf_release(t.grow_dsc);
t.grow_dsc = NULL;
if(rtl && !f.row) {
*cross_pos -= gap + track_gap;
}
else {
*cross_pos += t.track_cross_size + gap + track_gap;
}
}
LV_ASSERT_MEM_INTEGRITY();
if(w_set == LV_SIZE_CONTENT || h_set == LV_SIZE_CONTENT) {
lv_obj_refr_size(cont);
}
lv_event_send(cont, LV_EVENT_LAYOUT_CHANGED, NULL);
LV_TRACE_LAYOUT("finished");
}
/**
* Find the last item of a track
*/
static int32_t find_track_end(lv_obj_t * cont, flex_t * f, int32_t item_start_id, lv_coord_t max_main_size,
lv_coord_t item_gap, track_t * t)
{
lv_coord_t w_set = lv_obj_get_style_width(cont, LV_PART_MAIN);
lv_coord_t h_set = lv_obj_get_style_height(cont, LV_PART_MAIN);
/*Can't wrap if the size if auto (i.e. the size depends on the children)*/
if(f->wrap && ((f->row && w_set == LV_SIZE_CONTENT) || (!f->row && h_set == LV_SIZE_CONTENT))) {
f->wrap = false;
}
lv_coord_t(*get_main_size)(const lv_obj_t *) = (f->row ? lv_obj_get_width : lv_obj_get_height);
lv_coord_t(*get_cross_size)(const lv_obj_t *) = (!f->row ? lv_obj_get_width : lv_obj_get_height);
t->track_main_size = 0;
t->track_fix_main_size = 0;
t->grow_item_cnt = 0;
t->track_cross_size = 0;
t->item_cnt = 0;
t->grow_dsc = NULL;
int32_t item_id = item_start_id;
lv_obj_t * item = lv_obj_get_child(cont, item_id);
while(item) {
if(item_id != item_start_id && lv_obj_has_flag(item, LV_OBJ_FLAG_FLEX_IN_NEW_TRACK)) break;
if(!lv_obj_has_flag_any(item, LV_OBJ_FLAG_IGNORE_LAYOUT | LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_FLOATING)) {
uint8_t grow_value = lv_obj_get_style_flex_grow(item, LV_PART_MAIN);
if(grow_value) {
t->grow_item_cnt++;
t->track_fix_main_size += item_gap;
if(t->grow_dsc_calc) {
grow_dsc_t * new_dsc = lv_mem_buf_get(sizeof(grow_dsc_t) * (t->grow_item_cnt));
LV_ASSERT_MALLOC(new_dsc);
if(new_dsc == NULL) return item_id;
if(t->grow_dsc) {
lv_memcpy(new_dsc, t->grow_dsc, sizeof(grow_dsc_t) * (t->grow_item_cnt - 1));
lv_mem_buf_release(t->grow_dsc);
}
new_dsc[t->grow_item_cnt - 1].item = item;
new_dsc[t->grow_item_cnt - 1].min_size = f->row ? lv_obj_get_style_min_width(item,
LV_PART_MAIN) : lv_obj_get_style_min_height(item, LV_PART_MAIN);
new_dsc[t->grow_item_cnt - 1].max_size = f->row ? lv_obj_get_style_max_width(item,
LV_PART_MAIN) : lv_obj_get_style_max_height(item, LV_PART_MAIN);
new_dsc[t->grow_item_cnt - 1].grow_value = grow_value;
new_dsc[t->grow_item_cnt - 1].clamped = 0;
t->grow_dsc = new_dsc;
}
}
else {
lv_coord_t item_size = get_main_size(item);
if(f->wrap && t->track_fix_main_size + item_size > max_main_size) break;
t->track_fix_main_size += item_size + item_gap;
}
t->track_cross_size = LV_MAX(get_cross_size(item), t->track_cross_size);
t->item_cnt++;
}
item_id += f->rev ? -1 : +1;
if(item_id < 0) break;
item = lv_obj_get_child(cont, item_id);
}
if(t->track_fix_main_size > 0) t->track_fix_main_size -= item_gap; /*There is no gap after the last item*/
/*If there is at least one "grow item" the track takes the full space*/
t->track_main_size = t->grow_item_cnt ? max_main_size : t->track_fix_main_size;
/*Have at least one item in a row*/
if(item && item_id == item_start_id) {
item = cont->spec_attr->children[item_id];
get_next_item(cont, f->rev, &item_id);
if(item) {
t->track_cross_size = get_cross_size(item);
t->track_main_size = get_main_size(item);
t->item_cnt = 1;
}
}
return item_id;
}
/**
* Position the children in the same track
*/
static void children_repos(lv_obj_t * cont, flex_t * f, int32_t item_first_id, int32_t item_last_id, lv_coord_t abs_x,
lv_coord_t abs_y, lv_coord_t max_main_size, lv_coord_t item_gap, track_t * t)
{
void (*area_set_main_size)(lv_area_t *, lv_coord_t) = (f->row ? lv_area_set_width : lv_area_set_height);
lv_coord_t (*area_get_main_size)(const lv_area_t *) = (f->row ? lv_area_get_width : lv_area_get_height);
lv_coord_t (*area_get_cross_size)(const lv_area_t *) = (!f->row ? lv_area_get_width : lv_area_get_height);
/*Calculate the size of grow items first*/
uint32_t i;
bool grow_reiterate = true;
while(grow_reiterate) {
grow_reiterate = false;
lv_coord_t grow_value_sum = 0;
lv_coord_t grow_max_size = t->track_main_size - t->track_fix_main_size;
for(i = 0; i < t->grow_item_cnt; i++) {
if(t->grow_dsc[i].clamped == 0) {
grow_value_sum += t->grow_dsc[i].grow_value;
}
else {
grow_max_size -= t->grow_dsc[i].final_size;
}
}
lv_coord_t grow_unit;
for(i = 0; i < t->grow_item_cnt; i++) {
if(t->grow_dsc[i].clamped == 0) {
grow_unit = grow_max_size / grow_value_sum;
lv_coord_t size = grow_unit * t->grow_dsc[i].grow_value;
lv_coord_t size_clamp = LV_CLAMP(t->grow_dsc[i].min_size, size, t->grow_dsc[i].max_size);
if(size_clamp != size) {
t->grow_dsc[i].clamped = 1;
grow_reiterate = true;
}
t->grow_dsc[i].final_size = size_clamp;
grow_value_sum -= t->grow_dsc[i].grow_value;
grow_max_size -= t->grow_dsc[i].final_size;
}
}
}
bool rtl = lv_obj_get_style_base_dir(cont, LV_PART_MAIN) == LV_BASE_DIR_RTL ? true : false;
lv_coord_t main_pos = 0;
lv_coord_t place_gap = 0;
place_content(f->main_place, max_main_size, t->track_main_size, t->item_cnt, &main_pos, &place_gap);
if(f->row && rtl) main_pos += lv_obj_get_content_width(cont);
lv_obj_t * item = lv_obj_get_child(cont, item_first_id);
/*Reposition the children*/
while(item && item_first_id != item_last_id) {
if(lv_obj_has_flag_any(item, LV_OBJ_FLAG_IGNORE_LAYOUT | LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_FLOATING)) {
item = get_next_item(cont, f->rev, &item_first_id);
continue;
}
lv_coord_t grow_size = lv_obj_get_style_flex_grow(item, LV_PART_MAIN);
if(grow_size) {
lv_coord_t s = 0;
for(i = 0; i < t->grow_item_cnt; i++) {
if(t->grow_dsc[i].item == item) {
s = t->grow_dsc[i].final_size;
break;
}
}
if(f->row) item->w_layout = 1;
else item->h_layout = 1;
if(s != area_get_main_size(&item->coords)) {
lv_obj_invalidate(item);
lv_area_t old_coords;
lv_area_copy(&old_coords, &item->coords);
area_set_main_size(&item->coords, s);
lv_event_send(item, LV_EVENT_SIZE_CHANGED, &old_coords);
lv_event_send(lv_obj_get_parent(item), LV_EVENT_CHILD_CHANGED, item);
lv_obj_invalidate(item);
}
}
else {
item->w_layout = 0;
item->h_layout = 0;
}
lv_coord_t cross_pos = 0;
switch(f->cross_place) {
case LV_FLEX_ALIGN_CENTER:
/*Round up the cross size to avoid rounding error when dividing by 2
*The issue comes up e,g, with column direction with center cross direction if an element's width changes*/
cross_pos = (((t->track_cross_size + 1) & (~1)) - area_get_cross_size(&item->coords)) / 2;
break;
case LV_FLEX_ALIGN_END:
cross_pos = t->track_cross_size - area_get_cross_size(&item->coords);
break;
default:
break;
}
if(f->row && rtl) main_pos -= area_get_main_size(&item->coords);
/*Handle percentage value of translate*/
lv_coord_t tr_x = lv_obj_get_style_translate_x(item, LV_PART_MAIN);
lv_coord_t tr_y = lv_obj_get_style_translate_y(item, LV_PART_MAIN);
lv_coord_t w = lv_obj_get_width(item);
lv_coord_t h = lv_obj_get_height(item);
if(LV_COORD_IS_PCT(tr_x)) tr_x = (w * LV_COORD_GET_PCT(tr_x)) / 100;
if(LV_COORD_IS_PCT(tr_y)) tr_y = (h * LV_COORD_GET_PCT(tr_y)) / 100;
lv_coord_t diff_x = abs_x - item->coords.x1 + tr_x;
lv_coord_t diff_y = abs_y - item->coords.y1 + tr_y;
diff_x += f->row ? main_pos : cross_pos;
diff_y += f->row ? cross_pos : main_pos;
if(diff_x || diff_y) {
lv_obj_invalidate(item);
item->coords.x1 += diff_x;
item->coords.x2 += diff_x;
item->coords.y1 += diff_y;
item->coords.y2 += diff_y;
lv_obj_invalidate(item);
lv_obj_move_children_by(item, diff_x, diff_y, true);
}
if(!(f->row && rtl)) main_pos += area_get_main_size(&item->coords) + item_gap + place_gap;
else main_pos -= item_gap + place_gap;
item = get_next_item(cont, f->rev, &item_first_id);
}
}
/**
* Tell a start coordinate and gap for a placement type.
*/
static void place_content(lv_flex_align_t place, lv_coord_t max_size, lv_coord_t content_size, lv_coord_t item_cnt,
lv_coord_t * start_pos, lv_coord_t * gap)
{
if(item_cnt <= 1) {
switch(place) {
case LV_FLEX_ALIGN_SPACE_BETWEEN:
case LV_FLEX_ALIGN_SPACE_AROUND:
case LV_FLEX_ALIGN_SPACE_EVENLY:
place = LV_FLEX_ALIGN_CENTER;
break;
default:
break;
}
}
switch(place) {
case LV_FLEX_ALIGN_CENTER:
*gap = 0;
*start_pos += (max_size - content_size) / 2;
break;
case LV_FLEX_ALIGN_END:
*gap = 0;
*start_pos += max_size - content_size;
break;
case LV_FLEX_ALIGN_SPACE_BETWEEN:
*gap = (lv_coord_t)(max_size - content_size) / (lv_coord_t)(item_cnt - 1);
break;
case LV_FLEX_ALIGN_SPACE_AROUND:
*gap += (lv_coord_t)(max_size - content_size) / (lv_coord_t)(item_cnt);
*start_pos += *gap / 2;
break;
case LV_FLEX_ALIGN_SPACE_EVENLY:
*gap = (lv_coord_t)(max_size - content_size) / (lv_coord_t)(item_cnt + 1);
*start_pos += *gap;
break;
default:
*gap = 0;
}
}
static lv_obj_t * get_next_item(lv_obj_t * cont, bool rev, int32_t * item_id)
{
if(rev) {
(*item_id)--;
if(*item_id >= 0) return cont->spec_attr->children[*item_id];
else return NULL;
}
else {
(*item_id)++;
if((*item_id) < (int32_t)cont->spec_attr->child_cnt) return cont->spec_attr->children[*item_id];
else return NULL;
}
}
#endif /*LV_USE_FLEX*/

View File

@@ -0,0 +1,153 @@
/**
* @file lv_flex.h
*
*/
#ifndef LV_FLEX_H
#define LV_FLEX_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../core/lv_obj.h"
#if LV_USE_FLEX
/*********************
* DEFINES
*********************/
#define LV_OBJ_FLAG_FLEX_IN_NEW_TRACK LV_OBJ_FLAG_LAYOUT_1
LV_EXPORT_CONST_INT(LV_OBJ_FLAG_FLEX_IN_NEW_TRACK);
#define _LV_FLEX_COLUMN (1 << 0)
#define _LV_FLEX_WRAP (1 << 2)
#define _LV_FLEX_REVERSE (1 << 3)
/**********************
* TYPEDEFS
**********************/
/*Can't include lv_obj.h because it includes this header file*/
struct _lv_obj_t;
typedef enum {
LV_FLEX_ALIGN_START,
LV_FLEX_ALIGN_END,
LV_FLEX_ALIGN_CENTER,
LV_FLEX_ALIGN_SPACE_EVENLY,
LV_FLEX_ALIGN_SPACE_AROUND,
LV_FLEX_ALIGN_SPACE_BETWEEN,
} lv_flex_align_t;
typedef enum {
LV_FLEX_FLOW_ROW = 0x00,
LV_FLEX_FLOW_COLUMN = _LV_FLEX_COLUMN,
LV_FLEX_FLOW_ROW_WRAP = LV_FLEX_FLOW_ROW | _LV_FLEX_WRAP,
LV_FLEX_FLOW_ROW_REVERSE = LV_FLEX_FLOW_ROW | _LV_FLEX_REVERSE,
LV_FLEX_FLOW_ROW_WRAP_REVERSE = LV_FLEX_FLOW_ROW | _LV_FLEX_WRAP | _LV_FLEX_REVERSE,
LV_FLEX_FLOW_COLUMN_WRAP = LV_FLEX_FLOW_COLUMN | _LV_FLEX_WRAP,
LV_FLEX_FLOW_COLUMN_REVERSE = LV_FLEX_FLOW_COLUMN | _LV_FLEX_REVERSE,
LV_FLEX_FLOW_COLUMN_WRAP_REVERSE = LV_FLEX_FLOW_COLUMN | _LV_FLEX_WRAP | _LV_FLEX_REVERSE,
} lv_flex_flow_t;
/**********************
* GLOBAL VARIABLES
**********************/
extern uint32_t LV_LAYOUT_FLEX;
extern lv_style_prop_t LV_STYLE_FLEX_FLOW;
extern lv_style_prop_t LV_STYLE_FLEX_MAIN_PLACE;
extern lv_style_prop_t LV_STYLE_FLEX_CROSS_PLACE;
extern lv_style_prop_t LV_STYLE_FLEX_TRACK_PLACE;
extern lv_style_prop_t LV_STYLE_FLEX_GROW;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Initialize a flex layout the default values
* @param flex pointer to a flex layout descriptor
*/
void lv_flex_init(void);
/**
* Set hot the item should flow
* @param flex pointer to a flex layout descriptor
* @param flow an element of `lv_flex_flow_t`.
*/
void lv_obj_set_flex_flow(lv_obj_t * obj, lv_flex_flow_t flow);
/**
* Set how to place (where to align) the items and tracks
* @param flex pointer: to a flex layout descriptor
* @param main_place where to place the items on main axis (in their track). Any value of `lv_flex_align_t`.
* @param cross_place where to place the item in their track on the cross axis. `LV_FLEX_ALIGN_START/END/CENTER`
* @param track_place where to place the tracks in the cross direction. Any value of `lv_flex_align_t`.
*/
void lv_obj_set_flex_align(lv_obj_t * obj, lv_flex_align_t main_place, lv_flex_align_t cross_place,
lv_flex_align_t track_cross_place);
/**
* Sets the width or height (on main axis) to grow the object in order fill the free space
* @param obj pointer to an object. The parent must have flex layout else nothing will happen.
* @param grow a value to set how much free space to take proportionally to other growing items.
*/
void lv_obj_set_flex_grow(lv_obj_t * obj, uint8_t grow);
void lv_style_set_flex_flow(lv_style_t * style, lv_flex_flow_t value);
void lv_style_set_flex_main_place(lv_style_t * style, lv_flex_align_t value);
void lv_style_set_flex_cross_place(lv_style_t * style, lv_flex_align_t value);
void lv_style_set_flex_track_place(lv_style_t * style, lv_flex_align_t value);
void lv_style_set_flex_grow(lv_style_t * style, uint8_t value);
void lv_obj_set_style_flex_flow(lv_obj_t * obj, lv_flex_flow_t value, lv_style_selector_t selector);
void lv_obj_set_style_flex_main_place(lv_obj_t * obj, lv_flex_align_t value, lv_style_selector_t selector);
void lv_obj_set_style_flex_cross_place(lv_obj_t * obj, lv_flex_align_t value, lv_style_selector_t selector);
void lv_obj_set_style_flex_track_place(lv_obj_t * obj, lv_flex_align_t value, lv_style_selector_t selector);
void lv_obj_set_style_flex_grow(lv_obj_t * obj, uint8_t value, lv_style_selector_t selector);
static inline lv_flex_flow_t lv_obj_get_style_flex_flow(const lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_FLEX_FLOW);
return (lv_flex_flow_t)v.num;
}
static inline lv_flex_align_t lv_obj_get_style_flex_main_place(const lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_FLEX_MAIN_PLACE);
return (lv_flex_align_t)v.num;
}
static inline lv_flex_align_t lv_obj_get_style_flex_cross_place(const lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_FLEX_CROSS_PLACE);
return (lv_flex_align_t)v.num;
}
static inline lv_flex_align_t lv_obj_get_style_flex_track_place(const lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_FLEX_TRACK_PLACE);
return (lv_flex_align_t)v.num;
}
static inline uint8_t lv_obj_get_style_flex_grow(const lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_FLEX_GROW);
return (uint8_t)v.num;
}
/**********************
* MACROS
**********************/
#endif /*LV_USE_FLEX*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_FLEX_H*/

View File

@@ -0,0 +1,780 @@
/**
* @file lv_grid.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../lv_layouts.h"
#if LV_USE_GRID
/*********************
* DEFINES
*********************/
/**
* Some helper defines
*/
#define IS_FR(x) (x >= LV_COORD_MAX - 100)
#define IS_CONTENT(x) (x == LV_COORD_MAX - 101)
#define GET_FR(x) (x - (LV_COORD_MAX - 100))
/**********************
* TYPEDEFS
**********************/
typedef struct {
uint32_t col;
uint32_t row;
lv_point_t grid_abs;
} item_repos_hint_t;
typedef struct {
lv_coord_t * x;
lv_coord_t * y;
lv_coord_t * w;
lv_coord_t * h;
uint32_t col_num;
uint32_t row_num;
lv_coord_t grid_w;
lv_coord_t grid_h;
} _lv_grid_calc_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void grid_update(lv_obj_t * cont, void * user_data);
static void calc(lv_obj_t * obj, _lv_grid_calc_t * calc);
static void calc_free(_lv_grid_calc_t * calc);
static void calc_cols(lv_obj_t * cont, _lv_grid_calc_t * c);
static void calc_rows(lv_obj_t * cont, _lv_grid_calc_t * c);
static void item_repos(lv_obj_t * item, _lv_grid_calc_t * c, item_repos_hint_t * hint);
static lv_coord_t grid_align(lv_coord_t cont_size, bool auto_size, uint8_t align, lv_coord_t gap, uint32_t track_num,
lv_coord_t * size_array, lv_coord_t * pos_array, bool reverse);
static uint32_t count_tracks(const lv_coord_t * templ);
static inline const lv_coord_t * get_col_dsc(lv_obj_t * obj)
{
return lv_obj_get_style_grid_column_dsc_array(obj, 0);
}
static inline const lv_coord_t * get_row_dsc(lv_obj_t * obj)
{
return lv_obj_get_style_grid_row_dsc_array(obj, 0);
}
static inline uint8_t get_col_pos(lv_obj_t * obj)
{
return lv_obj_get_style_grid_cell_column_pos(obj, 0);
}
static inline uint8_t get_row_pos(lv_obj_t * obj)
{
return lv_obj_get_style_grid_cell_row_pos(obj, 0);
}
static inline uint8_t get_col_span(lv_obj_t * obj)
{
return lv_obj_get_style_grid_cell_column_span(obj, 0);
}
static inline uint8_t get_row_span(lv_obj_t * obj)
{
return lv_obj_get_style_grid_cell_row_span(obj, 0);
}
static inline uint8_t get_cell_col_align(lv_obj_t * obj)
{
return lv_obj_get_style_grid_cell_x_align(obj, 0);
}
static inline uint8_t get_cell_row_align(lv_obj_t * obj)
{
return lv_obj_get_style_grid_cell_y_align(obj, 0);
}
static inline uint8_t get_grid_col_align(lv_obj_t * obj)
{
return lv_obj_get_style_grid_column_align(obj, 0);
}
static inline uint8_t get_grid_row_align(lv_obj_t * obj)
{
return lv_obj_get_style_grid_row_align(obj, 0);
}
/**********************
* GLOBAL VARIABLES
**********************/
uint32_t LV_LAYOUT_GRID;
lv_style_prop_t LV_STYLE_GRID_COLUMN_DSC_ARRAY;
lv_style_prop_t LV_STYLE_GRID_COLUMN_ALIGN;
lv_style_prop_t LV_STYLE_GRID_ROW_DSC_ARRAY;
lv_style_prop_t LV_STYLE_GRID_ROW_ALIGN;
lv_style_prop_t LV_STYLE_GRID_CELL_COLUMN_POS;
lv_style_prop_t LV_STYLE_GRID_CELL_COLUMN_SPAN;
lv_style_prop_t LV_STYLE_GRID_CELL_X_ALIGN;
lv_style_prop_t LV_STYLE_GRID_CELL_ROW_POS;
lv_style_prop_t LV_STYLE_GRID_CELL_ROW_SPAN;
lv_style_prop_t LV_STYLE_GRID_CELL_Y_ALIGN;
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_grid_init(void)
{
LV_LAYOUT_GRID = lv_layout_register(grid_update, NULL);
LV_STYLE_GRID_COLUMN_DSC_ARRAY = lv_style_register_prop() | LV_STYLE_PROP_LAYOUT_REFR;
LV_STYLE_GRID_ROW_DSC_ARRAY = lv_style_register_prop() | LV_STYLE_PROP_LAYOUT_REFR;
LV_STYLE_GRID_COLUMN_ALIGN = lv_style_register_prop() | LV_STYLE_PROP_LAYOUT_REFR;
LV_STYLE_GRID_ROW_ALIGN = lv_style_register_prop() | LV_STYLE_PROP_LAYOUT_REFR;
LV_STYLE_GRID_CELL_ROW_SPAN = lv_style_register_prop() | LV_STYLE_PROP_LAYOUT_REFR;
LV_STYLE_GRID_CELL_ROW_POS = lv_style_register_prop() | LV_STYLE_PROP_LAYOUT_REFR;
LV_STYLE_GRID_CELL_COLUMN_SPAN = lv_style_register_prop() | LV_STYLE_PROP_LAYOUT_REFR;
LV_STYLE_GRID_CELL_COLUMN_POS = lv_style_register_prop() | LV_STYLE_PROP_LAYOUT_REFR;
LV_STYLE_GRID_CELL_X_ALIGN = lv_style_register_prop() | LV_STYLE_PROP_LAYOUT_REFR;
LV_STYLE_GRID_CELL_Y_ALIGN = lv_style_register_prop() | LV_STYLE_PROP_LAYOUT_REFR;
}
void lv_obj_set_grid_dsc_array(lv_obj_t * obj, const lv_coord_t col_dsc[], const lv_coord_t row_dsc[])
{
lv_obj_set_style_grid_column_dsc_array(obj, col_dsc, 0);
lv_obj_set_style_grid_row_dsc_array(obj, row_dsc, 0);
lv_obj_set_style_layout(obj, LV_LAYOUT_GRID, 0);
}
void lv_obj_set_grid_align(lv_obj_t * obj, lv_grid_align_t column_align, lv_grid_align_t row_align)
{
lv_obj_set_style_grid_column_align(obj, column_align, 0);
lv_obj_set_style_grid_row_align(obj, row_align, 0);
}
void lv_obj_set_grid_cell(lv_obj_t * obj, lv_grid_align_t x_align, uint8_t col_pos, uint8_t col_span,
lv_grid_align_t y_align, uint8_t row_pos, uint8_t row_span)
{
lv_obj_set_style_grid_cell_column_pos(obj, col_pos, 0);
lv_obj_set_style_grid_cell_row_pos(obj, row_pos, 0);
lv_obj_set_style_grid_cell_x_align(obj, x_align, 0);
lv_obj_set_style_grid_cell_column_span(obj, col_span, 0);
lv_obj_set_style_grid_cell_row_span(obj, row_span, 0);
lv_obj_set_style_grid_cell_y_align(obj, y_align, 0);
lv_obj_mark_layout_as_dirty(lv_obj_get_parent(obj));
}
void lv_style_set_grid_row_dsc_array(lv_style_t * style, const lv_coord_t value[])
{
lv_style_value_t v = {
.ptr = (const void *)value
};
lv_style_set_prop(style, LV_STYLE_GRID_ROW_DSC_ARRAY, v);
}
void lv_style_set_grid_column_dsc_array(lv_style_t * style, const lv_coord_t value[])
{
lv_style_value_t v = {
.ptr = (const void *)value
};
lv_style_set_prop(style, LV_STYLE_GRID_COLUMN_DSC_ARRAY, v);
}
void lv_style_set_grid_row_align(lv_style_t * style, lv_grid_align_t value)
{
lv_style_value_t v = {
.num = (lv_grid_align_t)value
};
lv_style_set_prop(style, LV_STYLE_GRID_ROW_ALIGN, v);
}
void lv_style_set_grid_column_align(lv_style_t * style, lv_grid_align_t value)
{
lv_style_value_t v = {
.num = (lv_grid_align_t)value
};
lv_style_set_prop(style, LV_STYLE_GRID_COLUMN_ALIGN, v);
}
void lv_style_set_grid_cell_column_pos(lv_style_t * style, lv_coord_t value)
{
lv_style_value_t v = {
.num = value
};
lv_style_set_prop(style, LV_STYLE_GRID_CELL_COLUMN_POS, v);
}
void lv_style_set_grid_cell_column_span(lv_style_t * style, lv_coord_t value)
{
lv_style_value_t v = {
.num = value
};
lv_style_set_prop(style, LV_STYLE_GRID_CELL_COLUMN_SPAN, v);
}
void lv_style_set_grid_cell_row_pos(lv_style_t * style, lv_coord_t value)
{
lv_style_value_t v = {
.num = value
};
lv_style_set_prop(style, LV_STYLE_GRID_CELL_ROW_POS, v);
}
void lv_style_set_grid_cell_row_span(lv_style_t * style, lv_coord_t value)
{
lv_style_value_t v = {
.num = value
};
lv_style_set_prop(style, LV_STYLE_GRID_CELL_ROW_SPAN, v);
}
void lv_style_set_grid_cell_x_align(lv_style_t * style, lv_coord_t value)
{
lv_style_value_t v = {
.num = value
};
lv_style_set_prop(style, LV_STYLE_GRID_CELL_X_ALIGN, v);
}
void lv_style_set_grid_cell_y_align(lv_style_t * style, lv_coord_t value)
{
lv_style_value_t v = {
.num = value
};
lv_style_set_prop(style, LV_STYLE_GRID_CELL_Y_ALIGN, v);
}
void lv_obj_set_style_grid_row_dsc_array(lv_obj_t * obj, const lv_coord_t value[], lv_style_selector_t selector)
{
lv_style_value_t v = {
.ptr = (const void *)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_GRID_ROW_DSC_ARRAY, v, selector);
}
void lv_obj_set_style_grid_column_dsc_array(lv_obj_t * obj, const lv_coord_t value[], lv_style_selector_t selector)
{
lv_style_value_t v = {
.ptr = (const void *)value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_GRID_COLUMN_DSC_ARRAY, v, selector);
}
void lv_obj_set_style_grid_row_align(lv_obj_t * obj, lv_grid_align_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t) value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_GRID_ROW_ALIGN, v, selector);
}
void lv_obj_set_style_grid_column_align(lv_obj_t * obj, lv_grid_align_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = (int32_t) value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_GRID_COLUMN_ALIGN, v, selector);
}
void lv_obj_set_style_grid_cell_column_pos(lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_GRID_CELL_COLUMN_POS, v, selector);
}
void lv_obj_set_style_grid_cell_column_span(lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_GRID_CELL_COLUMN_SPAN, v, selector);
}
void lv_obj_set_style_grid_cell_row_pos(lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_GRID_CELL_ROW_POS, v, selector);
}
void lv_obj_set_style_grid_cell_row_span(lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_GRID_CELL_ROW_SPAN, v, selector);
}
void lv_obj_set_style_grid_cell_x_align(lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_GRID_CELL_X_ALIGN, v, selector);
}
void lv_obj_set_style_grid_cell_y_align(lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.num = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_GRID_CELL_Y_ALIGN, v, selector);
}
/**********************
* STATIC FUNCTIONS
**********************/
static void grid_update(lv_obj_t * cont, void * user_data)
{
LV_LOG_INFO("update %p container", (void *)cont);
LV_UNUSED(user_data);
const lv_coord_t * col_templ = get_col_dsc(cont);
const lv_coord_t * row_templ = get_row_dsc(cont);
if(col_templ == NULL || row_templ == NULL) return;
_lv_grid_calc_t c;
calc(cont, &c);
item_repos_hint_t hint;
lv_memset_00(&hint, sizeof(hint));
/*Calculate the grids absolute x and y coordinates.
*It will be used as helper during item repositioning to avoid calculating this value for every children*/
lv_coord_t border_widt = lv_obj_get_style_border_width(cont, LV_PART_MAIN);
lv_coord_t pad_left = lv_obj_get_style_pad_left(cont, LV_PART_MAIN) + border_widt;
lv_coord_t pad_top = lv_obj_get_style_pad_top(cont, LV_PART_MAIN) + border_widt;
hint.grid_abs.x = pad_left + cont->coords.x1 - lv_obj_get_scroll_x(cont);
hint.grid_abs.y = pad_top + cont->coords.y1 - lv_obj_get_scroll_y(cont);
uint32_t i;
for(i = 0; i < cont->spec_attr->child_cnt; i++) {
lv_obj_t * item = cont->spec_attr->children[i];
item_repos(item, &c, &hint);
}
calc_free(&c);
lv_coord_t w_set = lv_obj_get_style_width(cont, LV_PART_MAIN);
lv_coord_t h_set = lv_obj_get_style_height(cont, LV_PART_MAIN);
if(w_set == LV_SIZE_CONTENT || h_set == LV_SIZE_CONTENT) {
lv_obj_refr_size(cont);
}
lv_event_send(cont, LV_EVENT_LAYOUT_CHANGED, NULL);
LV_TRACE_LAYOUT("finished");
}
/**
* Calculate the grid cells coordinates
* @param cont an object that has a grid
* @param calc store the calculated cells sizes here
* @note `_lv_grid_calc_free(calc_out)` needs to be called when `calc_out` is not needed anymore
*/
static void calc(lv_obj_t * cont, _lv_grid_calc_t * calc_out)
{
if(lv_obj_get_child(cont, 0) == NULL) {
lv_memset_00(calc_out, sizeof(_lv_grid_calc_t));
return;
}
calc_rows(cont, calc_out);
calc_cols(cont, calc_out);
lv_coord_t col_gap = lv_obj_get_style_pad_column(cont, LV_PART_MAIN);
lv_coord_t row_gap = lv_obj_get_style_pad_row(cont, LV_PART_MAIN);
bool rev = lv_obj_get_style_base_dir(cont, LV_PART_MAIN) == LV_BASE_DIR_RTL ? true : false;
lv_coord_t w_set = lv_obj_get_style_width(cont, LV_PART_MAIN);
lv_coord_t h_set = lv_obj_get_style_height(cont, LV_PART_MAIN);
bool auto_w = (w_set == LV_SIZE_CONTENT && !cont->w_layout) ? true : false;
lv_coord_t cont_w = lv_obj_get_content_width(cont);
calc_out->grid_w = grid_align(cont_w, auto_w, get_grid_col_align(cont), col_gap, calc_out->col_num, calc_out->w,
calc_out->x, rev);
bool auto_h = (h_set == LV_SIZE_CONTENT && !cont->h_layout) ? true : false;
lv_coord_t cont_h = lv_obj_get_content_height(cont);
calc_out->grid_h = grid_align(cont_h, auto_h, get_grid_row_align(cont), row_gap, calc_out->row_num, calc_out->h,
calc_out->y, false);
LV_ASSERT_MEM_INTEGRITY();
}
/**
* Free the a grid calculation's data
* @param calc pointer to the calculated gtrid cell coordinates
*/
static void calc_free(_lv_grid_calc_t * calc)
{
lv_mem_buf_release(calc->x);
lv_mem_buf_release(calc->y);
lv_mem_buf_release(calc->w);
lv_mem_buf_release(calc->h);
}
static void calc_cols(lv_obj_t * cont, _lv_grid_calc_t * c)
{
const lv_coord_t * col_templ = get_col_dsc(cont);
lv_coord_t cont_w = lv_obj_get_content_width(cont);
c->col_num = count_tracks(col_templ);
c->x = lv_mem_buf_get(sizeof(lv_coord_t) * c->col_num);
c->w = lv_mem_buf_get(sizeof(lv_coord_t) * c->col_num);
/*Set sizes for CONTENT cells*/
uint32_t i;
for(i = 0; i < c->col_num; i++) {
lv_coord_t size = LV_COORD_MIN;
if(IS_CONTENT(col_templ[i])) {
/*Check the size of children of this cell*/
uint32_t ci;
for(ci = 0; ci < lv_obj_get_child_cnt(cont); ci++) {
lv_obj_t * item = lv_obj_get_child(cont, ci);
if(lv_obj_has_flag_any(item, LV_OBJ_FLAG_IGNORE_LAYOUT | LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_FLOATING)) continue;
uint32_t col_span = get_col_span(item);
if(col_span != 1) continue;
uint32_t col_pos = get_col_pos(item);
if(col_pos != i) continue;
size = LV_MAX(size, lv_obj_get_width(item));
}
if(size >= 0) c->w[i] = size;
else c->w[i] = 0;
}
}
uint32_t col_fr_cnt = 0;
lv_coord_t grid_w = 0;
for(i = 0; i < c->col_num; i++) {
lv_coord_t x = col_templ[i];
if(IS_FR(x)) {
col_fr_cnt += GET_FR(x);
}
else if(IS_CONTENT(x)) {
grid_w += c->w[i];
}
else {
c->w[i] = x;
grid_w += x;
}
}
lv_coord_t col_gap = lv_obj_get_style_pad_column(cont, LV_PART_MAIN);
cont_w -= col_gap * (c->col_num - 1);
lv_coord_t free_w = cont_w - grid_w;
if(free_w < 0) free_w = 0;
int32_t last_fr_i = -1;
int32_t last_fr_x = 0;
for(i = 0; i < c->col_num; i++) {
lv_coord_t x = col_templ[i];
if(IS_FR(x)) {
lv_coord_t f = GET_FR(x);
c->w[i] = (free_w * f) / col_fr_cnt;
last_fr_i = i;
last_fr_x = f;
}
}
/*To avoid rounding errors set the last FR track to the remaining size */
if(last_fr_i >= 0) {
c->w[last_fr_i] = free_w - ((free_w * (col_fr_cnt - last_fr_x)) / col_fr_cnt);
}
}
static void calc_rows(lv_obj_t * cont, _lv_grid_calc_t * c)
{
uint32_t i;
const lv_coord_t * row_templ = get_row_dsc(cont);
c->row_num = count_tracks(row_templ);
c->y = lv_mem_buf_get(sizeof(lv_coord_t) * c->row_num);
c->h = lv_mem_buf_get(sizeof(lv_coord_t) * c->row_num);
/*Set sizes for CONTENT cells*/
for(i = 0; i < c->row_num; i++) {
lv_coord_t size = LV_COORD_MIN;
if(IS_CONTENT(row_templ[i])) {
/*Check the size of children of this cell*/
uint32_t ci;
for(ci = 0; ci < lv_obj_get_child_cnt(cont); ci++) {
lv_obj_t * item = lv_obj_get_child(cont, ci);
if(lv_obj_has_flag_any(item, LV_OBJ_FLAG_IGNORE_LAYOUT | LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_FLOATING)) continue;
uint32_t row_span = get_row_span(item);
if(row_span != 1) continue;
uint32_t row_pos = get_row_pos(item);
if(row_pos != i) continue;
size = LV_MAX(size, lv_obj_get_height(item));
}
if(size >= 0) c->h[i] = size;
else c->h[i] = 0;
}
}
uint32_t row_fr_cnt = 0;
lv_coord_t grid_h = 0;
for(i = 0; i < c->row_num; i++) {
lv_coord_t x = row_templ[i];
if(IS_FR(x)) {
row_fr_cnt += GET_FR(x);
}
else if(IS_CONTENT(x)) {
grid_h += c->h[i];
}
else {
c->h[i] = x;
grid_h += x;
}
}
lv_coord_t row_gap = lv_obj_get_style_pad_row(cont, LV_PART_MAIN);
lv_coord_t cont_h = lv_obj_get_content_height(cont) - row_gap * (c->row_num - 1);
lv_coord_t free_h = cont_h - grid_h;
if(free_h < 0) free_h = 0;
int32_t last_fr_i = -1;
int32_t last_fr_x = 0;
for(i = 0; i < c->row_num; i++) {
lv_coord_t x = row_templ[i];
if(IS_FR(x)) {
lv_coord_t f = GET_FR(x);
c->h[i] = (free_h * f) / row_fr_cnt;
}
}
/*To avoid rounding errors set the last FR track to the remaining size */
if(last_fr_i >= 0) {
c->h[last_fr_i] = free_h - ((free_h * (row_fr_cnt - last_fr_x)) / row_fr_cnt);
}
}
/**
* Reposition a grid item in its cell
* @param item a grid item to reposition
* @param calc the calculated grid of `cont`
* @param child_id_ext helper value if the ID of the child is know (order from the oldest) else -1
* @param grid_abs helper value, the absolute position of the grid, NULL if unknown
*/
static void item_repos(lv_obj_t * item, _lv_grid_calc_t * c, item_repos_hint_t * hint)
{
if(lv_obj_has_flag_any(item, LV_OBJ_FLAG_IGNORE_LAYOUT | LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_FLOATING)) return;
uint32_t col_span = get_col_span(item);
uint32_t row_span = get_row_span(item);
if(row_span == 0 || col_span == 0) return;
uint32_t col_pos = get_col_pos(item);
uint32_t row_pos = get_row_pos(item);
lv_grid_align_t col_align = get_cell_col_align(item);
lv_grid_align_t row_align = get_cell_row_align(item);
lv_coord_t col_x1 = c->x[col_pos];
lv_coord_t col_x2 = c->x[col_pos + col_span - 1] + c->w[col_pos + col_span - 1];
lv_coord_t col_w = col_x2 - col_x1;
lv_coord_t row_y1 = c->y[row_pos];
lv_coord_t row_y2 = c->y[row_pos + row_span - 1] + c->h[row_pos + row_span - 1];
lv_coord_t row_h = row_y2 - row_y1;
/*If the item has RTL base dir switch start and end*/
if(lv_obj_get_style_base_dir(item, LV_PART_MAIN) == LV_BASE_DIR_RTL) {
if(col_align == LV_GRID_ALIGN_START) col_align = LV_GRID_ALIGN_END;
else if(col_align == LV_GRID_ALIGN_END) col_align = LV_GRID_ALIGN_START;
}
lv_coord_t x;
lv_coord_t y;
lv_coord_t item_w = lv_area_get_width(&item->coords);
lv_coord_t item_h = lv_area_get_height(&item->coords);
switch(col_align) {
default:
case LV_GRID_ALIGN_START:
x = c->x[col_pos];
item->w_layout = 0;
break;
case LV_GRID_ALIGN_STRETCH:
x = c->x[col_pos];
item_w = col_w;
item->w_layout = 1;
break;
case LV_GRID_ALIGN_CENTER:
x = c->x[col_pos] + (col_w - item_w) / 2;
item->w_layout = 0;
break;
case LV_GRID_ALIGN_END:
x = c->x[col_pos] + col_w - lv_obj_get_width(item);
item->w_layout = 0;
break;
}
switch(row_align) {
default:
case LV_GRID_ALIGN_START:
y = c->y[row_pos];
item->h_layout = 0;
break;
case LV_GRID_ALIGN_STRETCH:
y = c->y[row_pos];
item_h = row_h;
item->h_layout = 1;
break;
case LV_GRID_ALIGN_CENTER:
y = c->y[row_pos] + (row_h - item_h) / 2;
item->h_layout = 0;
break;
case LV_GRID_ALIGN_END:
y = c->y[row_pos] + row_h - lv_obj_get_height(item);
item->h_layout = 0;
break;
}
/*Set a new size if required*/
if(lv_obj_get_width(item) != item_w || lv_obj_get_height(item) != item_h) {
lv_area_t old_coords;
lv_area_copy(&old_coords, &item->coords);
lv_obj_invalidate(item);
lv_area_set_width(&item->coords, item_w);
lv_area_set_height(&item->coords, item_h);
lv_obj_invalidate(item);
lv_event_send(item, LV_EVENT_SIZE_CHANGED, &old_coords);
lv_event_send(lv_obj_get_parent(item), LV_EVENT_CHILD_CHANGED, item);
}
/*Handle percentage value of translate*/
lv_coord_t tr_x = lv_obj_get_style_translate_x(item, LV_PART_MAIN);
lv_coord_t tr_y = lv_obj_get_style_translate_y(item, LV_PART_MAIN);
lv_coord_t w = lv_obj_get_width(item);
lv_coord_t h = lv_obj_get_height(item);
if(LV_COORD_IS_PCT(tr_x)) tr_x = (w * LV_COORD_GET_PCT(tr_x)) / 100;
if(LV_COORD_IS_PCT(tr_y)) tr_y = (h * LV_COORD_GET_PCT(tr_y)) / 100;
x += tr_x;
y += tr_y;
lv_coord_t diff_x = hint->grid_abs.x + x - item->coords.x1;
lv_coord_t diff_y = hint->grid_abs.y + y - item->coords.y1;
if(diff_x || diff_y) {
lv_obj_invalidate(item);
item->coords.x1 += diff_x;
item->coords.x2 += diff_x;
item->coords.y1 += diff_y;
item->coords.y2 += diff_y;
lv_obj_invalidate(item);
lv_obj_move_children_by(item, diff_x, diff_y, true);
}
}
/**
* Place the grid track according to align methods. It keeps the track sizes but sets their position.
* It can process both columns or rows according to the passed parameters.
* @param cont_size size of the containers content area (width/height)
* @param auto_size true: the container has auto size in the current direction
* @param align align method
* @param gap grid gap
* @param track_num number of tracks
* @param size_array array with the track sizes
* @param pos_array write the positions of the tracks here
* @return the total size of the grid
*/
static lv_coord_t grid_align(lv_coord_t cont_size, bool auto_size, uint8_t align, lv_coord_t gap, uint32_t track_num,
lv_coord_t * size_array, lv_coord_t * pos_array, bool reverse)
{
lv_coord_t grid_size = 0;
uint32_t i;
if(auto_size) {
pos_array[0] = 0;
}
else {
/*With spaced alignment gap will be calculated from the remaining space*/
if(align == LV_GRID_ALIGN_SPACE_AROUND || align == LV_GRID_ALIGN_SPACE_BETWEEN || align == LV_GRID_ALIGN_SPACE_EVENLY) {
gap = 0;
if(track_num == 1) align = LV_GRID_ALIGN_CENTER;
}
/*Get the full grid size with gap*/
for(i = 0; i < track_num; i++) {
grid_size += size_array[i] + gap;
}
grid_size -= gap;
/*Calculate the position of the first item and set gap is necessary*/
switch(align) {
case LV_GRID_ALIGN_START:
pos_array[0] = 0;
break;
case LV_GRID_ALIGN_CENTER:
pos_array[0] = (cont_size - grid_size) / 2;
break;
case LV_GRID_ALIGN_END:
pos_array[0] = cont_size - grid_size;
break;
case LV_GRID_ALIGN_SPACE_BETWEEN:
pos_array[0] = 0;
gap = (lv_coord_t)(cont_size - grid_size) / (lv_coord_t)(track_num - 1);
break;
case LV_GRID_ALIGN_SPACE_AROUND:
gap = (lv_coord_t)(cont_size - grid_size) / (lv_coord_t)(track_num);
pos_array[0] = gap / 2;
break;
case LV_GRID_ALIGN_SPACE_EVENLY:
gap = (lv_coord_t)(cont_size - grid_size) / (lv_coord_t)(track_num + 1);
pos_array[0] = gap;
break;
}
}
/*Set the position of all tracks from the start position, gaps and track sizes*/
for(i = 0; i < track_num - 1; i++) {
pos_array[i + 1] = pos_array[i] + size_array[i] + gap;
}
lv_coord_t total_gird_size = pos_array[track_num - 1] + size_array[track_num - 1] - pos_array[0];
if(reverse) {
for(i = 0; i < track_num; i++) {
pos_array[i] = cont_size - pos_array[i] - size_array[i];
}
}
/*Return the full size of the grid*/
return total_gird_size;
}
static uint32_t count_tracks(const lv_coord_t * templ)
{
uint32_t i;
for(i = 0; templ[i] != LV_GRID_TEMPLATE_LAST; i++);
return i;
}
#endif /*LV_USE_GRID*/

View File

@@ -0,0 +1,194 @@
/**
* @file lv_grid.h
*
*/
#ifndef LV_GRID_H
#define LV_GRID_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../core/lv_obj.h"
#if LV_USE_GRID
/*********************
* DEFINES
*********************/
/**
* Can be used track size to make the track fill the free space.
* @param x how much space to take proportionally to other FR tracks
* @return a special track size
*/
#define LV_GRID_FR(x) (LV_COORD_MAX - 100 + x)
#define LV_GRID_CONTENT (LV_COORD_MAX - 101)
LV_EXPORT_CONST_INT(LV_GRID_CONTENT);
#define LV_GRID_TEMPLATE_LAST (LV_COORD_MAX)
LV_EXPORT_CONST_INT(LV_GRID_TEMPLATE_LAST);
/**********************
* TYPEDEFS
**********************/
/*Can't include lv_obj.h because it includes this header file*/
struct _lv_obj_t;
typedef enum {
LV_GRID_ALIGN_START,
LV_GRID_ALIGN_CENTER,
LV_GRID_ALIGN_END,
LV_GRID_ALIGN_STRETCH,
LV_GRID_ALIGN_SPACE_EVENLY,
LV_GRID_ALIGN_SPACE_AROUND,
LV_GRID_ALIGN_SPACE_BETWEEN,
} lv_grid_align_t;
/**********************
* GLOBAL VARIABLES
**********************/
extern uint32_t LV_LAYOUT_GRID;
extern lv_style_prop_t LV_STYLE_GRID_COLUMN_DSC_ARRAY;
extern lv_style_prop_t LV_STYLE_GRID_COLUMN_ALIGN;
extern lv_style_prop_t LV_STYLE_GRID_ROW_DSC_ARRAY;
extern lv_style_prop_t LV_STYLE_GRID_ROW_ALIGN;
extern lv_style_prop_t LV_STYLE_GRID_CELL_COLUMN_POS;
extern lv_style_prop_t LV_STYLE_GRID_CELL_COLUMN_SPAN;
extern lv_style_prop_t LV_STYLE_GRID_CELL_X_ALIGN;
extern lv_style_prop_t LV_STYLE_GRID_CELL_ROW_POS;
extern lv_style_prop_t LV_STYLE_GRID_CELL_ROW_SPAN;
extern lv_style_prop_t LV_STYLE_GRID_CELL_Y_ALIGN;
/**********************
* GLOBAL PROTOTYPES
**********************/
void lv_grid_init(void);
void lv_obj_set_grid_dsc_array(lv_obj_t * obj, const lv_coord_t col_dsc[], const lv_coord_t row_dsc[]);
void lv_obj_set_grid_align(lv_obj_t * obj, lv_grid_align_t column_align, lv_grid_align_t row_align);
/**
* Set the cell of an object. The object's parent needs to have grid layout, else nothing will happen
* @param obj pointer to an object
* @param column_align the vertical alignment in the cell. `LV_GRID_START/END/CENTER/STRETCH`
* @param col_pos column ID
* @param col_span number of columns to take (>= 1)
* @param row_align the horizontal alignment in the cell. `LV_GRID_START/END/CENTER/STRETCH`
* @param row_pos row ID
* @param row_span number of rows to take (>= 1)
*/
void lv_obj_set_grid_cell(lv_obj_t * obj, lv_grid_align_t column_align, uint8_t col_pos, uint8_t col_span,
lv_grid_align_t row_align, uint8_t row_pos, uint8_t row_span);
/**
* Just a wrapper to `LV_GRID_FR` for bindings.
*/
static inline lv_coord_t lv_grid_fr(uint8_t x)
{
return LV_GRID_FR(x);
}
void lv_style_set_grid_row_dsc_array(lv_style_t * style, const lv_coord_t value[]);
void lv_style_set_grid_column_dsc_array(lv_style_t * style, const lv_coord_t value[]);
void lv_style_set_grid_row_align(lv_style_t * style, lv_grid_align_t value);
void lv_style_set_grid_column_align(lv_style_t * style, lv_grid_align_t value);
void lv_style_set_grid_cell_column_pos(lv_style_t * style, lv_coord_t value);
void lv_style_set_grid_cell_column_span(lv_style_t * style, lv_coord_t value);
void lv_style_set_grid_cell_row_pos(lv_style_t * style, lv_coord_t value);
void lv_style_set_grid_cell_row_span(lv_style_t * style, lv_coord_t value);
void lv_style_set_grid_cell_x_align(lv_style_t * style, lv_coord_t value);
void lv_style_set_grid_cell_y_align(lv_style_t * style, lv_coord_t value);
void lv_obj_set_style_grid_row_dsc_array(lv_obj_t * obj, const lv_coord_t value[], lv_style_selector_t selector);
void lv_obj_set_style_grid_column_dsc_array(lv_obj_t * obj, const lv_coord_t value[], lv_style_selector_t selector);
void lv_obj_set_style_grid_row_align(lv_obj_t * obj, lv_grid_align_t value, lv_style_selector_t selector);
void lv_obj_set_style_grid_column_align(lv_obj_t * obj, lv_grid_align_t value, lv_style_selector_t selector);
void lv_obj_set_style_grid_cell_column_pos(lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_grid_cell_column_span(lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_grid_cell_row_pos(lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_grid_cell_row_span(lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_grid_cell_x_align(lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
void lv_obj_set_style_grid_cell_y_align(lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
static inline const lv_coord_t * lv_obj_get_style_grid_row_dsc_array(const lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_GRID_ROW_DSC_ARRAY);
return (const lv_coord_t *)v.ptr;
}
static inline const lv_coord_t * lv_obj_get_style_grid_column_dsc_array(const lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_GRID_COLUMN_DSC_ARRAY);
return (const lv_coord_t *)v.ptr;
}
static inline lv_grid_align_t lv_obj_get_style_grid_row_align(const lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_GRID_ROW_ALIGN);
return (lv_grid_align_t)v.num;
}
static inline lv_grid_align_t lv_obj_get_style_grid_column_align(const lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_GRID_COLUMN_ALIGN);
return (lv_grid_align_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_grid_cell_column_pos(const lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_GRID_CELL_COLUMN_POS);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_grid_cell_column_span(const lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_GRID_CELL_COLUMN_SPAN);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_grid_cell_row_pos(const lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_GRID_CELL_ROW_POS);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_grid_cell_row_span(const lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_GRID_CELL_ROW_SPAN);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_grid_cell_x_align(const lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_GRID_CELL_X_ALIGN);
return (lv_coord_t)v.num;
}
static inline lv_coord_t lv_obj_get_style_grid_cell_y_align(const lv_obj_t * obj, uint32_t part)
{
lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_GRID_CELL_Y_ALIGN);
return (lv_coord_t)v.num;
}
/**********************
* GLOBAL VARIABLES
**********************/
/**********************
* MACROS
**********************/
#endif /*LV_USE_GRID*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_GRID_H*/

View File

@@ -0,0 +1,44 @@
/**
* @file lv_layouts.h
*
*/
#ifndef LV_LAYOUTS_H
#define LV_LAYOUTS_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "flex/lv_flex.h"
#include "grid/lv_grid.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* MACROS
**********************/
#if LV_USE_LOG && LV_LOG_TRACE_LAYOUT
# define LV_TRACE_LAYOUT(...) LV_LOG_TRACE(__VA_ARGS__)
#else
# define LV_TRACE_LAYOUT(...)
#endif
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_LAYOUTS_H*/

View File

@@ -0,0 +1,228 @@
/**
* @file lv_bmp.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../../lvgl.h"
#if LV_USE_BMP
#include <string.h>
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_fs_file_t f;
unsigned int px_offset;
int px_width;
int px_height;
unsigned int bpp;
int row_size_bytes;
} bmp_dsc_t;
/**********************
* STATIC PROTOTYPES
**********************/
static lv_res_t decoder_info(lv_img_decoder_t * decoder, const void * src, lv_img_header_t * header);
static lv_res_t decoder_open(lv_img_decoder_t * dec, lv_img_decoder_dsc_t * dsc);
static lv_res_t decoder_read_line(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc,
lv_coord_t x, lv_coord_t y, lv_coord_t len, uint8_t * buf);
static void decoder_close(lv_img_decoder_t * dec, lv_img_decoder_dsc_t * dsc);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_bmp_init(void)
{
lv_img_decoder_t * dec = lv_img_decoder_create();
lv_img_decoder_set_info_cb(dec, decoder_info);
lv_img_decoder_set_open_cb(dec, decoder_open);
lv_img_decoder_set_read_line_cb(dec, decoder_read_line);
lv_img_decoder_set_close_cb(dec, decoder_close);
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Get info about a PNG image
* @param src can be file name or pointer to a C array
* @param header store the info here
* @return LV_RES_OK: no error; LV_RES_INV: can't get the info
*/
static lv_res_t decoder_info(lv_img_decoder_t * decoder, const void * src, lv_img_header_t * header)
{
LV_UNUSED(decoder);
lv_img_src_t src_type = lv_img_src_get_type(src); /*Get the source type*/
/*If it's a BMP file...*/
if(src_type == LV_IMG_SRC_FILE) {
const char * fn = src;
if(!strcmp(&fn[strlen(fn) - 3], "bmp")) { /*Check the extension*/
/*Save the data in the header*/
lv_fs_file_t f;
lv_fs_res_t res = lv_fs_open(&f, src, LV_FS_MODE_RD);
if(res != LV_FS_RES_OK) return LV_RES_INV;
uint8_t headers[54];
lv_fs_read(&f, headers, 54, NULL);
uint32_t w;
uint32_t h;
memcpy(&w, headers + 18, 4);
memcpy(&h, headers + 22, 4);
header->w = w;
header->h = h;
header->always_zero = 0;
lv_fs_close(&f);
#if LV_COLOR_DEPTH == 32
uint16_t bpp;
memcpy(&bpp, headers + 28, 2);
header->cf = bpp == 32 ? LV_IMG_CF_TRUE_COLOR_ALPHA : LV_IMG_CF_TRUE_COLOR;
#else
header->cf = LV_IMG_CF_TRUE_COLOR;
#endif
return LV_RES_OK;
}
}
/* BMP file as data not supported for simplicity.
* Convert them to LVGL compatible C arrays directly. */
else if(src_type == LV_IMG_SRC_VARIABLE) {
return LV_RES_INV;
}
return LV_RES_INV; /*If didn't succeeded earlier then it's an error*/
}
/**
* Open a PNG image and return the decided image
* @param src can be file name or pointer to a C array
* @param style style of the image object (unused now but certain formats might use it)
* @return pointer to the decoded image or `LV_IMG_DECODER_OPEN_FAIL` if failed
*/
static lv_res_t decoder_open(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc)
{
LV_UNUSED(decoder);
/*If it's a PNG file...*/
if(dsc->src_type == LV_IMG_SRC_FILE) {
const char * fn = dsc->src;
if(strcmp(&fn[strlen(fn) - 3], "bmp")) return LV_RES_INV; /*Check the extension*/
bmp_dsc_t b;
memset(&b, 0x00, sizeof(b));
lv_fs_res_t res = lv_fs_open(&b.f, dsc->src, LV_FS_MODE_RD);
if(res == LV_RES_OK) return LV_RES_INV;
uint8_t header[54];
lv_fs_read(&b.f, header, 54, NULL);
if (0x42 != header[0] || 0x4d != header[1]) {
return LV_RES_INV;
}
memcpy(&b.px_offset, header + 10, 4);
memcpy(&b.px_width, header + 18, 4);
memcpy(&b.px_height, header + 22, 4);
memcpy(&b.bpp, header + 28, 2);
b.row_size_bytes = ((b.bpp * b.px_width + 31) / 32) * 4;
dsc->user_data = lv_mem_alloc(sizeof(bmp_dsc_t));
LV_ASSERT_MALLOC(dsc->user_data);
if(dsc->user_data == NULL) return LV_RES_INV;
memcpy(dsc->user_data, &b, sizeof(b));
dsc->img_data = NULL;
return LV_RES_OK;
}
/* BMP file as data not supported for simplicity.
* Convert them to LVGL compatible C arrays directly. */
else if(dsc->src_type == LV_IMG_SRC_VARIABLE) {
return LV_RES_INV;
}
return LV_RES_INV; /*If not returned earlier then it failed*/
}
static lv_res_t decoder_read_line(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc,
lv_coord_t x, lv_coord_t y, lv_coord_t len, uint8_t * buf)
{
LV_UNUSED(decoder);
bmp_dsc_t * b = dsc->user_data;
y = (b->px_height - 1) - y; /*BMP images are stored upside down*/
uint32_t p = b->px_offset + b->row_size_bytes * y;
p += x * (b->bpp / 8);
lv_fs_seek(&b->f, p, LV_FS_SEEK_SET);
lv_fs_read(&b->f, buf, len * (b->bpp / 8), NULL);
#if LV_COLOR_DEPTH == 32
if(b->bpp == 32) {
lv_coord_t i;
for(i = 0; i < len; i++) {
uint8_t b0 = buf[i * 4];
uint8_t b1 = buf[i * 4 + 1];
uint8_t b2 = buf[i * 4 + 2];
uint8_t b3 = buf[i * 4 + 3];
lv_color32_t *c = (lv_color32_t*)&buf[i*4];
c->ch.red = b2;
c->ch.green = b1;
c->ch.blue = b0;
c->ch.alpha = b3;
}
}
if(b->bpp == 24) {
lv_coord_t i;
for(i = len - 1; i >= 0; i--) {
uint8_t * t = &buf[i * 3];
lv_color32_t *c = (lv_color32_t*)&buf[i*4];
c->ch.red = t[2];
c->ch.green = t[1];
c->ch.blue = t[0];
c->ch.alpha = 0xff;
}
}
#endif
return LV_RES_OK;
}
/**
* Free the allocated resources
*/
static void decoder_close(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc)
{
LV_UNUSED(decoder);
bmp_dsc_t * b = dsc->user_data;
lv_fs_close(&b->f);
lv_mem_free(dsc->user_data);
}
#endif /*LV_USE_BMP*/

View File

@@ -0,0 +1,42 @@
/**
* @file lv_templ.h
*
*/
#ifndef LV_BMP_H
#define LV_BMP_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../lv_conf_internal.h"
#if LV_USE_BMP
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
void lv_bmp_init(void);
/**********************
* MACROS
**********************/
#endif /*LV_USE_BMP*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_BMP_H*/

View File

@@ -0,0 +1,486 @@
/**
* @file lv_freetype.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_freetype.h"
#if LV_USE_FREETYPE
#include "ft2build.h"
#include FT_FREETYPE_H
#include FT_GLYPH_H
#include FT_CACHE_H
#include FT_SIZES_H
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct {
char * name;
} lv_face_info_t;
typedef struct {
lv_ll_t face_ll;
} lv_faces_control_t;
typedef struct {
#if LV_FREETYPE_CACHE_SIZE >= 0
void * face_id;
#else
FT_Size size;
#endif
lv_font_t * font;
uint16_t style;
uint16_t height;
} lv_font_fmt_ft_dsc_t;
/**********************
* STATIC PROTOTYPES
**********************/
#if LV_FREETYPE_CACHE_SIZE >= 0
static FT_Error font_face_requester(FTC_FaceID face_id,
FT_Library library_is, FT_Pointer req_data, FT_Face * aface);
static bool lv_ft_font_init_cache(lv_ft_info_t * info);
static void lv_ft_font_destroy_cache(lv_font_t * font);
static bool lv_ft_font_init_cache(lv_ft_info_t * info);
static void lv_ft_font_destroy_cache(lv_font_t * font);
#else
static FT_Face face_find_in_list(lv_ft_info_t * info);
static void face_add_to_list(FT_Face face);
static void face_remove_from_list(FT_Face face);
static void face_generic_finalizer(void * object);
static bool lv_ft_font_init_nocache(lv_ft_info_t * info);
static void lv_ft_font_destroy_nocache(lv_font_t * font);
#endif
/**********************
* STATIC VARIABLES
**********************/
static FT_Library library;
#if LV_FREETYPE_CACHE_SIZE >= 0
static FTC_Manager cache_manager;
static FTC_CMapCache cmap_cache;
static FTC_SBitCache sbit_cache;
static FTC_SBit sbit;
#else
static lv_faces_control_t face_control;
#endif
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
bool lv_freetype_init(uint16_t max_faces, uint16_t max_sizes, uint32_t max_bytes)
{
FT_Error error = FT_Init_FreeType(&library);
if(error) {
LV_LOG_ERROR("init freeType error(%d)", error);
return false;
}
#if LV_FREETYPE_CACHE_SIZE >= 0
error = FTC_Manager_New(library, max_faces, max_sizes,
max_bytes, font_face_requester, NULL, &cache_manager);
if(error) {
FT_Done_FreeType(library);
LV_LOG_ERROR("Failed to open cache manager");
return false;
}
error = FTC_CMapCache_New(cache_manager, &cmap_cache);
if(error) {
LV_LOG_ERROR("Failed to open Cmap Cache");
goto Fail;
}
error = FTC_SBitCache_New(cache_manager, &sbit_cache);
if(error) {
LV_LOG_ERROR("Failed to open sbit cache");
goto Fail;
}
return true;
Fail:
FTC_Manager_Done(cache_manager);
FT_Done_FreeType(library);
return false;
#else
LV_UNUSED(max_faces);
LV_UNUSED(max_sizes);
LV_UNUSED(max_bytes);
_lv_ll_init(&face_control.face_ll, sizeof(FT_Face *));
return true;
#endif/* LV_FREETYPE_CACHE_SIZE */
}
void lv_freetype_destroy(void)
{
#if LV_FREETYPE_CACHE_SIZE >= 0
FTC_Manager_Done(cache_manager);
#endif
FT_Done_FreeType(library);
}
bool lv_ft_font_init(lv_ft_info_t * info)
{
#if LV_FREETYPE_CACHE_SIZE >= 0
return lv_ft_font_init_cache(info);
#else
return lv_ft_font_init_nocache(info);
#endif
}
void lv_ft_font_destroy(lv_font_t * font)
{
#if LV_FREETYPE_CACHE_SIZE >= 0
lv_ft_font_destroy_cache(font);
#else
lv_ft_font_destroy_nocache(font);
#endif
}
/**********************
* STATIC FUNCTIONS
**********************/
#if LV_FREETYPE_CACHE_SIZE >= 0
static FT_Error font_face_requester(FTC_FaceID face_id,
FT_Library library_is, FT_Pointer req_data, FT_Face * aface)
{
LV_UNUSED(library_is);
LV_UNUSED(req_data);
lv_face_info_t * info = (lv_face_info_t *)face_id;
FT_Error error = FT_New_Face(library, info->name, 0, aface);
if(error) {
LV_LOG_ERROR("FT_New_Face error:%d\n", error);
return error;
}
return FT_Err_Ok;
}
static bool get_glyph_dsc_cb_cache(const lv_font_t * font,
lv_font_glyph_dsc_t * dsc_out, uint32_t unicode_letter, uint32_t unicode_letter_next)
{
LV_UNUSED(unicode_letter_next);
if(unicode_letter < 0x20) {
dsc_out->adv_w = 0;
dsc_out->box_h = 0;
dsc_out->box_w = 0;
dsc_out->ofs_x = 0;
dsc_out->ofs_y = 0;
dsc_out->bpp = 0;
return true;
}
lv_font_fmt_ft_dsc_t * dsc = (lv_font_fmt_ft_dsc_t *)(font->dsc);
FT_Face face;
FTC_ImageTypeRec desc_sbit_type;
FTC_FaceID face_id = (FTC_FaceID)dsc->face_id;
FTC_Manager_LookupFace(cache_manager, face_id, &face);
desc_sbit_type.face_id = face_id;
desc_sbit_type.flags = FT_LOAD_RENDER | FT_LOAD_TARGET_NORMAL;
desc_sbit_type.height = dsc->height;
desc_sbit_type.width = dsc->height;
FT_UInt charmap_index = FT_Get_Charmap_Index(face->charmap);
FT_UInt glyph_index = FTC_CMapCache_Lookup(cmap_cache, face_id, charmap_index, unicode_letter);
FT_Error error = FTC_SBitCache_Lookup(sbit_cache, &desc_sbit_type, glyph_index, &sbit, NULL);
if(error) {
LV_LOG_ERROR("SBitCache_Lookup error");
}
dsc_out->adv_w = sbit->xadvance;
dsc_out->box_h = sbit->height; /*Height of the bitmap in [px]*/
dsc_out->box_w = sbit->width; /*Width of the bitmap in [px]*/
dsc_out->ofs_x = sbit->left; /*X offset of the bitmap in [pf]*/
dsc_out->ofs_y = sbit->top - sbit->height; /*Y offset of the bitmap measured from the as line*/
dsc_out->bpp = 8; /*Bit per pixel: 1/2/4/8*/
return true;
}
static const uint8_t * get_glyph_bitmap_cb_cache(const lv_font_t * font, uint32_t unicode_letter)
{
LV_UNUSED(font);
LV_UNUSED(unicode_letter);
return (const uint8_t *)sbit->buffer;
}
static bool lv_ft_font_init_cache(lv_ft_info_t * info)
{
lv_font_fmt_ft_dsc_t * dsc = lv_mem_alloc(sizeof(lv_font_fmt_ft_dsc_t));
if(dsc == NULL) return false;
dsc->font = lv_mem_alloc(sizeof(lv_font_t));
if(dsc->font == NULL) {
lv_mem_free(dsc);
return false;
}
lv_face_info_t * face_info = NULL;
face_info = lv_mem_alloc(sizeof(lv_face_info_t) + strlen(info->name) + 1);
if(face_info == NULL) {
goto Fail;
}
face_info->name = ((char *)face_info) + sizeof(lv_face_info_t);
strcpy(face_info->name, info->name);
dsc->face_id = face_info;
dsc->height = info->weight;
dsc->style = info->style;
/* use to get font info */
FT_Size face_size;
struct FTC_ScalerRec_ scaler;
scaler.face_id = (FTC_FaceID)dsc->face_id;
scaler.width = info->weight;
scaler.height = info->weight;
scaler.pixel = 1;
FT_Error error = FTC_Manager_LookupSize(cache_manager, &scaler, &face_size);
if(error) {
lv_mem_free(face_info);
LV_LOG_ERROR("Failed to LookupSize");
goto Fail;
}
lv_font_t * font = dsc->font;
font->dsc = dsc;
font->get_glyph_dsc = get_glyph_dsc_cb_cache;
font->get_glyph_bitmap = get_glyph_bitmap_cb_cache;
font->subpx = LV_FONT_SUBPX_NONE;
font->line_height = (face_size->face->size->metrics.height >> 6);
font->base_line = -(face_size->face->size->metrics.descender >> 6);
FT_Fixed scale = face_size->face->size->metrics.y_scale;
int8_t thickness = FT_MulFix(scale, face_size->face->underline_thickness) >> 6;
font->underline_position = FT_MulFix(scale, face_size->face->underline_position) >> 6;
font->underline_thickness = thickness < 1 ? 1 : thickness;
/* return to user */
info->font = font;
return true;
Fail:
lv_mem_free(dsc->font);
lv_mem_free(dsc);
return false;
}
void lv_ft_font_destroy_cache(lv_font_t * font)
{
if(font == NULL) {
return;
}
lv_font_fmt_ft_dsc_t * dsc = (lv_font_fmt_ft_dsc_t *)(font->dsc);
if(dsc) {
lv_mem_free(dsc->face_id);
lv_mem_free(dsc->font);
lv_mem_free(dsc);
}
}
#else/* LV_FREETYPE_CACHE_SIZE */
static FT_Face face_find_in_list(lv_ft_info_t * info)
{
lv_face_info_t * face_info;
FT_Face * pface = _lv_ll_get_head(&face_control.face_ll);
while(pface) {
face_info = (lv_face_info_t *)(*pface)->generic.data;
if(strcmp(face_info->name, info->name) == 0) {
return *pface;
}
pface = _lv_ll_get_next(&face_control.face_ll, pface);
}
return NULL;
}
static void face_add_to_list(FT_Face face)
{
FT_Face * pface;
pface = (FT_Face *)_lv_ll_ins_tail(&face_control.face_ll);
*pface = face;
}
static void face_remove_from_list(FT_Face face)
{
FT_Face * pface = _lv_ll_get_head(&face_control.face_ll);
while(pface) {
if(*pface == face) {
_lv_ll_remove(&face_control.face_ll, pface);
lv_mem_free(pface);
break;
}
pface = _lv_ll_get_next(&face_control.face_ll, pface);
}
}
static void face_generic_finalizer(void * object)
{
FT_Face face = (FT_Face)object;
face_remove_from_list(face);
if(face->generic.data) {
lv_face_info_t * face_info = (lv_face_info_t *)face->generic.data;
lv_mem_free(face_info);
}
LV_LOG_INFO("face finalizer(%p)\n", face);
}
static bool get_glyph_dsc_cb_nocache(const lv_font_t * font,
lv_font_glyph_dsc_t * dsc_out, uint32_t unicode_letter, uint32_t unicode_letter_next)
{
LV_UNUSED(unicode_letter_next);
if(unicode_letter < 0x20) {
dsc_out->adv_w = 0;
dsc_out->box_h = 0;
dsc_out->box_w = 0;
dsc_out->ofs_x = 0;
dsc_out->ofs_y = 0;
dsc_out->bpp = 0;
return true;
}
FT_Error error;
lv_font_fmt_ft_dsc_t * dsc = (lv_font_fmt_ft_dsc_t *)(font->dsc);
FT_Face face = dsc->size->face;
FT_UInt glyph_index = FT_Get_Char_Index(face, unicode_letter);
if(face->size != dsc->size) {
FT_Activate_Size(dsc->size);
}
error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT);
if(error) {
return false;
}
error = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL);
if(error) {
return false;
}
dsc_out->adv_w = (face->glyph->metrics.horiAdvance >> 6);
dsc_out->box_h = face->glyph->bitmap.rows; /*Height of the bitmap in [px]*/
dsc_out->box_w = face->glyph->bitmap.width; /*Width of the bitmap in [px]*/
dsc_out->ofs_x = face->glyph->bitmap_left; /*X offset of the bitmap in [pf]*/
dsc_out->ofs_y = face->glyph->bitmap_top -
face->glyph->bitmap.rows; /*Y offset of the bitmap measured from the as line*/
dsc_out->bpp = 8; /*Bit per pixel: 1/2/4/8*/
return true;
}
static const uint8_t * get_glyph_bitmap_cb_nocache(const lv_font_t * font, uint32_t unicode_letter)
{
LV_UNUSED(unicode_letter);
lv_font_fmt_ft_dsc_t * dsc = (lv_font_fmt_ft_dsc_t *)(font->dsc);
FT_Face face = dsc->size->face;
return (const uint8_t *)(face->glyph->bitmap.buffer);
}
static bool lv_ft_font_init_nocache(lv_ft_info_t * info)
{
lv_font_fmt_ft_dsc_t * dsc = lv_mem_alloc(sizeof(lv_font_fmt_ft_dsc_t));
if(dsc == NULL) return false;
dsc->font = lv_mem_alloc(sizeof(lv_font_t));
if(dsc->font == NULL) {
lv_mem_free(dsc);
return false;
}
lv_face_info_t * face_info = NULL;
FT_Face face = face_find_in_list(info);
if(face == NULL) {
face_info = lv_mem_alloc(sizeof(lv_face_info_t) + strlen(info->name) + 1);
if(face_info == NULL) {
goto Fail;
}
FT_Error error = FT_New_Face(library, info->name, 0, &face);
if(error) {
lv_mem_free(face_info);
LV_LOG_WARN("create face error(%d)", error);
goto Fail;
}
/* link face and face info */
face_info->name = ((char *)face_info) + sizeof(lv_face_info_t);
strcpy(face_info->name, info->name);
face->generic.data = face_info;
face->generic.finalizer = face_generic_finalizer;
face_add_to_list(face);
}
else {
FT_Size size;
FT_Error error = FT_New_Size(face, &size);
if(error) {
goto Fail;
}
FT_Activate_Size(size);
FT_Reference_Face(face);
}
FT_Set_Pixel_Sizes(face, 0, info->weight);
dsc->size = face->size;
dsc->height = info->weight;
dsc->style = info->style;
lv_font_t * font = dsc->font;
font->dsc = dsc;
font->get_glyph_dsc = get_glyph_dsc_cb_nocache;
font->get_glyph_bitmap = get_glyph_bitmap_cb_nocache;
font->line_height = (face->size->metrics.height >> 6);
font->base_line = -(face->size->metrics.descender >> 6);
font->subpx = LV_FONT_SUBPX_NONE;
FT_Fixed scale = face->size->metrics.y_scale;
int8_t thickness = FT_MulFix(scale, face->underline_thickness) >> 6;
font->underline_position = FT_MulFix(scale, face->underline_position) >> 6;
font->underline_thickness = thickness < 1 ? 1 : thickness;
info->font = font;
return true;
Fail:
lv_mem_free(dsc->font);
lv_mem_free(dsc);
return false;
}
static void lv_ft_font_destroy_nocache(lv_font_t * font)
{
if(font == NULL) {
return;
}
lv_font_fmt_ft_dsc_t * dsc = (lv_font_fmt_ft_dsc_t *)(font->dsc);
if(dsc) {
FT_Face face = dsc->size->face;
FT_Done_Size(dsc->size);
FT_Done_Face(face);
lv_mem_free(dsc->font);
lv_mem_free(dsc);
}
}
#endif/* LV_FREETYPE_CACHE_SIZE */
#endif /*LV_USE_FREETYPE*/

View File

@@ -0,0 +1,81 @@
/**
* @file lv_freetype.h
*
*/
#ifndef LV_FREETYPE_H
#define LV_FREETYPE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../lvgl.h"
#if LV_USE_FREETYPE
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef enum {
FT_FONT_STYLE_NORMAL = 0,
FT_FONT_STYLE_ITALIC = 1 << 0,
FT_FONT_STYLE_BOLD = 1 << 1
} LV_FT_FONT_STYLE;
typedef struct {
const char * name; /* The name of the font file */
lv_font_t * font; /* point to lvgl font */
uint16_t weight; /* font size */
uint16_t style; /* font style */
} lv_ft_info_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* init freetype library
* @param max_faces Maximum number of opened FT_Face objects managed by this cache instance. Use 0 for defaults.
* @param max_sizes Maximum number of opened FT_Size objects managed by this cache instance. Use 0 for defaults.
* @param max_bytes Maximum number of bytes to use for cached data nodes. Use 0 for defaults.
* Note that this value does not account for managed FT_Face and FT_Size objects.
* @return true on success, otherwise false.
*/
bool lv_freetype_init(uint16_t max_faces, uint16_t max_sizes, uint32_t max_bytes);
/**
* Destroy freetype library
*/
void lv_freetype_destroy(void);
/**
* Creates a font with info parameter specified.
* @param info See lv_ft_info_t for details.
* when success, lv_ft_info_t->font point to the font you created.
* @return true on success, otherwise false.
*/
bool lv_ft_font_init(lv_ft_info_t * info);
/**
* Destroy a font that has been created.
* @param font pointer to font.
*/
void lv_ft_font_destroy(lv_font_t * font);
/**********************
* MACROS
**********************/
#endif /*LV_USE_FREETYPE*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* LV_FREETYPE_H */

View File

@@ -0,0 +1,276 @@
/**
* @file lv_fs_fatfs.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../../lvgl.h"
#if LV_USE_FS_FATFS != '\0'
#include "ff.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void fs_init(void);
static void * fs_open(lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode);
static lv_fs_res_t fs_close(lv_fs_drv_t * drv, void * file_p);
static lv_fs_res_t fs_read(lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br);
static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw);
static lv_fs_res_t fs_seek(lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs_whence_t whence);
static lv_fs_res_t fs_tell(lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p);
static void * fs_dir_open(lv_fs_drv_t * drv, const char * path);
static lv_fs_res_t fs_dir_read(lv_fs_drv_t * drv, void * dir_p, char * fn);
static lv_fs_res_t fs_dir_close(lv_fs_drv_t * drv, void * dir_p);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_fs_fatfs_init(void)
{
/*----------------------------------------------------
* Initialize your storage device and File System
* -------------------------------------------------*/
fs_init();
/*---------------------------------------------------
* Register the file system interface in LittlevGL
*--------------------------------------------------*/
/*Add a simple drive to open images*/
static lv_fs_drv_t fs_drv; /*A driver descriptor*/
lv_fs_drv_init(&fs_drv);
/*Set up fields...*/
fs_drv.letter = LV_USE_FS_FATFS;
fs_drv.open_cb = fs_open;
fs_drv.close_cb = fs_close;
fs_drv.read_cb = fs_read;
fs_drv.write_cb = fs_write;
fs_drv.seek_cb = fs_seek;
fs_drv.tell_cb = fs_tell;
fs_drv.dir_close_cb = fs_dir_close;
fs_drv.dir_open_cb = fs_dir_open;
fs_drv.dir_read_cb = fs_dir_read;
lv_fs_drv_register(&fs_drv);
}
/**********************
* STATIC FUNCTIONS
**********************/
/*Initialize your Storage device and File system.*/
static void fs_init(void)
{
/*Initialize the SD card and FatFS itself.
*Better to do it in your code to keep this library untouched for easy updating*/
}
/**
* Open a file
* @param drv pointer to a driver where this function belongs
* @param path path to the file beginning with the driver letter (e.g. S:/folder/file.txt)
* @param mode read: FS_MODE_RD, write: FS_MODE_WR, both: FS_MODE_RD | FS_MODE_WR
* @return pointer to FIL struct or NULL in case of fail
*/
static void * fs_open(lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode)
{
LV_UNUSED(drv);
uint8_t flags = 0;
if(mode == LV_FS_MODE_WR) flags = FA_WRITE | FA_OPEN_ALWAYS;
else if(mode == LV_FS_MODE_RD) flags = FA_READ;
else if(mode == (LV_FS_MODE_WR | LV_FS_MODE_RD)) flags = FA_READ | FA_WRITE | FA_OPEN_ALWAYS;
FIL * f = lv_mem_alloc(sizeof(FIL));
if(f == NULL) return NULL;
FRESULT res = f_open(f, path, flags);
if(res == FR_OK) {
return f;
} else {
lv_mem_free(f);
return NULL;
}
}
/**
* Close an opened file
* @param drv pointer to a driver where this function belongs
* @param file_p pointer to a FIL variable. (opened with fs_open)
* @return LV_FS_RES_OK: no error, the file is read
* any error from lv_fs_res_t enum
*/
static lv_fs_res_t fs_close(lv_fs_drv_t * drv, void * file_p)
{
LV_UNUSED(drv);
f_close(file_p);
lv_mem_free(file_p);
return LV_FS_RES_OK;
}
/**
* Read data from an opened file
* @param drv pointer to a driver where this function belongs
* @param file_p pointer to a FIL variable.
* @param buf pointer to a memory block where to store the read data
* @param btr number of Bytes To Read
* @param br the real number of read bytes (Byte Read)
* @return LV_FS_RES_OK: no error, the file is read
* any error from lv_fs_res_t enum
*/
static lv_fs_res_t fs_read(lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br)
{
LV_UNUSED(drv);
FRESULT res = f_read(file_p, buf, btr, (UINT *)br);
if(res == FR_OK) return LV_FS_RES_OK;
else return LV_FS_RES_UNKNOWN;
}
/**
* Write into a file
* @param drv pointer to a driver where this function belongs
* @param file_p pointer to a FIL variable
* @param buf pointer to a buffer with the bytes to write
* @param btr Bytes To Write
* @param br the number of real written bytes (Bytes Written). NULL if unused.
* @return LV_FS_RES_OK or any error from lv_fs_res_t enum
*/
static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw)
{
LV_UNUSED(drv);
FRESULT res = f_write(file_p, buf, btw, (UINT *)bw);
if(res == FR_OK) return LV_FS_RES_OK;
else return LV_FS_RES_UNKNOWN;
}
/**
* Set the read write pointer. Also expand the file size if necessary.
* @param drv pointer to a driver where this function belongs
* @param file_p pointer to a FIL variable. (opened with fs_open )
* @param pos the new position of read write pointer
* @param whence only LV_SEEK_SET is supported
* @return LV_FS_RES_OK: no error, the file is read
* any error from lv_fs_res_t enum
*/
static lv_fs_res_t fs_seek(lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs_whence_t whence)
{
LV_UNUSED(drv);
switch (whence) {
case LV_FS_SEEK_SET:
f_lseek(file_p, pos);
break;
case LV_FS_SEEK_CUR:
f_lseek(file_p, f_tell(file_p) + pos);
break;
case LV_FS_SEEK_END:
f_lseek(file_p, f_size(file_p) + pos);
break;
default:
break;
}
return LV_FS_RES_OK;
}
/**
* Give the position of the read write pointer
* @param drv pointer to a driver where this function belongs
* @param file_p pointer to a FIL variable.
* @param pos_p pointer to to store the result
* @return LV_FS_RES_OK: no error, the file is read
* any error from lv_fs_res_t enum
*/
static lv_fs_res_t fs_tell(lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p)
{
LV_UNUSED(drv);
*pos_p = f_tell(file_p);
return LV_FS_RES_OK;
}
/**
* Initialize a 'DIR' variable for directory reading
* @param drv pointer to a driver where this function belongs
* @param path path to a directory
* @return pointer to an initialized 'DIR' variable
*/
static void * fs_dir_open(lv_fs_drv_t * drv, const char * path)
{
LV_UNUSED(drv);
DIR * d = lv_mem_alloc(sizeof(DIR));
if(d == NULL) return NULL;
FRESULT res = f_opendir(d, path);
if(res != FR_OK) {
lv_mem_free(d);
d = NULL;
}
return d;
}
/**
* Read the next filename from a directory.
* The name of the directories will begin with '/'
* @param drv pointer to a driver where this function belongs
* @param dir_p pointer to an initialized 'DIR' variable
* @param fn pointer to a buffer to store the filename
* @return LV_FS_RES_OK or any error from lv_fs_res_t enum
*/
static lv_fs_res_t fs_dir_read(lv_fs_drv_t * drv, void * dir_p, char * fn)
{
LV_UNUSED(drv);
FRESULT res;
FILINFO fno;
fn[0] = '\0';
do {
res = f_readdir(dir_p, &fno);
if(res != FR_OK) return LV_FS_RES_UNKNOWN;
if(fno.fattrib & AM_DIR) {
fn[0] = '/';
strcpy(&fn[1], fno.fname);
}
else strcpy(fn, fno.fname);
} while(strcmp(fn, "/.") == 0 || strcmp(fn, "/..") == 0);
return LV_FS_RES_OK;
}
/**
* Close the directory reading
* @param drv pointer to a driver where this function belongs
* @param dir_p pointer to an initialized 'DIR' variable
* @return LV_FS_RES_OK or any error from lv_fs_res_t enum
*/
static lv_fs_res_t fs_dir_close(lv_fs_drv_t * drv, void * dir_p)
{
LV_UNUSED(drv);
f_closedir(dir_p);
lv_mem_free(dir_p);
return LV_FS_RES_OK;
}
#endif /*LV_USE_FS_FATFS*/

View File

@@ -0,0 +1,313 @@
/**
* @file lv_fs_posix.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../../lvgl.h"
#if LV_USE_FS_POSIX != '\0'
#include <fcntl.h>
#include <stdio.h>
#ifndef WIN32
#include <dirent.h>
#include <unistd.h>
#else
#include <windows.h>
#endif
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void * fs_open(lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode);
static lv_fs_res_t fs_close(lv_fs_drv_t * drv, void * file_p);
static lv_fs_res_t fs_read(lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br);
static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw);
static lv_fs_res_t fs_seek(lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs_whence_t whence);
static lv_fs_res_t fs_tell(lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p);
static void * fs_dir_open(lv_fs_drv_t * drv, const char * path);
static lv_fs_res_t fs_dir_read(lv_fs_drv_t * drv, void * dir_p, char * fn);
static lv_fs_res_t fs_dir_close(lv_fs_drv_t * drv, void * dir_p);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Register a driver for the File system interface
*/
void lv_fs_posix_init(void)
{
/*---------------------------------------------------
* Register the file system interface in LittlevGL
*--------------------------------------------------*/
/*Add a simple drive to open images*/
static lv_fs_drv_t fs_drv; /*A driver descriptor*/
lv_fs_drv_init(&fs_drv);
/*Set up fields...*/
fs_drv.letter = LV_USE_FS_POSIX;
fs_drv.open_cb = fs_open;
fs_drv.close_cb = fs_close;
fs_drv.read_cb = fs_read;
fs_drv.write_cb = fs_write;
fs_drv.seek_cb = fs_seek;
fs_drv.tell_cb = fs_tell;
fs_drv.dir_close_cb = fs_dir_close;
fs_drv.dir_open_cb = fs_dir_open;
fs_drv.dir_read_cb = fs_dir_read;
lv_fs_drv_register(&fs_drv);
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Open a file
* @param drv pointer to a driver where this function belongs
* @param path path to the file beginning with the driver letter (e.g. S:/folder/file.txt)
* @param mode read: FS_MODE_RD, write: FS_MODE_WR, both: FS_MODE_RD | FS_MODE_WR
* @return a file handle or -1 in case of fail
*/
static void * fs_open(lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode)
{
LV_UNUSED(drv);
uint32_t flags = 0;
if(mode == LV_FS_MODE_WR) flags = O_WRONLY;
else if(mode == LV_FS_MODE_RD) flags = O_RDONLY;
else if(mode == (LV_FS_MODE_WR | LV_FS_MODE_RD)) flags = O_RDWR;
#ifdef LV_FS_POSIX_PATH
/*Make the path relative to the current directory (the projects root folder)*/
char buf[256];
sprintf(buf, LV_FS_POSIX_PATH "%s", path);
int f = open(buf, flags);
#else
int f = open(path, flags);
#endif
if(f < 0) return NULL;
return (void *)(lv_uintptr_t)f;
}
/**
* Close an opened file
* @param drv pointer to a driver where this function belongs
* @param file_p a file handle. (opened with fs_open)
* @return LV_FS_RES_OK: no error, the file is read
* any error from lv_fs_res_t enum
*/
static lv_fs_res_t fs_close(lv_fs_drv_t * drv, void * file_p)
{
LV_UNUSED(drv);
close((lv_uintptr_t)file_p);
return LV_FS_RES_OK;
}
/**
* Read data from an opened file
* @param drv pointer to a driver where this function belongs
* @param file_p a file handle variable.
* @param buf pointer to a memory block where to store the read data
* @param btr number of Bytes To Read
* @param br the real number of read bytes (Byte Read)
* @return LV_FS_RES_OK: no error, the file is read
* any error from lv_fs_res_t enum
*/
static lv_fs_res_t fs_read(lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br)
{
LV_UNUSED(drv);
*br = read((lv_uintptr_t)file_p, buf, btr);
return (int32_t)*br < 0 ? LV_FS_RES_UNKNOWN : LV_FS_RES_OK;
}
/**
* Write into a file
* @param drv pointer to a driver where this function belongs
* @param file_p a file handle variable
* @param buf pointer to a buffer with the bytes to write
* @param btr Bytes To Write
* @param br the number of real written bytes (Bytes Written). NULL if unused.
* @return LV_FS_RES_OK or any error from lv_fs_res_t enum
*/
static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw)
{
LV_UNUSED(drv);
*bw = write((lv_uintptr_t)file_p, buf, btw);
return (int32_t)*bw < 0 ? LV_FS_RES_UNKNOWN : LV_FS_RES_OK;
}
/**
* Set the read write pointer. Also expand the file size if necessary.
* @param drv pointer to a driver where this function belongs
* @param file_p a file handle variable. (opened with fs_open )
* @param pos the new position of read write pointer
* @return LV_FS_RES_OK: no error, the file is read
* any error from lv_fs_res_t enum
*/
static lv_fs_res_t fs_seek(lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs_whence_t whence)
{
LV_UNUSED(drv);
lseek((lv_uintptr_t)file_p, pos, whence);
return LV_FS_RES_OK;
}
/**
* Give the position of the read write pointer
* @param drv pointer to a driver where this function belongs
* @param file_p a file handle variable.
* @param pos_p pointer to to store the result
* @return LV_FS_RES_OK: no error, the file is read
* any error from lv_fs_res_t enum
*/
static lv_fs_res_t fs_tell(lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p)
{
LV_UNUSED(drv);
*pos_p = lseek((lv_uintptr_t)file_p, 0, SEEK_CUR);
return LV_FS_RES_OK;
}
#ifdef WIN32
static char next_fn[256];
#endif
/**
* Initialize a 'fs_read_dir_t' variable for directory reading
* @param drv pointer to a driver where this function belongs
* @param path path to a directory
* @return pointer to an initialized 'DIR' or 'HANDLE' variable
*/
static void * fs_dir_open(lv_fs_drv_t * drv, const char * path)
{
LV_UNUSED(drv);
#ifndef WIN32
# ifdef LV_FS_POSIX_PATH
/*Make the path relative to the current directory (the projects root folder)*/
char buf[256];
sprintf(buf, LV_FS_POSIX_PATH "%s", path);
return opendir(buf);
# else
return opendir(path);
# endif
#else
HANDLE d = INVALID_HANDLE_VALUE;
WIN32_FIND_DATA fdata;
/*Make the path relative to the current directory (the projects root folder)*/
char buf[256];
# ifdef LV_FS_POSIX_PATH
sprintf(buf, LV_FS_POSIX_PATH "%s\\*", path);
# else
sprintf(buf, "%s\\*", path);
# endif
strcpy(next_fn, "");
d = FindFirstFile(buf, &fdata);
do {
if (strcmp(fdata.cFileName, ".") == 0 || strcmp(fdata.cFileName, "..") == 0) {
continue;
} else {
if (fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
sprintf(next_fn, "/%s", fdata.cFileName);
} else {
sprintf(next_fn, "%s", fdata.cFileName);
}
break;
}
} while(FindNextFileA(d, &fdata));
return d;
#endif
}
/**
* Read the next filename from a directory.
* The name of the directories will begin with '/'
* @param drv pointer to a driver where this function belongs
* @param dir_p pointer to an initialized 'DIR' or 'HANDLE' variable
* @param fn pointer to a buffer to store the filename
* @return LV_FS_RES_OK or any error from lv_fs_res_t enum
*/
static lv_fs_res_t fs_dir_read(lv_fs_drv_t * drv, void * dir_p, char * fn)
{
LV_UNUSED(drv);
#ifndef WIN32
struct dirent *entry;
do {
entry = readdir(dir_p);
if(entry) {
if(entry->d_type == DT_DIR) sprintf(fn, "/%s", entry->d_name);
else strcpy(fn, entry->d_name);
} else {
strcpy(fn, "");
}
} while(strcmp(fn, "/.") == 0 || strcmp(fn, "/..") == 0);
#else
strcpy(fn, next_fn);
strcpy(next_fn, "");
WIN32_FIND_DATA fdata;
if(FindNextFile(dir_p, &fdata) == false) return LV_FS_RES_OK;
do {
if (strcmp(fdata.cFileName, ".") == 0 || strcmp(fdata.cFileName, "..") == 0) {
continue;
} else {
if (fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
sprintf(next_fn, "/%s", fdata.cFileName);
} else {
sprintf(next_fn, "%s", fdata.cFileName);
}
break;
}
} while(FindNextFile(dir_p, &fdata));
#endif
return LV_FS_RES_OK;
}
/**
* Close the directory reading
* @param drv pointer to a driver where this function belongs
* @param dir_p pointer to an initialized 'DIR' or 'HANDLE' variable
* @return LV_FS_RES_OK or any error from lv_fs_res_t enum
*/
static lv_fs_res_t fs_dir_close(lv_fs_drv_t * drv, void * dir_p)
{
LV_UNUSED(drv);
#ifndef WIN32
closedir(dir_p);
#else
FindClose(dir_p);
#endif
return LV_FS_RES_OK;
}
#endif /*LV_USE_FS_POSIX*/

View File

@@ -0,0 +1,310 @@
/**
* @file lv_fs_stdio.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../../lvgl.h"
#if LV_USE_FS_STDIO != '\0'
#include <stdio.h>
#ifndef WIN32
#include <dirent.h>
#include <unistd.h>
#else
#include <windows.h>
#endif
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void * fs_open(lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode);
static lv_fs_res_t fs_close(lv_fs_drv_t * drv, void * file_p);
static lv_fs_res_t fs_read(lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br);
static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw);
static lv_fs_res_t fs_seek(lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs_whence_t whence);
static lv_fs_res_t fs_tell(lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p);
static void * fs_dir_open(lv_fs_drv_t * drv, const char * path);
static lv_fs_res_t fs_dir_read(lv_fs_drv_t * drv, void * dir_p, char * fn);
static lv_fs_res_t fs_dir_close(lv_fs_drv_t * drv, void * dir_p);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Register a driver for the File system interface
*/
void lv_fs_stdio_init(void)
{
/*---------------------------------------------------
* Register the file system interface in LittlevGL
*--------------------------------------------------*/
/*Add a simple drive to open images*/
static lv_fs_drv_t fs_drv; /*A driver descriptor*/
lv_fs_drv_init(&fs_drv);
/*Set up fields...*/
fs_drv.letter = LV_USE_FS_STDIO;
fs_drv.open_cb = fs_open;
fs_drv.close_cb = fs_close;
fs_drv.read_cb = fs_read;
fs_drv.write_cb = fs_write;
fs_drv.seek_cb = fs_seek;
fs_drv.tell_cb = fs_tell;
fs_drv.dir_close_cb = fs_dir_close;
fs_drv.dir_open_cb = fs_dir_open;
fs_drv.dir_read_cb = fs_dir_read;
lv_fs_drv_register(&fs_drv);
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Open a file
* @param drv pointer to a driver where this function belongs
* @param path path to the file beginning with the driver letter (e.g. S:/folder/file.txt)
* @param mode read: FS_MODE_RD, write: FS_MODE_WR, both: FS_MODE_RD | FS_MODE_WR
* @return pointer to FIL struct or NULL in case of fail
*/
static void * fs_open(lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode)
{
LV_UNUSED(drv);
const char * flags = "";
if(mode == LV_FS_MODE_WR) flags = "wb";
else if(mode == LV_FS_MODE_RD) flags = "rb";
else if(mode == (LV_FS_MODE_WR | LV_FS_MODE_RD)) flags = "rb+";
#ifdef LV_FS_STDIO_PATH
/*Make the path relative to the current directory (the projects root folder)*/
char buf[256];
sprintf(buf, LV_FS_STDIO_PATH "%s", path);
return fopen(buf, flags);
#else
return fopen(path, flags);
#endif
}
/**
* Close an opened file
* @param drv pointer to a driver where this function belongs
* @param file_p pointer to a FILE variable. (opened with fs_open)
* @return LV_FS_RES_OK: no error, the file is read
* any error from lv_fs_res_t enum
*/
static lv_fs_res_t fs_close(lv_fs_drv_t * drv, void * file_p)
{
LV_UNUSED(drv);
fclose(file_p);
return LV_FS_RES_OK;
}
/**
* Read data from an opened file
* @param drv pointer to a driver where this function belongs
* @param file_p pointer to a FILE variable.
* @param buf pointer to a memory block where to store the read data
* @param btr number of Bytes To Read
* @param br the real number of read bytes (Byte Read)
* @return LV_FS_RES_OK: no error, the file is read
* any error from lv_fs_res_t enum
*/
static lv_fs_res_t fs_read(lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br)
{
LV_UNUSED(drv);
*br = fread(buf, 1, btr, file_p);
return (int32_t)*br < 0 ? LV_FS_RES_UNKNOWN : LV_FS_RES_OK;
}
/**
* Write into a file
* @param drv pointer to a driver where this function belongs
* @param file_p pointer to a FILE variable
* @param buf pointer to a buffer with the bytes to write
* @param btr Bytes To Write
* @param br the number of real written bytes (Bytes Written). NULL if unused.
* @return LV_FS_RES_OK or any error from lv_fs_res_t enum
*/
static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw)
{
LV_UNUSED(drv);
*bw = fwrite(buf, 1, btw, file_p);
return (int32_t)*bw < 0 ? LV_FS_RES_UNKNOWN : LV_FS_RES_OK;
}
/**
* Set the read write pointer. Also expand the file size if necessary.
* @param drv pointer to a driver where this function belongs
* @param file_p pointer to a FILE variable. (opened with fs_open )
* @param pos the new position of read write pointer
* @return LV_FS_RES_OK: no error, the file is read
* any error from lv_fs_res_t enum
*/
static lv_fs_res_t fs_seek(lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs_whence_t whence)
{
LV_UNUSED(drv);
fseek(file_p, pos, whence);
return LV_FS_RES_OK;
}
/**
* Give the position of the read write pointer
* @param drv pointer to a driver where this function belongs
* @param file_p pointer to a FILE variable.
* @param pos_p pointer to to store the result
* @return LV_FS_RES_OK: no error, the file is read
* any error from lv_fs_res_t enum
*/
static lv_fs_res_t fs_tell(lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p)
{
LV_UNUSED(drv);
*pos_p = ftell(file_p);
return LV_FS_RES_OK;
}
#ifdef WIN32
static char next_fn[256];
#endif
/**
* Initialize a 'DIR' or 'HANDLE' variable for directory reading
* @param drv pointer to a driver where this function belongs
* @param path path to a directory
* @return pointer to an initialized 'DIR' or 'HANDLE' variable
*/
static void * fs_dir_open(lv_fs_drv_t * drv, const char * path)
{
LV_UNUSED(drv);
#ifndef WIN32
# ifdef LV_FS_STDIO_PATH
/*Make the path relative to the current directory (the projects root folder)*/
char buf[256];
sprintf(buf, LV_FS_STDIO_PATH "%s", path);
return opendir(buf);
# else
return opendir(path);
# endif
#else
HANDLE d = INVALID_HANDLE_VALUE;
WIN32_FIND_DATA fdata;
/*Make the path relative to the current directory (the projects root folder)*/
char buf[256];
# ifdef LV_FS_STDIO_PATH
sprintf(buf, LV_FS_STDIO_PATH "%s\\*", path);
# else
sprintf(buf, "%s\\*", path);
# endif
strcpy(next_fn, "");
d = FindFirstFile(buf, &fdata);
do {
if (strcmp(fdata.cFileName, ".") == 0 || strcmp(fdata.cFileName, "..") == 0) {
continue;
} else {
if (fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
sprintf(next_fn, "/%s", fdata.cFileName);
} else {
sprintf(next_fn, "%s", fdata.cFileName);
}
break;
}
} while(FindNextFileA(d, &fdata));
return d;
#endif
}
/**
* Read the next filename form a directory.
* The name of the directories will begin with '/'
* @param drv pointer to a driver where this function belongs
* @param dir_p pointer to an initialized 'DIR' or 'HANDLE' variable
* @param fn pointer to a buffer to store the filename
* @return LV_FS_RES_OK or any error from lv_fs_res_t enum
*/
static lv_fs_res_t fs_dir_read(lv_fs_drv_t * drv, void * dir_p, char * fn)
{
LV_UNUSED(drv);
#ifndef WIN32
struct dirent *entry;
do {
entry = readdir(dir_p);
if(entry) {
if(entry->d_type == DT_DIR) sprintf(fn, "/%s", entry->d_name);
else strcpy(fn, entry->d_name);
} else {
strcpy(fn, "");
}
} while(strcmp(fn, "/.") == 0 || strcmp(fn, "/..") == 0);
#else
strcpy(fn, next_fn);
strcpy(next_fn, "");
WIN32_FIND_DATA fdata;
if(FindNextFile(dir_p, &fdata) == false) return LV_FS_RES_OK;
do {
if (strcmp(fdata.cFileName, ".") == 0 || strcmp(fdata.cFileName, "..") == 0) {
continue;
} else {
if (fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
sprintf(next_fn, "/%s", fdata.cFileName);
} else {
sprintf(next_fn, "%s", fdata.cFileName);
}
break;
}
} while(FindNextFile(dir_p, &fdata));
#endif
return LV_FS_RES_OK;
}
/**
* Close the directory reading
* @param drv pointer to a driver where this function belongs
* @param dir_p pointer to an initialized 'DIR' or 'HANDLE' variable
* @return LV_FS_RES_OK or any error from lv_fs_res_t enum
*/
static lv_fs_res_t fs_dir_close(lv_fs_drv_t * drv, void * dir_p)
{
LV_UNUSED(drv);
#ifndef WIN32
closedir(dir_p);
#else
FindClose(dir_p);
#endif
return LV_FS_RES_OK;
}
#endif /*LV_USE_FS_STDIO*/

View File

@@ -0,0 +1,451 @@
/**
* @file lv_fs_win32.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../../lvgl.h"
#if LV_USE_FS_WIN32 != '\0'
#include <windows.h>
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static bool is_dots_name(const char * name);
static lv_fs_res_t fs_error_from_win32(DWORD error);
static void * fs_open(lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode);
static lv_fs_res_t fs_close(lv_fs_drv_t * drv, void * file_p);
static lv_fs_res_t fs_read(lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br);
static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw);
static lv_fs_res_t fs_seek(lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs_whence_t whence);
static lv_fs_res_t fs_tell(lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p);
static void * fs_dir_open(lv_fs_drv_t * drv, const char * path);
static lv_fs_res_t fs_dir_read(lv_fs_drv_t * drv, void * dir_p, char * fn);
static lv_fs_res_t fs_dir_close(lv_fs_drv_t * drv, void * dir_p);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Register a driver for the File system interface
*/
void lv_fs_win32_init(void)
{
/*---------------------------------------------------
* Register the file system interface in LittlevGL
*--------------------------------------------------*/
/*Add a simple drive to open images*/
static lv_fs_drv_t fs_drv; /*A driver descriptor*/
lv_fs_drv_init(&fs_drv);
/*Set up fields...*/
fs_drv.letter = LV_USE_FS_WIN32;
fs_drv.open_cb = fs_open;
fs_drv.close_cb = fs_close;
fs_drv.read_cb = fs_read;
fs_drv.write_cb = fs_write;
fs_drv.seek_cb = fs_seek;
fs_drv.tell_cb = fs_tell;
fs_drv.dir_close_cb = fs_dir_close;
fs_drv.dir_open_cb = fs_dir_open;
fs_drv.dir_read_cb = fs_dir_read;
lv_fs_drv_register(&fs_drv);
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Check the dots name
* @param name file or dir name
* @return true if the name is dots name
*/
static bool is_dots_name(const char * name)
{
return name[0] == '.' && (!name[1] || (name[1] == '.' && !name[2]));
}
/**
* Convert Win32 error code to error from lv_fs_res_t enum
* @param error Win32 error code
* @return LV_FS_RES_OK: no error, the file is read
* any error from lv_fs_res_t enum
*/
static lv_fs_res_t fs_error_from_win32(DWORD error)
{
lv_fs_res_t res;
switch (error) {
case ERROR_SUCCESS:
res = LV_FS_RES_OK;
break;
case ERROR_BAD_UNIT:
case ERROR_NOT_READY:
case ERROR_CRC:
case ERROR_SEEK:
case ERROR_NOT_DOS_DISK:
case ERROR_WRITE_FAULT:
case ERROR_READ_FAULT:
case ERROR_GEN_FAILURE:
case ERROR_WRONG_DISK:
res = LV_FS_RES_HW_ERR;
break;
case ERROR_INVALID_HANDLE:
case ERROR_INVALID_TARGET_HANDLE:
res = LV_FS_RES_FS_ERR;
break;
case ERROR_FILE_NOT_FOUND:
case ERROR_PATH_NOT_FOUND:
case ERROR_INVALID_DRIVE:
case ERROR_NO_MORE_FILES:
case ERROR_SECTOR_NOT_FOUND:
case ERROR_BAD_NETPATH:
case ERROR_BAD_NET_NAME:
case ERROR_BAD_PATHNAME:
case ERROR_FILENAME_EXCED_RANGE:
res = LV_FS_RES_NOT_EX;
break;
case ERROR_DISK_FULL:
res = LV_FS_RES_FULL;
break;
case ERROR_SHARING_VIOLATION:
case ERROR_LOCK_VIOLATION:
case ERROR_DRIVE_LOCKED:
res = LV_FS_RES_LOCKED;
break;
case ERROR_ACCESS_DENIED:
case ERROR_CURRENT_DIRECTORY:
case ERROR_WRITE_PROTECT:
case ERROR_NETWORK_ACCESS_DENIED:
case ERROR_CANNOT_MAKE:
case ERROR_FAIL_I24:
case ERROR_SEEK_ON_DEVICE:
case ERROR_NOT_LOCKED:
case ERROR_LOCK_FAILED:
res = LV_FS_RES_DENIED;
break;
case ERROR_BUSY:
res = LV_FS_RES_BUSY;
break;
case ERROR_TIMEOUT:
res = LV_FS_RES_TOUT;
break;
case ERROR_NOT_SAME_DEVICE:
case ERROR_DIRECT_ACCESS_HANDLE:
res = LV_FS_RES_NOT_IMP;
break;
case ERROR_TOO_MANY_OPEN_FILES:
case ERROR_ARENA_TRASHED:
case ERROR_NOT_ENOUGH_MEMORY:
case ERROR_INVALID_BLOCK:
case ERROR_OUT_OF_PAPER:
case ERROR_SHARING_BUFFER_EXCEEDED:
case ERROR_NOT_ENOUGH_QUOTA:
res = LV_FS_RES_OUT_OF_MEM;
break;
case ERROR_INVALID_FUNCTION:
case ERROR_INVALID_ACCESS:
case ERROR_INVALID_DATA:
case ERROR_BAD_COMMAND:
case ERROR_BAD_LENGTH:
case ERROR_INVALID_PARAMETER:
case ERROR_NEGATIVE_SEEK:
res = LV_FS_RES_INV_PARAM;
break;
default:
res = LV_FS_RES_UNKNOWN;
break;
}
return res;
}
/**
* Open a file
* @param drv pointer to a driver where this function belongs
* @param path path to the file beginning with the driver letter (e.g. S:/folder/file.txt)
* @param mode read: FS_MODE_RD, write: FS_MODE_WR, both: FS_MODE_RD | FS_MODE_WR
* @return pointer to FIL struct or NULL in case of fail
*/
static void * fs_open(lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode)
{
LV_UNUSED(drv);
DWORD desired_access = 0;
if (mode & LV_FS_MODE_RD) {
desired_access |= GENERIC_READ;
}
if (mode & LV_FS_MODE_WR) {
desired_access |= GENERIC_WRITE;
}
#ifdef LV_FS_WIN32_PATH
/*Make the path relative to the current directory (the projects root folder)*/
char buf[MAX_PATH];
sprintf(buf, LV_FS_WIN32_PATH "%s", path);
#endif
return (void*)CreateFileA(
#ifdef LV_FS_WIN32_PATH
buf,
#else
path,
#endif
desired_access,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
}
/**
* Close an opened file
* @param drv pointer to a driver where this function belongs
* @param file_p pointer to a FILE variable. (opened with fs_open)
* @return LV_FS_RES_OK: no error, the file is read
* any error from lv_fs_res_t enum
*/
static lv_fs_res_t fs_close(lv_fs_drv_t * drv, void * file_p)
{
LV_UNUSED(drv);
return CloseHandle((HANDLE)file_p)
? LV_FS_RES_OK
: fs_error_from_win32(GetLastError());
}
/**
* Read data from an opened file
* @param drv pointer to a driver where this function belongs
* @param file_p pointer to a FILE variable.
* @param buf pointer to a memory block where to store the read data
* @param btr number of Bytes To Read
* @param br the real number of read bytes (Byte Read)
* @return LV_FS_RES_OK: no error, the file is read
* any error from lv_fs_res_t enum
*/
static lv_fs_res_t fs_read(lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br)
{
LV_UNUSED(drv);
return ReadFile((HANDLE)file_p, buf, btr, (LPDWORD)br, NULL)
? LV_FS_RES_OK
: fs_error_from_win32(GetLastError());
}
/**
* Write into a file
* @param drv pointer to a driver where this function belongs
* @param file_p pointer to a FILE variable
* @param buf pointer to a buffer with the bytes to write
* @param btr Bytes To Write
* @param br the number of real written bytes (Bytes Written). NULL if unused.
* @return LV_FS_RES_OK or any error from lv_fs_res_t enum
*/
static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw)
{
LV_UNUSED(drv);
return WriteFile((HANDLE)file_p, buf, btw, (LPDWORD)bw, NULL)
? LV_FS_RES_OK
: fs_error_from_win32(GetLastError());
}
/**
* Set the read write pointer. Also expand the file size if necessary.
* @param drv pointer to a driver where this function belongs
* @param file_p pointer to a FILE variable. (opened with fs_open )
* @param pos the new position of read write pointer
* @return LV_FS_RES_OK: no error, the file is read
* any error from lv_fs_res_t enum
*/
static lv_fs_res_t fs_seek(lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs_whence_t whence)
{
LV_UNUSED(drv);
DWORD move_method = (DWORD)-1;
if (whence == LV_FS_SEEK_SET) {
move_method = FILE_BEGIN;
}
else if(whence == LV_FS_SEEK_CUR) {
move_method = FILE_CURRENT;
}
else if(whence == LV_FS_SEEK_END) {
move_method = FILE_END;
}
LARGE_INTEGER distance_to_move;
distance_to_move.QuadPart = pos;
return SetFilePointerEx((HANDLE)file_p, distance_to_move, NULL, move_method)
? LV_FS_RES_OK
: fs_error_from_win32(GetLastError());
}
/**
* Give the position of the read write pointer
* @param drv pointer to a driver where this function belongs
* @param file_p pointer to a FILE variable.
* @param pos_p pointer to to store the result
* @return LV_FS_RES_OK: no error, the file is read
* any error from lv_fs_res_t enum
*/
static lv_fs_res_t fs_tell(lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p)
{
LV_UNUSED(drv);
if (!pos_p) {
return LV_FS_RES_INV_PARAM;
}
LARGE_INTEGER file_pointer;
file_pointer.QuadPart = 0;
LARGE_INTEGER distance_to_move;
distance_to_move.QuadPart = 0;
if (SetFilePointerEx(
(HANDLE)file_p,
distance_to_move,
&file_pointer,
FILE_CURRENT)) {
if (file_pointer.QuadPart > LONG_MAX) {
return LV_FS_RES_INV_PARAM;
}
else {
*pos_p = file_pointer.LowPart;
return LV_FS_RES_OK;
}
}
else {
return fs_error_from_win32(GetLastError());
}
}
static char next_fn[256];
static lv_fs_res_t next_error = LV_FS_RES_OK;
/**
* Initialize a 'DIR' or 'HANDLE' variable for directory reading
* @param drv pointer to a driver where this function belongs
* @param path path to a directory
* @return pointer to an initialized 'DIR' or 'HANDLE' variable
*/
static void * fs_dir_open(lv_fs_drv_t * drv, const char * path)
{
LV_UNUSED(drv);
HANDLE d = INVALID_HANDLE_VALUE;
WIN32_FIND_DATAA fdata;
/*Make the path relative to the current directory (the projects root folder)*/
char buf[256];
#ifdef LV_FS_WIN32_PATH
sprintf(buf, LV_FS_WIN32_PATH "%s\\*", path);
#else
sprintf(buf, "%s\\*", path);
#endif
strcpy(next_fn, "");
d = FindFirstFileA(buf, &fdata);
do {
if (is_dots_name(fdata.cFileName)) {
continue;
}
else {
if (fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
sprintf(next_fn, "/%s", fdata.cFileName);
}
else {
sprintf(next_fn, "%s", fdata.cFileName);
}
break;
}
} while (FindNextFileA(d, &fdata));
next_error = fs_error_from_win32(GetLastError());
return d;
}
/**
* Read the next filename from a directory.
* The name of the directories will begin with '/'
* @param drv pointer to a driver where this function belongs
* @param dir_p pointer to an initialized 'DIR' or 'HANDLE' variable
* @param fn pointer to a buffer to store the filename
* @return LV_FS_RES_OK or any error from lv_fs_res_t enum
*/
static lv_fs_res_t fs_dir_read(lv_fs_drv_t * drv, void * dir_p, char * fn)
{
LV_UNUSED(drv);
strcpy(fn, next_fn);
lv_fs_res_t current_error = next_error;
next_error = LV_FS_RES_OK;
strcpy(next_fn, "");
WIN32_FIND_DATAA fdata;
while (FindNextFileA(dir_p, &fdata)) {
if (is_dots_name(fdata.cFileName)) {
continue;
}
else {
if (fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
sprintf(next_fn, "/%s", fdata.cFileName);
}
else {
sprintf(next_fn, "%s", fdata.cFileName);
}
break;
}
}
if (next_fn[0] == '\0') {
next_error = fs_error_from_win32(GetLastError());
}
return current_error;
}
/**
* Close the directory reading
* @param drv pointer to a driver where this function belongs
* @param dir_p pointer to an initialized 'DIR' or 'HANDLE' variable
* @return LV_FS_RES_OK or any error from lv_fs_res_t enum
*/
static lv_fs_res_t fs_dir_close(lv_fs_drv_t * drv, void * dir_p)
{
LV_UNUSED(drv);
return FindClose((HANDLE)dir_p)
? LV_FS_RES_OK
: fs_error_from_win32(GetLastError());
}
#endif /*LV_USE_FS_WIN32*/

View File

@@ -0,0 +1,55 @@
/**
* @file lv_fsdrv.h
*
*/
#ifndef LV_FSDRV_H
#define LV_FSDRV_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../lv_conf_internal.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
#if LV_USE_FS_FATFS != '\0'
void lv_fs_fatfs_init(void);
#endif
#if LV_USE_FS_STDIO != '\0'
void lv_fs_stdio_init(void);
#endif
#if LV_USE_FS_POSIX != '\0'
void lv_fs_posix_init(void);
#endif
#if LV_USE_FS_WIN32 != '\0'
void lv_fs_win32_init(void);
#endif
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_FSDRV_H*/

View File

@@ -0,0 +1,655 @@
#include "gifdec.h"
#include "../../../misc/lv_log.h"
#include "../../../misc/lv_mem.h"
#include "../../../misc/lv_color.h"
#if LV_USE_GIF
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#define MIN(A, B) ((A) < (B) ? (A) : (B))
#define MAX(A, B) ((A) > (B) ? (A) : (B))
typedef struct Entry {
uint16_t length;
uint16_t prefix;
uint8_t suffix;
} Entry;
typedef struct Table {
int bulk;
int nentries;
Entry *entries;
} Table;
static gd_GIF * gif_open(gd_GIF * gif);
static bool f_gif_open(gd_GIF * gif, const void * path, bool is_file);
static void f_gif_read(gd_GIF * gif, void * buf, size_t len);
static int f_gif_seek(gd_GIF * gif, size_t pos, int k);
static void f_gif_close(gd_GIF * gif);
static uint16_t
read_num(gd_GIF * gif)
{
uint8_t bytes[2];
f_gif_read(gif, bytes, 2);
return bytes[0] + (((uint16_t) bytes[1]) << 8);
}
gd_GIF *
gd_open_gif_file(const char *fname)
{
gd_GIF gif_base;
memset(&gif_base, 0, sizeof(gif_base));
bool res = f_gif_open(&gif_base, fname, true);
if(!res) return NULL;
return gif_open(&gif_base);
}
gd_GIF *
gd_open_gif_data(const void *data)
{
gd_GIF gif_base;
memset(&gif_base, 0, sizeof(gif_base));
bool res = f_gif_open(&gif_base, data, false);
if(!res) return NULL;
return gif_open(&gif_base);
}
static gd_GIF * gif_open(gd_GIF * gif_base)
{
uint8_t sigver[3];
uint16_t width, height, depth;
uint8_t fdsz, bgidx, aspect;
int i;
uint8_t *bgcolor;
int gct_sz;
gd_GIF *gif;
/* Header */
f_gif_read(gif_base, sigver, 3);
if (memcmp(sigver, "GIF", 3) != 0) {
LV_LOG_WARN("invalid signature\n");
goto fail;
}
/* Version */
f_gif_read(gif_base, sigver, 3);
if (memcmp(sigver, "89a", 3) != 0) {
LV_LOG_WARN("invalid version\n");
goto fail;
}
/* Width x Height */
width = read_num(gif_base);
height = read_num(gif_base);
/* FDSZ */
f_gif_read(gif_base, &fdsz, 1);
/* Presence of GCT */
if (!(fdsz & 0x80)) {
LV_LOG_WARN("no global color table\n");
goto fail;
}
/* Color Space's Depth */
depth = ((fdsz >> 4) & 7) + 1;
/* Ignore Sort Flag. */
/* GCT Size */
gct_sz = 1 << ((fdsz & 0x07) + 1);
/* Background Color Index */
f_gif_read(gif_base, &bgidx, 1);
/* Aspect Ratio */
f_gif_read(gif_base, &aspect, 1);
/* Create gd_GIF Structure. */
#if LV_COLOR_DEPTH == 32
gif = lv_mem_alloc(sizeof(gd_GIF) + 5 * width * height);
#elif LV_COLOR_DEPTH == 16
gif = lv_mem_alloc(sizeof(gd_GIF) + 4 * width * height);
#elif LV_COLOR_DEPTH == 8 || LV_COLOR_DEPTH == 1
gif = lv_mem_alloc(sizeof(gd_GIF) + 3 * width * height);
#endif
if (!gif) goto fail;
memcpy(gif, gif_base, sizeof(gd_GIF));
gif->width = width;
gif->height = height;
gif->depth = depth;
/* Read GCT */
gif->gct.size = gct_sz;
f_gif_read(gif, gif->gct.colors, 3 * gif->gct.size);
gif->palette = &gif->gct;
gif->bgindex = bgidx;
gif->canvas = (uint8_t *) &gif[1];
#if LV_COLOR_DEPTH == 32
gif->frame = &gif->canvas[4 * width * height];
#elif LV_COLOR_DEPTH == 16
gif->frame = &gif->canvas[3 * width * height];
#elif LV_COLOR_DEPTH == 8 || LV_COLOR_DEPTH == 1
gif->frame = &gif->canvas[2 * width * height];
#endif
if (gif->bgindex)
memset(gif->frame, gif->bgindex, gif->width * gif->height);
bgcolor = &gif->palette->colors[gif->bgindex*3];
if (bgcolor[0] || bgcolor[1] || bgcolor [2])
for (i = 0; i < gif->width * gif->height; i++) {
#if LV_COLOR_DEPTH == 32
gif->canvas[i*4 + 0] = *(bgcolor + 2);
gif->canvas[i*4 + 1] = *(bgcolor + 1);
gif->canvas[i*4 + 2] = *(bgcolor + 0);
gif->canvas[i*4 + 3] = 0xff;
#elif LV_COLOR_DEPTH == 16
lv_color_t c = lv_color_make(*(bgcolor + 0), *(bgcolor + 1), *(bgcolor + 2));
gif->canvas[i*3 + 0] = c.full & 0xff;
gif->canvas[i*3 + 1] = (c.full >> 8) & 0xff;
gif->canvas[i*3 + 2] = 0xff;
#elif LV_COLOR_DEPTH == 8
lv_color_t c = lv_color_make(*(bgcolor + 0), *(bgcolor + 1), *(bgcolor + 2));
gif->canvas[i*2 + 0] = c.full;
gif->canvas[i*2 + 1] = 0xff;
#endif
}
gif->anim_start = f_gif_seek(gif, 0, LV_FS_SEEK_CUR);
goto ok;
fail:
f_gif_close(gif_base);
ok:
return gif;
}
static void
discard_sub_blocks(gd_GIF *gif)
{
uint8_t size;
do {
f_gif_read(gif, &size, 1);
f_gif_seek(gif, size, LV_FS_SEEK_CUR);
} while (size);
}
static void
read_plain_text_ext(gd_GIF *gif)
{
if (gif->plain_text) {
uint16_t tx, ty, tw, th;
uint8_t cw, ch, fg, bg;
size_t sub_block;
f_gif_seek(gif, 1, LV_FS_SEEK_CUR); /* block size = 12 */
tx = read_num(gif);
ty = read_num(gif);
tw = read_num(gif);
th = read_num(gif);
f_gif_read(gif, &cw, 1);
f_gif_read(gif, &ch, 1);
f_gif_read(gif, &fg, 1);
f_gif_read(gif, &bg, 1);
sub_block = f_gif_seek(gif, 0, LV_FS_SEEK_CUR);
gif->plain_text(gif, tx, ty, tw, th, cw, ch, fg, bg);
f_gif_seek(gif, sub_block, LV_FS_SEEK_SET);
} else {
/* Discard plain text metadata. */
f_gif_seek(gif, 13, LV_FS_SEEK_CUR);
}
/* Discard plain text sub-blocks. */
discard_sub_blocks(gif);
}
static void
read_graphic_control_ext(gd_GIF *gif)
{
uint8_t rdit;
/* Discard block size (always 0x04). */
f_gif_seek(gif, 1, LV_FS_SEEK_CUR);
f_gif_read(gif, &rdit, 1);
gif->gce.disposal = (rdit >> 2) & 3;
gif->gce.input = rdit & 2;
gif->gce.transparency = rdit & 1;
gif->gce.delay = read_num(gif);
f_gif_read(gif, &gif->gce.tindex, 1);
/* Skip block terminator. */
f_gif_seek(gif, 1, LV_FS_SEEK_CUR);
}
static void
read_comment_ext(gd_GIF *gif)
{
if (gif->comment) {
size_t sub_block = f_gif_seek(gif, 0, LV_FS_SEEK_CUR);
gif->comment(gif);
f_gif_seek(gif, sub_block, LV_FS_SEEK_SET);
}
/* Discard comment sub-blocks. */
discard_sub_blocks(gif);
}
static void
read_application_ext(gd_GIF *gif)
{
char app_id[8];
char app_auth_code[3];
/* Discard block size (always 0x0B). */
f_gif_seek(gif, 1, LV_FS_SEEK_CUR);
/* Application Identifier. */
f_gif_read(gif, app_id, 8);
/* Application Authentication Code. */
f_gif_read(gif, app_auth_code, 3);
if (!strncmp(app_id, "NETSCAPE", sizeof(app_id))) {
/* Discard block size (0x03) and constant byte (0x01). */
f_gif_seek(gif, 2, LV_FS_SEEK_CUR);
gif->loop_count = read_num(gif);
/* Skip block terminator. */
f_gif_seek(gif, 1, LV_FS_SEEK_CUR);
} else if (gif->application) {
size_t sub_block = f_gif_seek(gif, 0, LV_FS_SEEK_CUR);
gif->application(gif, app_id, app_auth_code);
f_gif_seek(gif, sub_block, LV_FS_SEEK_SET);
discard_sub_blocks(gif);
} else {
discard_sub_blocks(gif);
}
}
static void
read_ext(gd_GIF *gif)
{
uint8_t label;
f_gif_read(gif, &label, 1);
switch (label) {
case 0x01:
read_plain_text_ext(gif);
break;
case 0xF9:
read_graphic_control_ext(gif);
break;
case 0xFE:
read_comment_ext(gif);
break;
case 0xFF:
read_application_ext(gif);
break;
default:
LV_LOG_WARN("unknown extension: %02X\n", label);
}
}
static Table *
new_table(int key_size)
{
int key;
int init_bulk = MAX(1 << (key_size + 1), 0x100);
Table *table = lv_mem_alloc(sizeof(*table) + sizeof(Entry) * init_bulk);
if (table) {
table->bulk = init_bulk;
table->nentries = (1 << key_size) + 2;
table->entries = (Entry *) &table[1];
for (key = 0; key < (1 << key_size); key++)
table->entries[key] = (Entry) {1, 0xFFF, key};
}
return table;
}
/* Add table entry. Return value:
* 0 on success
* +1 if key size must be incremented after this addition
* -1 if could not realloc table */
static int
add_entry(Table **tablep, uint16_t length, uint16_t prefix, uint8_t suffix)
{
Table *table = *tablep;
if (table->nentries == table->bulk) {
table->bulk *= 2;
table = lv_mem_realloc(table, sizeof(*table) + sizeof(Entry) * table->bulk);
if (!table) return -1;
table->entries = (Entry *) &table[1];
*tablep = table;
}
table->entries[table->nentries] = (Entry) {length, prefix, suffix};
table->nentries++;
if ((table->nentries & (table->nentries - 1)) == 0)
return 1;
return 0;
}
static uint16_t
get_key(gd_GIF *gif, int key_size, uint8_t *sub_len, uint8_t *shift, uint8_t *byte)
{
int bits_read;
int rpad;
int frag_size;
uint16_t key;
key = 0;
for (bits_read = 0; bits_read < key_size; bits_read += frag_size) {
rpad = (*shift + bits_read) % 8;
if (rpad == 0) {
/* Update byte. */
if (*sub_len == 0) {
f_gif_read(gif, sub_len, 1); /* Must be nonzero! */
if (*sub_len == 0) return 0x1000;
}
f_gif_read(gif, byte, 1);
(*sub_len)--;
}
frag_size = MIN(key_size - bits_read, 8 - rpad);
key |= ((uint16_t) ((*byte) >> rpad)) << bits_read;
}
/* Clear extra bits to the left. */
key &= (1 << key_size) - 1;
*shift = (*shift + key_size) % 8;
return key;
}
/* Compute output index of y-th input line, in frame of height h. */
static int
interlaced_line_index(int h, int y)
{
int p; /* number of lines in current pass */
p = (h - 1) / 8 + 1;
if (y < p) /* pass 1 */
return y * 8;
y -= p;
p = (h - 5) / 8 + 1;
if (y < p) /* pass 2 */
return y * 8 + 4;
y -= p;
p = (h - 3) / 4 + 1;
if (y < p) /* pass 3 */
return y * 4 + 2;
y -= p;
/* pass 4 */
return y * 2 + 1;
}
/* Decompress image pixels.
* Return 0 on success or -1 on out-of-memory (w.r.t. LZW code table). */
static int
read_image_data(gd_GIF *gif, int interlace)
{
uint8_t sub_len, shift, byte;
int init_key_size, key_size, table_is_full=0;
int frm_off, frm_size, str_len=0, i, p, x, y;
uint16_t key, clear, stop;
int ret;
Table *table;
Entry entry = {0};
size_t start, end;
f_gif_read(gif, &byte, 1);
key_size = (int) byte;
start = f_gif_seek(gif, 0, LV_FS_SEEK_CUR);
discard_sub_blocks(gif);
end = f_gif_seek(gif, 0, LV_FS_SEEK_CUR);
f_gif_seek(gif, start, LV_FS_SEEK_SET);
clear = 1 << key_size;
stop = clear + 1;
table = new_table(key_size);
key_size++;
init_key_size = key_size;
sub_len = shift = 0;
key = get_key(gif, key_size, &sub_len, &shift, &byte); /* clear code */
frm_off = 0;
ret = 0;
frm_size = gif->fw*gif->fh;
while (frm_off < frm_size) {
if (key == clear) {
key_size = init_key_size;
table->nentries = (1 << (key_size - 1)) + 2;
table_is_full = 0;
} else if (!table_is_full) {
ret = add_entry(&table, str_len + 1, key, entry.suffix);
if (ret == -1) {
lv_mem_free(table);
return -1;
}
if (table->nentries == 0x1000) {
ret = 0;
table_is_full = 1;
}
}
key = get_key(gif, key_size, &sub_len, &shift, &byte);
if (key == clear) continue;
if (key == stop || key == 0x1000) break;
if (ret == 1) key_size++;
entry = table->entries[key];
str_len = entry.length;
for (i = 0; i < str_len; i++) {
p = frm_off + entry.length - 1;
x = p % gif->fw;
y = p / gif->fw;
if (interlace)
y = interlaced_line_index((int) gif->fh, y);
gif->frame[(gif->fy + y) * gif->width + gif->fx + x] = entry.suffix;
if (entry.prefix == 0xFFF)
break;
else
entry = table->entries[entry.prefix];
}
frm_off += str_len;
if (key < table->nentries - 1 && !table_is_full)
table->entries[table->nentries - 1].suffix = entry.suffix;
}
lv_mem_free(table);
if (key == stop) f_gif_read(gif, &sub_len, 1); /* Must be zero! */
f_gif_seek(gif, end, LV_FS_SEEK_SET);
return 0;
}
/* Read image.
* Return 0 on success or -1 on out-of-memory (w.r.t. LZW code table). */
static int
read_image(gd_GIF *gif)
{
uint8_t fisrz;
int interlace;
/* Image Descriptor. */
gif->fx = read_num(gif);
gif->fy = read_num(gif);
gif->fw = read_num(gif);
gif->fh = read_num(gif);
f_gif_read(gif, &fisrz, 1);
interlace = fisrz & 0x40;
/* Ignore Sort Flag. */
/* Local Color Table? */
if (fisrz & 0x80) {
/* Read LCT */
gif->lct.size = 1 << ((fisrz & 0x07) + 1);
f_gif_read(gif, gif->lct.colors, 3 * gif->lct.size);
gif->palette = &gif->lct;
} else
gif->palette = &gif->gct;
/* Image Data. */
return read_image_data(gif, interlace);
}
static void
render_frame_rect(gd_GIF *gif, uint8_t *buffer)
{
int i, j, k;
uint8_t index, *color;
i = gif->fy * gif->width + gif->fx;
for (j = 0; j < gif->fh; j++) {
for (k = 0; k < gif->fw; k++) {
index = gif->frame[(gif->fy + j) * gif->width + gif->fx + k];
color = &gif->palette->colors[index*3];
if (!gif->gce.transparency || index != gif->gce.tindex) {
#if LV_COLOR_DEPTH == 32
buffer[(i+k)*4 + 0] = *(color + 2);
buffer[(i+k)*4 + 1] = *(color + 1);
buffer[(i+k)*4 + 2] = *(color + 0);
buffer[(i+k)*4 + 3] = 0xFF;
#elif LV_COLOR_DEPTH == 16
lv_color_t c = lv_color_make(*(color + 0), *(color + 1), *(color + 2));
buffer[(i+k)*3 + 0] = c.full & 0xff;
buffer[(i+k)*3 + 1] = (c.full >> 8) & 0xff;
buffer[(i+k)*3 + 2] = 0xff;
#elif LV_COLOR_DEPTH == 8
lv_color_t c = lv_color_make(*(color + 0), *(color + 1), *(color + 2));
buffer[(i+k)*2 + 0] = c.full;
buffer[(i+k)*2 + 1] = 0xff;
#elif LV_COLOR_DEPTH == 1
uint8_t b = (*(color + 0)) | (*(color + 1)) | (*(color + 2));
buffer[(i+k)*2 + 0] = b > 128 ? 1 : 0;
buffer[(i+k)*2 + 1] = 0xff;
#endif
}
}
i += gif->width;
}
}
static void
dispose(gd_GIF *gif)
{
int i, j, k;
uint8_t *bgcolor;
switch (gif->gce.disposal) {
case 2: /* Restore to background color. */
bgcolor = &gif->palette->colors[gif->bgindex*3];
uint8_t opa = 0xff;
if(gif->gce.transparency) opa = 0x00;
i = gif->fy * gif->width + gif->fx;
for (j = 0; j < gif->fh; j++) {
for (k = 0; k < gif->fw; k++) {
#if LV_COLOR_DEPTH == 32
gif->canvas[(i+k)*4 + 0] = *(bgcolor + 2);
gif->canvas[(i+k)*4 + 1] = *(bgcolor + 1);
gif->canvas[(i+k)*4 + 2] = *(bgcolor + 0);
gif->canvas[(i+k)*4 + 3] = opa;
#elif LV_COLOR_DEPTH == 16
lv_color_t c = lv_color_make(*(bgcolor + 0), *(bgcolor + 1), *(bgcolor + 2));
gif->canvas[(i+k)*3 + 0] = c.full & 0xff;
gif->canvas[(i+k)*3 + 1] = (c.full >> 8) & 0xff;
gif->canvas[(i+k)*3 + 2] = opa;
#elif LV_COLOR_DEPTH == 8
lv_color_t c = lv_color_make(*(bgcolor + 0), *(bgcolor + 1), *(bgcolor + 2));
gif->canvas[(i+k)*2 + 0] = c.full;
gif->canvas[(i+k)*2 + 1] = opa;
#elif LV_COLOR_DEPTH == 1
uint8_t b = (*(bgcolor + 0)) | (*(bgcolor + 1)) | (*(bgcolor + 2));
gif->canvas[(i+k)*2 + 0] = b > 128 ? 1 : 0;
gif->canvas[(i+k)*2 + 1] = opa;
#endif
}
i += gif->width;
}
break;
case 3: /* Restore to previous, i.e., don't update canvas.*/
break;
default:
/* Add frame non-transparent pixels to canvas. */
render_frame_rect(gif, gif->canvas);
}
}
/* Return 1 if got a frame; 0 if got GIF trailer; -1 if error. */
int
gd_get_frame(gd_GIF *gif)
{
char sep;
dispose(gif);
f_gif_read(gif, &sep, 1);
while (sep != ',') {
if (sep == ';')
return 0;
if (sep == '!')
read_ext(gif);
else return -1;
f_gif_read(gif, &sep, 1);
}
if (read_image(gif) == -1)
return -1;
return 1;
}
void
gd_render_frame(gd_GIF *gif, uint8_t *buffer)
{
// uint32_t i;
// uint32_t j;
// for(i = 0, j = 0; i < gif->width * gif->height * 3; i+= 3, j+=4) {
// buffer[j + 0] = gif->canvas[i + 2];
// buffer[j + 1] = gif->canvas[i + 1];
// buffer[j + 2] = gif->canvas[i + 0];
// buffer[j + 3] = 0xFF;
// }
// memcpy(buffer, gif->canvas, gif->width * gif->height * 3);
render_frame_rect(gif, buffer);
}
void
gd_rewind(gd_GIF *gif)
{
f_gif_seek(gif, gif->anim_start, LV_FS_SEEK_SET);
}
void
gd_close_gif(gd_GIF *gif)
{
f_gif_close(gif);
lv_mem_free(gif);
}
static bool f_gif_open(gd_GIF * gif, const void * path, bool is_file)
{
gif->f_rw_p = 0;
gif->data = NULL;
gif->is_file = is_file;
if(is_file) {
lv_fs_res_t res = lv_fs_open(&gif->fd, path, LV_FS_MODE_RD);
if(res != LV_FS_RES_OK) return false;
else return true;
} else {
gif->data = path;
return true;
}
}
static void f_gif_read(gd_GIF * gif, void * buf, size_t len)
{
if(gif->is_file) {
lv_fs_read(&gif->fd, buf, len, NULL);
} else
{
memcpy(buf, &gif->data[gif->f_rw_p], len);
gif->f_rw_p += len;
}
}
static int f_gif_seek(gd_GIF * gif, size_t pos, int k)
{
if(gif->is_file) {
lv_fs_seek(&gif->fd, pos, k);
uint32_t x;
lv_fs_tell(&gif->fd, &x);
return x;
} else {
if(k == LV_FS_SEEK_CUR) gif->f_rw_p += pos;
else if(k == LV_FS_SEEK_SET) gif->f_rw_p = pos;
return gif->f_rw_p;
}
}
static void f_gif_close(gd_GIF * gif)
{
if(gif->is_file) {
lv_fs_close(&gif->fd);
}
}
#endif /*LV_USE_GIF*/

View File

@@ -0,0 +1,60 @@
#ifndef GIFDEC_H
#define GIFDEC_H
#include <stdint.h>
#include "../../../misc/lv_fs.h"
#if LV_USE_GIF
typedef struct gd_Palette {
int size;
uint8_t colors[0x100 * 3];
} gd_Palette;
typedef struct gd_GCE {
uint16_t delay;
uint8_t tindex;
uint8_t disposal;
int input;
int transparency;
} gd_GCE;
typedef struct gd_GIF {
lv_fs_file_t fd;
const char * data;
uint8_t is_file;
uint32_t f_rw_p;
int32_t anim_start;
uint16_t width, height;
uint16_t depth;
uint16_t loop_count;
gd_GCE gce;
gd_Palette *palette;
gd_Palette lct, gct;
void (*plain_text)(
struct gd_GIF *gif, uint16_t tx, uint16_t ty,
uint16_t tw, uint16_t th, uint8_t cw, uint8_t ch,
uint8_t fg, uint8_t bg
);
void (*comment)(struct gd_GIF *gif);
void (*application)(struct gd_GIF *gif, char id[8], char auth[3]);
uint16_t fx, fy, fw, fh;
uint8_t bgindex;
uint8_t *canvas, *frame;
} gd_GIF;
gd_GIF * gd_open_gif_file(const char *fname);
gd_GIF * gd_open_gif_data(const void *data);
void gd_render_frame(gd_GIF *gif, uint8_t *buffer);
int gd_get_frame(gd_GIF *gif);
void gd_rewind(gd_GIF *gif);
void gd_close_gif(gd_GIF *gif);
#endif /*LV_USE_GIF*/
#endif /* GIFDEC_H */

View File

@@ -0,0 +1,154 @@
/**
* @file lv_gifenc.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_gif.h"
#if LV_USE_GIF
#include "gifdec.h"
/*********************
* DEFINES
*********************/
#define MY_CLASS &lv_gif_class
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_gif_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_gif_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void next_frame_task_cb(lv_timer_t * t);
/**********************
* STATIC VARIABLES
**********************/
const lv_obj_class_t lv_gif_class = {
.constructor_cb = lv_gif_constructor,
.destructor_cb = lv_gif_destructor,
.instance_size = sizeof(lv_gif_t),
.base_class = &lv_img_class
};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_obj_t * lv_gif_create(lv_obj_t * parent)
{
LV_LOG_INFO("begin");
lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
lv_obj_class_init_obj(obj);
return obj;
}
void lv_gif_set_src(lv_obj_t * obj, const void * src)
{
lv_gif_t * gifobj = (lv_gif_t *) obj;
/*Close previous gif if any*/
if(gifobj->gif) {
lv_img_cache_invalidate_src(&gifobj->imgdsc);
gd_close_gif(gifobj->gif);
gifobj->gif = NULL;
gifobj->imgdsc.data = NULL;
}
if(lv_img_src_get_type(src) == LV_IMG_SRC_VARIABLE) {
const lv_img_dsc_t * img_dsc = src;
gifobj->gif = gd_open_gif_data(img_dsc->data);
}
else if(lv_img_src_get_type(src) == LV_IMG_SRC_FILE) {
gifobj->gif = gd_open_gif_file(src);
}
if(gifobj->gif == NULL) {
LV_LOG_WARN("Could't load the source");
return;
}
gifobj->imgdsc.data = gifobj->gif->canvas;
gifobj->imgdsc.header.always_zero = 0;
gifobj->imgdsc.header.cf = LV_IMG_CF_TRUE_COLOR_ALPHA;
gifobj->imgdsc.header.h = gifobj->gif->height;
gifobj->imgdsc.header.w = gifobj->gif->width;
gifobj->last_call = lv_tick_get();
lv_img_set_src(obj, &gifobj->imgdsc);
lv_timer_resume(gifobj->timer);
lv_timer_reset(gifobj->timer);
next_frame_task_cb(gifobj->timer);
}
void lv_gif_restart(lv_obj_t * obj)
{
lv_gif_t * gifobj = (lv_gif_t *) obj;
gd_rewind(gifobj->gif);
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_gif_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
lv_gif_t * gifobj = (lv_gif_t *) obj;
gifobj->timer = lv_timer_create(next_frame_task_cb, 10, obj);
lv_timer_pause(gifobj->timer);
}
static void lv_gif_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
lv_gif_t * gifobj = (lv_gif_t *) obj;
lv_img_cache_invalidate_src(&gifobj->imgdsc);
gd_close_gif(gifobj->gif);
lv_timer_del(gifobj->timer);
}
static void next_frame_task_cb(lv_timer_t * t)
{
lv_obj_t * obj = t->user_data;
lv_gif_t * gifobj = (lv_gif_t *) obj;
uint32_t elaps = lv_tick_elaps(gifobj->last_call);
if(elaps < gifobj->gif->gce.delay * 10) return;
gifobj->last_call = lv_tick_get();
int has_next = gd_get_frame(gifobj->gif);
if(has_next == 0) {
/*It was the last repeat*/
if(gifobj->gif->loop_count == 1) {
lv_res_t res = lv_event_send(obj, LV_EVENT_READY, NULL);
if(res != LV_FS_RES_OK) return;
} else {
if(gifobj->gif->loop_count > 1) gifobj->gif->loop_count--;
gd_rewind(gifobj->gif);
}
}
gd_render_frame(gifobj->gif, (uint8_t *)gifobj->imgdsc.data);
lv_img_cache_invalidate_src(lv_img_get_src(obj));
lv_obj_invalidate(obj);
}
#endif /*LV_USE_GIF*/

View File

@@ -0,0 +1,58 @@
/**
* @file lv_gif.h
*
*/
#ifndef LV_GIF_H
#define LV_GIF_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../lvgl.h"
#if LV_USE_GIF
#include "gifdec.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_img_t img;
gd_GIF *gif;
lv_timer_t * timer;
lv_img_dsc_t imgdsc;
uint32_t last_call;
}lv_gif_t;
extern const lv_obj_class_t lv_gif_class;
/**********************
* GLOBAL PROTOTYPES
**********************/
lv_obj_t * lv_gif_create(lv_obj_t * parent);
void lv_gif_set_src(lv_obj_t * obj, const void * src);
void lv_gif_restart(lv_obj_t * gif);
/**********************
* MACROS
**********************/
#endif /*LV_USE_GIF*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_GIF_H*/

View File

@@ -0,0 +1,45 @@
/**
* @file lv_libs.h
*
*/
#ifndef LV_LIBS_H
#define LV_LIBS_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "bmp/lv_bmp.h"
#include "fsdrv/lv_fsdrv.h"
#include "png/lv_png.h"
#include "gif/lv_gif.h"
#include "qrcode/lv_qrcode.h"
#include "sjpg/lv_sjpg.h"
#include "freetype/lv_freetype.h"
#include "rlottie/lv_rlottie.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_LAYOUTS_H*/

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,245 @@
/**
* @file lv_png.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../../lvgl.h"
#if LV_USE_PNG
#include "lv_png.h"
#include "lodepng.h"
#include <stdlib.h>
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static lv_res_t decoder_info(struct _lv_img_decoder_t * decoder, const void * src, lv_img_header_t * header);
static lv_res_t decoder_open(lv_img_decoder_t * dec, lv_img_decoder_dsc_t * dsc);
static void decoder_close(lv_img_decoder_t * dec, lv_img_decoder_dsc_t * dsc);
static void convert_color_depth(uint8_t * img, uint32_t px_cnt);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Register the PNG decoder functions in LittlevGL
*/
void lv_png_init(void)
{
lv_img_decoder_t * dec = lv_img_decoder_create();
lv_img_decoder_set_info_cb(dec, decoder_info);
lv_img_decoder_set_open_cb(dec, decoder_open);
lv_img_decoder_set_close_cb(dec, decoder_close);
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Get info about a PNG image
* @param src can be file name or pointer to a C array
* @param header store the info here
* @return LV_RES_OK: no error; LV_RES_INV: can't get the info
*/
static lv_res_t decoder_info(struct _lv_img_decoder_t * decoder, const void * src, lv_img_header_t * header)
{
(void) decoder; /*Unused*/
lv_img_src_t src_type = lv_img_src_get_type(src); /*Get the source type*/
/*If it's a PNG file...*/
if(src_type == LV_IMG_SRC_FILE) {
const char * fn = src;
if(!strcmp(&fn[strlen(fn) - 3], "png")) { /*Check the extension*/
/* Read the width and height from the file. They have a constant location:
* [16..23]: width
* [24..27]: height
*/
uint32_t size[2];
lv_fs_file_t f;
lv_fs_res_t res = lv_fs_open(&f, fn, LV_FS_MODE_RD);
if(res != LV_FS_RES_OK) return LV_RES_INV;
lv_fs_seek(&f, 16, LV_FS_SEEK_SET);
uint32_t rn;
lv_fs_read(&f, &size, 8, &rn);
if(rn != 8) return LV_RES_INV;
lv_fs_close(&f);
/*Save the data in the header*/
header->always_zero = 0;
header->cf = LV_IMG_CF_RAW_ALPHA;
/*The width and height are stored in Big endian format so convert them to little endian*/
header->w = (lv_coord_t) ((size[0] & 0xff000000) >> 24) + ((size[0] & 0x00ff0000) >> 8);
header->h = (lv_coord_t) ((size[1] & 0xff000000) >> 24) + ((size[1] & 0x00ff0000) >> 8);
return LV_RES_OK;
}
}
/*If it's a PNG file in a C array...*/
else if(src_type == LV_IMG_SRC_VARIABLE) {
const lv_img_dsc_t * img_dsc = src;
header->always_zero = 0;
header->cf = img_dsc->header.cf; /*Save the color format*/
header->w = img_dsc->header.w; /*Save the color width*/
header->h = img_dsc->header.h; /*Save the color height*/
return LV_RES_OK;
}
return LV_RES_INV; /*If didn't succeeded earlier then it's an error*/
}
/**
* Open a PNG image and return the decided image
* @param src can be file name or pointer to a C array
* @param style style of the image object (unused now but certain formats might use it)
* @return pointer to the decoded image or `LV_IMG_DECODER_OPEN_FAIL` if failed
*/
static lv_res_t decoder_open(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc)
{
(void) decoder; /*Unused*/
uint32_t error; /*For the return values of PNG decoder functions*/
uint8_t * img_data = NULL;
/*If it's a PNG file...*/
if(dsc->src_type == LV_IMG_SRC_FILE) {
const char * fn = dsc->src;
if(!strcmp(&fn[strlen(fn) - 3], "png")) { /*Check the extension*/
/*Load the PNG file into buffer. It's still compressed (not decoded)*/
unsigned char * png_data; /*Pointer to the loaded data. Same as the original file just loaded into the RAM*/
size_t png_data_size; /*Size of `png_data` in bytes*/
error = lodepng_load_file(&png_data, &png_data_size, fn); /*Load the file*/
if(error) {
LV_LOG_WARN("error %u: %s\n", error, lodepng_error_text(error));
return LV_RES_INV;
}
/*Decode the PNG image*/
uint32_t png_width; /*Will be the width of the decoded image*/
uint32_t png_height; /*Will be the width of the decoded image*/
/*Decode the loaded image in ARGB8888 */
error = lodepng_decode32(&img_data, &png_width, &png_height, png_data, png_data_size);
lv_mem_free(png_data); /*Free the loaded file*/
if(error) {
LV_LOG_WARN("error %u: %s\n", error, lodepng_error_text(error));
return LV_RES_INV;
}
/*Convert the image to the system's color depth*/
convert_color_depth(img_data, png_width * png_height);
dsc->img_data = img_data;
return LV_RES_OK; /*The image is fully decoded. Return with its pointer*/
}
}
/*If it's a PNG file in a C array...*/
else if(dsc->src_type == LV_IMG_SRC_VARIABLE) {
const lv_img_dsc_t * img_dsc = dsc->src;
uint32_t png_width; /*No used, just required by he decoder*/
uint32_t png_height; /*No used, just required by he decoder*/
/*Decode the image in ARGB8888 */
error = lodepng_decode32(&img_data, &png_width, &png_height, img_dsc->data, img_dsc->data_size);
if(error) {
return LV_RES_INV;
}
/*Convert the image to the system's color depth*/
convert_color_depth(img_data, png_width * png_height);
dsc->img_data = img_data;
return LV_RES_OK; /*Return with its pointer*/
}
return LV_RES_INV; /*If not returned earlier then it failed*/
}
/**
* Free the allocated resources
*/
static void decoder_close(lv_img_decoder_t *decoder, lv_img_decoder_dsc_t *dsc)
{
LV_UNUSED(decoder); /*Unused*/
if (dsc->img_data) {
lv_mem_free((uint8_t *)dsc->img_data);
dsc->img_data = NULL;
}
}
/**
* If the display is not in 32 bit format (ARGB888) then covert the image to the current color depth
* @param img the ARGB888 image
* @param px_cnt number of pixels in `img`
*/
static void convert_color_depth(uint8_t * img, uint32_t px_cnt)
{
#if LV_COLOR_DEPTH == 32
lv_color32_t * img_argb = (lv_color32_t*)img;
lv_color_t c;
lv_color_t * img_c = (lv_color_t *) img;
uint32_t i;
for(i = 0; i < px_cnt; i++) {
c = lv_color_make(img_argb[i].ch.red, img_argb[i].ch.green, img_argb[i].ch.blue);
img_c[i].ch.red = c.ch.blue;
img_c[i].ch.blue = c.ch.red;
}
#elif LV_COLOR_DEPTH == 16
lv_color32_t * img_argb = (lv_color32_t*)img;
lv_color_t c;
uint32_t i;
for(i = 0; i < px_cnt; i++) {
c = lv_color_make(img_argb[i].ch.blue, img_argb[i].ch.green, img_argb[i].ch.red);
img[i*3 + 2] = img_argb[i].ch.alpha;
img[i*3 + 1] = c.full >> 8;
img[i*3 + 0] = c.full & 0xFF;
}
#elif LV_COLOR_DEPTH == 8
lv_color32_t * img_argb = (lv_color32_t*)img;
lv_color_t c;
uint32_t i;
for(i = 0; i < px_cnt; i++) {
c = lv_color_make(img_argb[i].ch.red, img_argb[i].ch.green, img_argb[i].ch.blue);
img[i*2 + 1] = img_argb[i].ch.alpha;
img[i*2 + 0] = c.full;
}
#elif LV_COLOR_DEPTH == 1
lv_color32_t * img_argb = (lv_color32_t*)img;
uint8_t b;
uint32_t i;
for(i = 0; i < px_cnt; i++) {
b = img_argb[i].ch.red | img_argb[i].ch.green | img_argb[i].ch.blue;
img[i*2 + 1] = img_argb[i].ch.alpha;
img[i*2 + 0] = b > 128 ? 1 : 0;
}
#endif
}
#endif /*LV_USE_PNG*/

View File

@@ -0,0 +1,46 @@
/**
* @file lv_png.h
*
*/
#ifndef LV_PNG_H
#define LV_PNG_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../lv_conf_internal.h"
#if LV_USE_PNG
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Register the PNG decoder functions in LittlevGL
*/
void lv_png_init(void);
/**********************
* MACROS
**********************/
#endif /*LV_USE_PNG*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_PNG_H*/

View File

@@ -0,0 +1,178 @@
/**
* @file lv_qrcode.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_qrcode.h"
#if LV_USE_QRCODE
#include "qrcodegen.h"
/*********************
* DEFINES
*********************/
#define QR_SIZE 140
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
const lv_obj_class_t lv_qrcode_class = {
.base_class = &lv_canvas_class
};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Create an empty QR code (an `lv_canvas`) object.
* @param parent point to an object where to create the QR code
* @param size width and height of the QR code
* @param dark_color dark color of the QR code
* @param light_color light color of the QR code
* @return pointer to the created QR code object
*/
lv_obj_t * lv_qrcode_create(lv_obj_t * parent, lv_coord_t size, lv_color_t dark_color, lv_color_t light_color)
{
uint32_t buf_size = LV_CANVAS_BUF_SIZE_INDEXED_1BIT(size, size);
uint8_t * buf = lv_mem_alloc(buf_size);
LV_ASSERT_MALLOC(buf);
if(buf == NULL) return NULL;
lv_obj_t * canvas = lv_canvas_create(parent);
if(canvas == NULL) return NULL;
lv_canvas_set_buffer(canvas, buf, size, size, LV_IMG_CF_INDEXED_1BIT);
lv_canvas_set_palette(canvas, 0, dark_color);
lv_canvas_set_palette(canvas, 1, light_color);
return canvas;
}
/**
* Set the data of a QR code object
* @param qrcode pointer to aQ code object
* @param data data to display
* @param data_len length of data in bytes
* @return LV_RES_OK: if no error; LV_RES_INV: on error
*/
lv_res_t lv_qrcode_update(lv_obj_t * qrcode, const void * data, uint32_t data_len)
{
lv_color_t c;
c.full = 1;
lv_canvas_fill_bg(qrcode, c, LV_OPA_COVER);
if(data_len > qrcodegen_BUFFER_LEN_MAX) return LV_RES_INV;
uint8_t * qr0 = lv_mem_alloc(qrcodegen_BUFFER_LEN_MAX);
LV_ASSERT_MALLOC(qr0);
uint8_t * data_tmp = lv_mem_alloc(qrcodegen_BUFFER_LEN_MAX);
LV_ASSERT_MALLOC(data_tmp);
memcpy(data_tmp, data, data_len);
bool ok = qrcodegen_encodeBinary(data_tmp, data_len,
qr0, qrcodegen_Ecc_MEDIUM,
qrcodegen_VERSION_MIN, qrcodegen_VERSION_MAX,
qrcodegen_Mask_AUTO, true);
if (!ok) {
lv_mem_free(qr0);
lv_mem_free(data_tmp);
return LV_RES_INV;
}
lv_img_dsc_t * imgdsc = lv_canvas_get_img(qrcode);
lv_coord_t obj_w = imgdsc->header.w;
int qr_size = qrcodegen_getSize(qr0);
int scale = obj_w / qr_size;
int scaled = qr_size * scale;
int margin = (obj_w - scaled) / 2;
uint8_t * buf_u8 = (uint8_t *)imgdsc->data + 8; /*+8 skip the palette*/
/* Copy the qr code canvas:
* A simple `lv_canvas_set_px` would work but it's slow for so many pixels.
* So buffer 1 byte (8 px) from the qr code and set it in the canvas image */
uint32_t row_byte_cnt = (imgdsc->header.w + 7) >> 3;
int y;
for (y = margin; y < scaled + margin; y+=scale) {
uint8_t b = 0;
uint8_t p = 0;
bool aligned = false;
int x;
for (x = margin; x < scaled + margin; x++) {
bool a = qrcodegen_getModule(qr0, (x - margin) / scale, (y - margin) / scale);
if(aligned == false && (x & 0x7) == 0) aligned = true;
if(aligned == false) {
c.full = a ? 0 : 1;
lv_canvas_set_px_color(qrcode, x, y, c);
} else {
if(!a) b |= (1 << (7 - p));
p++;
if(p == 8) {
uint32_t px = row_byte_cnt * y + (x >> 3);
buf_u8[px] = b;
b = 0;
p = 0;
}
}
}
/*Process the last byte of the row*/
if(p) {
/*Make the rest of the bits white*/
b |= (1 << (8 - p)) - 1;
uint32_t px = row_byte_cnt * y + (x >> 3);
buf_u8[px] = b;
}
/*The Qr is probably scaled so simply to the repeated rows*/
int s;
const uint8_t * row_ori = buf_u8 + row_byte_cnt * y;
for(s = 1; s < scale; s++) {
memcpy((uint8_t*)buf_u8 + row_byte_cnt * (y + s), row_ori, row_byte_cnt);
}
}
lv_mem_free(qr0);
lv_mem_free(data_tmp);
return LV_RES_OK;
}
/**
* Delete a QR code object
* @param qrcode pointer to a QR code obejct
*/
void lv_qrcode_delete(lv_obj_t * qrcode)
{
lv_img_dsc_t * img = lv_canvas_get_img(qrcode);
lv_img_cache_invalidate_src(img);
lv_mem_free((void*)img->data);
lv_obj_del(qrcode);
}
/**********************
* STATIC FUNCTIONS
**********************/
#endif /*LV_USE_QRCODE*/

View File

@@ -0,0 +1,68 @@
/**
* @file lv_qrcode
*
*/
#ifndef LV_QRCODE_H
#define LV_QRCODE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../lvgl.h"
#if LV_USE_QRCODE
/*********************
* DEFINES
*********************/
extern const lv_obj_class_t lv_qrcode_class;
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create an empty QR code (an `lv_canvas`) object.
* @param parent point to an object where to create the QR code
* @param size width and height of the QR code
* @param dark_color dark color of the QR code
* @param light_color light color of the QR code
* @return pointer to the created QR code object
*/
lv_obj_t * lv_qrcode_create(lv_obj_t * parent, lv_coord_t size, lv_color_t dark_color, lv_color_t light_color);
/**
* Set the data of a QR code object
* @param qrcode pointer to aQ code object
* @param data data to display
* @param data_len length of data in bytes
* @return LV_RES_OK: if no error; LV_RES_INV: on error
*/
lv_res_t lv_qrcode_update(lv_obj_t * qrcode, const void * data, uint32_t data_len);
/**
* Delete a QR code object
* @param qrcode pointer to a QR code object
*/
void lv_qrcode_delete(lv_obj_t * qrcode);
/**********************
* MACROS
**********************/
#endif /*LV_USE_QRCODE*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_QRCODE_H*/

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,311 @@
/*
* QR Code generator library (C)
*
* Copyright (c) Project Nayuki. (MIT License)
* https://www.nayuki.io/page/qr-code-generator-library
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
* - The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* - The Software is provided "as is", without warranty of any kind, express or
* implied, including but not limited to the warranties of merchantability,
* fitness for a particular purpose and noninfringement. In no event shall the
* authors or copyright holders be liable for any claim, damages or other
* liability, whether in an action of contract, tort or otherwise, arising from,
* out of or in connection with the Software or the use or other dealings in the
* Software.
*/
#pragma once
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/*
* This library creates QR Code symbols, which is a type of two-dimension barcode.
* Invented by Denso Wave and described in the ISO/IEC 18004 standard.
* A QR Code structure is an immutable square grid of black and white cells.
* The library provides functions to create a QR Code from text or binary data.
* The library covers the QR Code Model 2 specification, supporting all versions (sizes)
* from 1 to 40, all 4 error correction levels, and 4 character encoding modes.
*
* Ways to create a QR Code object:
* - High level: Take the payload data and call qrcodegen_encodeText() or qrcodegen_encodeBinary().
* - Low level: Custom-make the list of segments and call
* qrcodegen_encodeSegments() or qrcodegen_encodeSegmentsAdvanced().
* (Note that all ways require supplying the desired error correction level and various byte buffers.)
*/
/*---- Enum and struct types----*/
/*
* The error correction level in a QR Code symbol.
*/
enum qrcodegen_Ecc {
// Must be declared in ascending order of error protection
// so that an internal qrcodegen function works properly
qrcodegen_Ecc_LOW = 0 , // The QR Code can tolerate about 7% erroneous codewords
qrcodegen_Ecc_MEDIUM , // The QR Code can tolerate about 15% erroneous codewords
qrcodegen_Ecc_QUARTILE, // The QR Code can tolerate about 25% erroneous codewords
qrcodegen_Ecc_HIGH , // The QR Code can tolerate about 30% erroneous codewords
};
/*
* The mask pattern used in a QR Code symbol.
*/
enum qrcodegen_Mask {
// A special value to tell the QR Code encoder to
// automatically select an appropriate mask pattern
qrcodegen_Mask_AUTO = -1,
// The eight actual mask patterns
qrcodegen_Mask_0 = 0,
qrcodegen_Mask_1,
qrcodegen_Mask_2,
qrcodegen_Mask_3,
qrcodegen_Mask_4,
qrcodegen_Mask_5,
qrcodegen_Mask_6,
qrcodegen_Mask_7,
};
/*
* Describes how a segment's data bits are interpreted.
*/
enum qrcodegen_Mode {
qrcodegen_Mode_NUMERIC = 0x1,
qrcodegen_Mode_ALPHANUMERIC = 0x2,
qrcodegen_Mode_BYTE = 0x4,
qrcodegen_Mode_KANJI = 0x8,
qrcodegen_Mode_ECI = 0x7,
};
/*
* A segment of character/binary/control data in a QR Code symbol.
* The mid-level way to create a segment is to take the payload data
* and call a factory function such as qrcodegen_makeNumeric().
* The low-level way to create a segment is to custom-make the bit buffer
* and initialize a qrcodegen_Segment struct with appropriate values.
* Even in the most favorable conditions, a QR Code can only hold 7089 characters of data.
* Any segment longer than this is meaningless for the purpose of generating QR Codes.
* Moreover, the maximum allowed bit length is 32767 because
* the largest QR Code (version 40) has 31329 modules.
*/
struct qrcodegen_Segment {
// The mode indicator of this segment.
enum qrcodegen_Mode mode;
// The length of this segment's unencoded data. Measured in characters for
// numeric/alphanumeric/kanji mode, bytes for byte mode, and 0 for ECI mode.
// Always zero or positive. Not the same as the data's bit length.
int numChars;
// The data bits of this segment, packed in bitwise big endian.
// Can be null if the bit length is zero.
uint8_t *data;
// The number of valid data bits used in the buffer. Requires
// 0 <= bitLength <= 32767, and bitLength <= (capacity of data array) * 8.
// The character count (numChars) must agree with the mode and the bit buffer length.
int bitLength;
};
/*---- Macro constants and functions ----*/
#define qrcodegen_VERSION_MIN 1 // The minimum version number supported in the QR Code Model 2 standard
#define qrcodegen_VERSION_MAX 40 // The maximum version number supported in the QR Code Model 2 standard
// Calculates the number of bytes needed to store any QR Code up to and including the given version number,
// as a compile-time constant. For example, 'uint8_t buffer[qrcodegen_BUFFER_LEN_FOR_VERSION(25)];'
// can store any single QR Code from version 1 to 25 (inclusive). The result fits in an int (or int16).
// Requires qrcodegen_VERSION_MIN <= n <= qrcodegen_VERSION_MAX.
#define qrcodegen_BUFFER_LEN_FOR_VERSION(n) ((((n) * 4 + 17) * ((n) * 4 + 17) + 7) / 8 + 1)
// The worst-case number of bytes needed to store one QR Code, up to and including
// version 40. This value equals 3918, which is just under 4 kilobytes.
// Use this more convenient value to avoid calculating tighter memory bounds for buffers.
#define qrcodegen_BUFFER_LEN_MAX qrcodegen_BUFFER_LEN_FOR_VERSION(qrcodegen_VERSION_MAX)
/*---- Functions (high level) to generate QR Codes ----*/
/*
* Encodes the given text string to a QR Code, returning true if encoding succeeded.
* If the data is too long to fit in any version in the given range
* at the given ECC level, then false is returned.
* - The input text must be encoded in UTF-8 and contain no NULs.
* - The variables ecl and mask must correspond to enum constant values.
* - Requires 1 <= minVersion <= maxVersion <= 40.
* - The arrays tempBuffer and qrcode must each have a length
* of at least qrcodegen_BUFFER_LEN_FOR_VERSION(maxVersion).
* - After the function returns, tempBuffer contains no useful data.
* - If successful, the resulting QR Code may use numeric,
* alphanumeric, or byte mode to encode the text.
* - In the most optimistic case, a QR Code at version 40 with low ECC
* can hold any UTF-8 string up to 2953 bytes, or any alphanumeric string
* up to 4296 characters, or any digit string up to 7089 characters.
* These numbers represent the hard upper limit of the QR Code standard.
* - Please consult the QR Code specification for information on
* data capacities per version, ECC level, and text encoding mode.
*/
bool qrcodegen_encodeText(const char *text, uint8_t tempBuffer[], uint8_t qrcode[],
enum qrcodegen_Ecc ecl, int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl);
/*
* Encodes the given binary data to a QR Code, returning true if encoding succeeded.
* If the data is too long to fit in any version in the given range
* at the given ECC level, then false is returned.
* - The input array range dataAndTemp[0 : dataLen] should normally be
* valid UTF-8 text, but is not required by the QR Code standard.
* - The variables ecl and mask must correspond to enum constant values.
* - Requires 1 <= minVersion <= maxVersion <= 40.
* - The arrays dataAndTemp and qrcode must each have a length
* of at least qrcodegen_BUFFER_LEN_FOR_VERSION(maxVersion).
* - After the function returns, the contents of dataAndTemp may have changed,
* and does not represent useful data anymore.
* - If successful, the resulting QR Code will use byte mode to encode the data.
* - In the most optimistic case, a QR Code at version 40 with low ECC can hold any byte
* sequence up to length 2953. This is the hard upper limit of the QR Code standard.
* - Please consult the QR Code specification for information on
* data capacities per version, ECC level, and text encoding mode.
*/
bool qrcodegen_encodeBinary(uint8_t dataAndTemp[], size_t dataLen, uint8_t qrcode[],
enum qrcodegen_Ecc ecl, int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl);
/*---- Functions (low level) to generate QR Codes ----*/
/*
* Renders a QR Code representing the given segments at the given error correction level.
* The smallest possible QR Code version is automatically chosen for the output. Returns true if
* QR Code creation succeeded, or false if the data is too long to fit in any version. The ECC level
* of the result may be higher than the ecl argument if it can be done without increasing the version.
* This function allows the user to create a custom sequence of segments that switches
* between modes (such as alphanumeric and byte) to encode text in less space.
* This is a low-level API; the high-level API is qrcodegen_encodeText() and qrcodegen_encodeBinary().
* To save memory, the segments' data buffers can alias/overlap tempBuffer, and will
* result in them being clobbered, but the QR Code output will still be correct.
* But the qrcode array must not overlap tempBuffer or any segment's data buffer.
*/
bool qrcodegen_encodeSegments(const struct qrcodegen_Segment segs[], size_t len,
enum qrcodegen_Ecc ecl, uint8_t tempBuffer[], uint8_t qrcode[]);
/*
* Renders a QR Code representing the given segments with the given encoding parameters.
* Returns true if QR Code creation succeeded, or false if the data is too long to fit in the range of versions.
* The smallest possible QR Code version within the given range is automatically
* chosen for the output. Iff boostEcl is true, then the ECC level of the result
* may be higher than the ecl argument if it can be done without increasing the
* version. The mask number is either between 0 to 7 (inclusive) to force that
* mask, or -1 to automatically choose an appropriate mask (which may be slow).
* This function allows the user to create a custom sequence of segments that switches
* between modes (such as alphanumeric and byte) to encode text in less space.
* This is a low-level API; the high-level API is qrcodegen_encodeText() and qrcodegen_encodeBinary().
* To save memory, the segments' data buffers can alias/overlap tempBuffer, and will
* result in them being clobbered, but the QR Code output will still be correct.
* But the qrcode array must not overlap tempBuffer or any segment's data buffer.
*/
bool qrcodegen_encodeSegmentsAdvanced(const struct qrcodegen_Segment segs[], size_t len, enum qrcodegen_Ecc ecl,
int minVersion, int maxVersion, int mask, bool boostEcl, uint8_t tempBuffer[], uint8_t qrcode[]);
/*
* Tests whether the given string can be encoded as a segment in alphanumeric mode.
* A string is encodable iff each character is in the following set: 0 to 9, A to Z
* (uppercase only), space, dollar, percent, asterisk, plus, hyphen, period, slash, colon.
*/
bool qrcodegen_isAlphanumeric(const char *text);
/*
* Tests whether the given string can be encoded as a segment in numeric mode.
* A string is encodable iff each character is in the range 0 to 9.
*/
bool qrcodegen_isNumeric(const char *text);
/*
* Returns the number of bytes (uint8_t) needed for the data buffer of a segment
* containing the given number of characters using the given mode. Notes:
* - Returns SIZE_MAX on failure, i.e. numChars > INT16_MAX or
* the number of needed bits exceeds INT16_MAX (i.e. 32767).
* - Otherwise, all valid results are in the range [0, ceil(INT16_MAX / 8)], i.e. at most 4096.
* - It is okay for the user to allocate more bytes for the buffer than needed.
* - For byte mode, numChars measures the number of bytes, not Unicode code points.
* - For ECI mode, numChars must be 0, and the worst-case number of bytes is returned.
* An actual ECI segment can have shorter data. For non-ECI modes, the result is exact.
*/
size_t qrcodegen_calcSegmentBufferSize(enum qrcodegen_Mode mode, size_t numChars);
/*
* Returns a segment representing the given binary data encoded in
* byte mode. All input byte arrays are acceptable. Any text string
* can be converted to UTF-8 bytes and encoded as a byte mode segment.
*/
struct qrcodegen_Segment qrcodegen_makeBytes(const uint8_t data[], size_t len, uint8_t buf[]);
/*
* Returns a segment representing the given string of decimal digits encoded in numeric mode.
*/
struct qrcodegen_Segment qrcodegen_makeNumeric(const char *digits, uint8_t buf[]);
/*
* Returns a segment representing the given text string encoded in alphanumeric mode.
* The characters allowed are: 0 to 9, A to Z (uppercase only), space,
* dollar, percent, asterisk, plus, hyphen, period, slash, colon.
*/
struct qrcodegen_Segment qrcodegen_makeAlphanumeric(const char *text, uint8_t buf[]);
/*
* Returns a segment representing an Extended Channel Interpretation
* (ECI) designator with the given assignment value.
*/
struct qrcodegen_Segment qrcodegen_makeEci(long assignVal, uint8_t buf[]);
/*---- Functions to extract raw data from QR Codes ----*/
/*
* Returns the side length of the given QR Code, assuming that encoding succeeded.
* The result is in the range [21, 177]. Note that the length of the array buffer
* is related to the side length - every 'uint8_t qrcode[]' must have length at least
* qrcodegen_BUFFER_LEN_FOR_VERSION(version), which equals ceil(size^2 / 8 + 1).
*/
int qrcodegen_getSize(const uint8_t qrcode[]);
/*
* Returns the color of the module (pixel) at the given coordinates, which is false
* for white or true for black. The top left corner has the coordinates (x=0, y=0).
* If the given coordinates are out of bounds, then false (white) is returned.
*/
bool qrcodegen_getModule(const uint8_t qrcode[], int x, int y);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,181 @@
/**
* @file lv_rlottie.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_rlottie.h"
#if LV_USE_RLOTTIE
/*********************
* DEFINES
*********************/
#define MY_CLASS &lv_rlottie_class
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_rlottie_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_rlottie_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void next_frame_task_cb(lv_timer_t* t);
/**********************
* STATIC VARIABLES
**********************/
const lv_obj_class_t lv_rlottie_class = {
.constructor_cb = lv_rlottie_constructor,
.destructor_cb = lv_rlottie_destructor,
.instance_size = sizeof(lv_rlottie_t),
.base_class = &lv_img_class
};
static lv_coord_t create_width;
static lv_coord_t create_height;
static const char* rlottie_desc_create;
static const char* path_create;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_obj_t* lv_rlottie_create_from_file(lv_obj_t* parent, lv_coord_t width, lv_coord_t height, const char* path)
{
create_width = width;
create_height = height;
path_create = path;
rlottie_desc_create = NULL;
LV_LOG_INFO("begin");
lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
lv_obj_class_init_obj(obj);
return obj;
}
lv_obj_t* lv_rlottie_create_from_raw(lv_obj_t* parent, lv_coord_t width, lv_coord_t height, const char* rlottie_desc)
{
create_width = width;
create_height = height;
rlottie_desc_create = rlottie_desc;
path_create = NULL;
LV_LOG_INFO("begin");
lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
lv_obj_class_init_obj(obj);
return obj;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_rlottie_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
lv_rlottie_t * rlottie = (lv_rlottie_t *) obj;
if(rlottie_desc_create) {
rlottie->animation = lottie_animation_from_data(rlottie_desc_create, rlottie_desc_create,"");
}
else if(path_create) {
rlottie->animation = lottie_animation_from_file(path_create);
}
if (rlottie->animation == NULL) {
LV_LOG_WARN("The aniamtion can't be opened");
return;
}
rlottie->total_frames = lottie_animation_get_totalframe(rlottie->animation);
rlottie->framerate = (size_t)lottie_animation_get_framerate(rlottie->animation);
rlottie->current_frame = 0;
rlottie->scanline_width = create_width * LV_COLOR_DEPTH / 8;
size_t allocaled_buf_size = (create_width * create_height * LV_COLOR_DEPTH / 8);
rlottie->allocated_buf = lv_mem_alloc(allocaled_buf_size);
if (rlottie->allocated_buf != NULL)
{
rlottie->allocated_buffer_size = allocaled_buf_size;
memset(rlottie->allocated_buf, 0, allocaled_buf_size);
}
rlottie->imgdsc.header.always_zero = 0;
rlottie->imgdsc.header.cf = LV_IMG_CF_TRUE_COLOR_ALPHA;
rlottie->imgdsc.header.h = create_height;
rlottie->imgdsc.header.w = create_width;
rlottie->imgdsc.data = (void*)rlottie->allocated_buf;
rlottie->imgdsc.data_size = allocaled_buf_size;
lv_img_set_src(obj, &rlottie->imgdsc);
rlottie->task = lv_timer_create(next_frame_task_cb, 1000 / rlottie->framerate, obj);
lv_obj_update_layout(obj);
}
static void lv_rlottie_destructor(const lv_obj_class_t * class_p, lv_obj_t* obj)
{
lv_rlottie_t * rlottie = (lv_rlottie_t *) obj;
if (rlottie->animation) {
lottie_animation_destroy(rlottie->animation);
rlottie->animation = 0;
rlottie->current_frame = 0;
rlottie->framerate = 0;
rlottie->scanline_width = 0;
rlottie->total_frames = 0;
}
if (rlottie->task){
lv_timer_del(rlottie->task);
rlottie->task = NULL;
}
if (rlottie->allocated_buf) {
lv_mem_free(rlottie->allocated_buf);
rlottie->allocated_buf = NULL;
rlottie->allocated_buffer_size = 0;
}
}
static void next_frame_task_cb(lv_timer_t* t)
{
lv_obj_t* obj = t->user_data;
lv_rlottie_t * rlottie = (lv_rlottie_t *) obj;
if (rlottie->current_frame == rlottie->total_frames)
rlottie->current_frame = 0;
else
++rlottie->current_frame;
lottie_animation_render(
rlottie->animation,
rlottie->current_frame,
rlottie->allocated_buf,
rlottie->imgdsc.header.w,
rlottie->imgdsc.header.h,
rlottie->scanline_width
);
lv_obj_invalidate(obj);
}
#endif /*LV_USE_RLOTTIE*/

View File

@@ -0,0 +1,61 @@
/**
* @file lv_rlottie.h
*
*/
#ifndef LV_RLOTTIE_H
#define LV_RLOTTIE_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../lvgl.h"
#if LV_USE_RLOTTIE
#include <rlottie_capi.h>
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_img_t img_ext;
Lottie_Animation* animation;
lv_timer_t* task;
lv_img_dsc_t imgdsc;
size_t total_frames;
size_t current_frame;
size_t framerate;
uint32_t* allocated_buf;
size_t allocated_buffer_size;
size_t scanline_width;
}lv_rlottie_t;
extern const lv_obj_class_t lv_rlottie_class;
/**********************
* GLOBAL PROTOTYPES
**********************/
lv_obj_t * lv_rlottie_create_from_file(lv_obj_t * parent,lv_coord_t width, lv_coord_t height, const char * path);
lv_obj_t* lv_rlottie_create_from_raw(lv_obj_t* parent, lv_coord_t width, lv_coord_t height, const char* rlottie_desc);
/**********************
* MACROS
**********************/
#endif /*LV_USE_RLOTTIE*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_RLOTTIE_H*/

View File

@@ -0,0 +1,904 @@
/**
* @file lv_sjpg.c
*
*/
/*----------------------------------------------------------------------------------------------------------------------------------
/ Added normal JPG support [7/10/2020]
/ ----------
/ SJPEG is a custom created modified JPEG file format for small embedded platforms.
/ It will contain multiple JPEG fragments all embedded into a single file with a custom header.
/ This makes JPEG decoding easier using any JPEG library. Overall file size will be almost
/ similar to the parent jpeg file. We can generate sjpeg from any jpeg using a python script
/ provided along with this project.
/ (by vinodstanur | 2020 )
/ SJPEG FILE STRUCTURE
/ --------------------------------------------------------------------------------------------------------------------------------
/ Bytes | Value |
/ --------------------------------------------------------------------------------------------------------------------------------
/
/ 0 - 7 | "_SJPG__" followed by '\0'
/
/ 8 - 13 | "V1.00" followed by '\0' [VERSION OF SJPG FILE for future compatibiliby]
/
/ 14 - 15 | X_RESOLUTION (width) [little endian]
/
/ 16 - 17 | Y_RESOLUTION (height) [little endian]
/
/ 18 - 19 | TOTAL_FRAMES inside sjpeg [little endian]
/
/ 20 - 21 | JPEG BLOCK WIDTH (16 normally) [little endian]
/
/ 22 - [(TOTAL_FRAMES*2 )] | SIZE OF EACH JPEG SPLIT FRAGMENTS (FRAME_INFO_ARRAY)
/
/ SJPEG data | Each JPEG frame can be extracted from SJPEG data by parsing the FRAME_INFO_ARRAY one time.
/
/----------------------------------------------------------------------------------------------------------------------------------
/ JPEG DECODER
/ ------------
/ We are using TJpgDec - Tiny JPEG Decompressor library from ELM-CHAN for decoding each split-jpeg fragments.
/ The tjpgd.c and tjpgd.h is not modified and those are used as it is. So if any update comes for the tiny-jpeg,
/ just replace those files with updated files.
/---------------------------------------------------------------------------------------------------------------------------------*/
/*********************
* INCLUDES
*********************/
#include "../../../lvgl.h"
#if LV_USE_SJPG
#include "tjpgd.h"
#include "lv_sjpg.h"
#include "../../../misc/lv_fs.h"
/*********************
* DEFINES
*********************/
#define TJPGD_WORKBUFF_SIZE 4096 //Recommended by TJPGD libray
//NEVER EDIT THESE OFFSET VALUES
#define SJPEG_VERSION_OFFSET 8
#define SJPEG_X_RES_OFFSET 14
#define SJPEG_y_RES_OFFSET 16
#define SJPEG_TOTAL_FRAMES_OFFSET 18
#define SJPEG_BLOCK_WIDTH_OFFSET 20
#define SJPEG_FRAME_INFO_ARRAY_OFFSET 22
/**********************
* TYPEDEFS
**********************/
enum io_source_type {
SJPEG_IO_SOURCE_C_ARRAY,
SJPEG_IO_SOURCE_DISK,
};
typedef struct {
enum io_source_type type;
lv_fs_file_t lv_file;
uint8_t* img_cache_buff;
int img_cache_x_res;
int img_cache_y_res;
uint8_t *raw_sjpg_data; //Used when type==SJPEG_IO_SOURCE_C_ARRAY.
uint32_t raw_sjpg_data_size; //Num bytes pointed to by raw_sjpg_data.
uint32_t raw_sjpg_data_next_read_pos; //Used for all types.
} io_source_t;
typedef struct {
uint8_t *sjpeg_data;
uint32_t sjpeg_data_size;
int sjpeg_x_res;
int sjpeg_y_res;
int sjpeg_total_frames;
int sjpeg_single_frame_height;
int sjpeg_cache_frame_index;
uint8_t **frame_base_array; //to save base address of each split frames upto sjpeg_total_frames.
int *frame_base_offset; //to save base offset for fseek
uint8_t *frame_cache;
uint8_t* workb; //JPG work buffer for jpeg library
JDEC *tjpeg_jd;
io_source_t io;
} SJPEG;
/**********************
* STATIC PROTOTYPES
**********************/
static lv_res_t decoder_info( lv_img_decoder_t * decoder, const void * src, lv_img_header_t * header );
static lv_res_t decoder_open( lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc );
static lv_res_t decoder_read_line( lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc, lv_coord_t x,lv_coord_t y, lv_coord_t len, uint8_t * buf );
static void decoder_close( lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc );
static size_t input_func ( JDEC* jd, uint8_t* buff, size_t ndata );
static int is_jpg( const uint8_t *raw_data );
static void lv_sjpg_cleanup( SJPEG* sjpeg );
static void lv_sjpg_free( SJPEG* sjpeg );
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_split_jpeg_init( void )
{
lv_img_decoder_t* dec = lv_img_decoder_create( );
lv_img_decoder_set_info_cb( dec, decoder_info );
lv_img_decoder_set_open_cb( dec, decoder_open );
lv_img_decoder_set_close_cb( dec, decoder_close );
lv_img_decoder_set_read_line_cb( dec, decoder_read_line );
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
* Get info about an SJPG / JPG image
* @param decoder pointer to the decoder where this function belongs
* @param src can be file name or pointer to a C array
* @param header store the info here
* @return LV_RES_OK: no error; LV_RES_INV: can't get the info
*/
static lv_res_t decoder_info( lv_img_decoder_t * decoder, const void * src, lv_img_header_t * header )
{
LV_UNUSED(decoder);
/*Check whether the type `src` is known by the decoder*/
/* Read the SJPG/JPG header and find `width` and `height` */
lv_img_src_t src_type = lv_img_src_get_type(src); /*Get the source type*/
lv_res_t ret = LV_RES_OK;
if(src_type == LV_IMG_SRC_VARIABLE) {
uint8_t *raw_sjpeg_data = (uint8_t *)((lv_img_dsc_t * )src)->data;
const uint32_t raw_sjpeg_data_size = ((lv_img_dsc_t *)src)->data_size;
if(!strncmp((char *)raw_sjpeg_data, "_SJPG__", strlen("_SJPG__") )) {
raw_sjpeg_data += 14; //seek to res info ... refer sjpeg format
header->always_zero = 0;
header->cf = LV_IMG_CF_RAW;
header->w = *raw_sjpeg_data++;
header->w |= *raw_sjpeg_data++ << 8;
header->h = *raw_sjpeg_data++;
header->h |= *raw_sjpeg_data++ << 8;
return ret;
} else if( is_jpg( raw_sjpeg_data ) == true ) {
header->always_zero = 0;
header->cf = LV_IMG_CF_RAW;
uint8_t *workb_temp = lv_mem_alloc( TJPGD_WORKBUFF_SIZE );
if(!workb_temp) return LV_RES_INV;
io_source_t io_source_temp;
io_source_temp.type = SJPEG_IO_SOURCE_C_ARRAY;
io_source_temp.raw_sjpg_data = raw_sjpeg_data;
io_source_temp.raw_sjpg_data_size = raw_sjpeg_data_size;
io_source_temp.raw_sjpg_data_next_read_pos = 0;
JDEC jd_tmp;
JRESULT rc = jd_prepare( &jd_tmp, input_func, workb_temp, (size_t)TJPGD_WORKBUFF_SIZE, &io_source_temp);
if(rc == JDR_OK ) {
header->w = jd_tmp.width;
header->h = jd_tmp.height;
} else {
ret = LV_RES_INV;
goto end;
}
end:
lv_mem_free(workb_temp);
return ret;
}
}
else if( src_type == LV_IMG_SRC_FILE ) {
const char * fn = src;
if(!strcmp(&fn[strlen(fn) - 5], ".sjpg")) {
uint8_t buff[22];
memset(buff, 0, sizeof(buff));
lv_fs_file_t file;
lv_fs_res_t res = lv_fs_open(&file , fn, LV_FS_MODE_RD);
if(res != LV_FS_RES_OK) return 78;
uint32_t rn;
res = lv_fs_read(&file, buff, 8, &rn);
if(res != LV_FS_RES_OK || rn != 8) {
lv_fs_close(&file);
return LV_RES_INV;
}
if(strcmp((char *)buff, "_SJPG__") == 0 ) {
lv_fs_seek(&file, 14, LV_FS_SEEK_SET);
res = lv_fs_read(&file, buff, 4, &rn);
if(res != LV_FS_RES_OK || rn != 4 ) {
lv_fs_close(&file);
return LV_RES_INV;
}
header->always_zero = 0;
header->cf = LV_IMG_CF_RAW;
uint8_t *raw_sjpeg_data = buff;
header->w = *raw_sjpeg_data++;
header->w |= *raw_sjpeg_data++ << 8;
header->h = *raw_sjpeg_data++;
header->h |= *raw_sjpeg_data++ << 8;
lv_fs_close(&file);
return LV_RES_OK;
}
} else if(!strcmp(&fn[strlen(fn) - 4], ".jpg")) {
lv_fs_file_t file;
lv_fs_res_t res = lv_fs_open(&file , fn, LV_FS_MODE_RD);
if(res != LV_FS_RES_OK) return 78;
uint8_t *workb_temp = lv_mem_alloc( TJPGD_WORKBUFF_SIZE );
if(!workb_temp) {
lv_fs_close(&file);
return LV_RES_INV;
}
io_source_t io_source_temp;
io_source_temp.type = SJPEG_IO_SOURCE_DISK;
io_source_temp.raw_sjpg_data_next_read_pos = 0;
io_source_temp.img_cache_buff = NULL;
io_source_temp.lv_file = file;
JDEC jd_tmp;
JRESULT rc = jd_prepare( &jd_tmp, input_func, workb_temp, (size_t)TJPGD_WORKBUFF_SIZE, &io_source_temp);
lv_mem_free(workb_temp);
lv_fs_close(&file);
if(rc == JDR_OK ) {
header->always_zero = 0;
header->cf = LV_IMG_CF_RAW;
header->w = jd_tmp.width;
header->h = jd_tmp.height;
return LV_RES_OK;
}
}
}
return LV_RES_INV;
}
static int img_data_cb( JDEC* jd, void* data, JRECT* rect )
{
io_source_t *io = jd->device;
uint8_t *cache = io->img_cache_buff;
const int xres = io->img_cache_x_res;
uint8_t *buf = data;
const int INPUT_PIXEL_SIZE = 3;
const int row_width = rect->right - rect->left + 1; // Row width in pixels.
const int row_size = row_width * INPUT_PIXEL_SIZE; // Row size (bytes).
for( int y = rect->top; y <= rect->bottom; y++ ) {
int row_offset = y * xres * INPUT_PIXEL_SIZE + rect->left * INPUT_PIXEL_SIZE;
memcpy( cache + row_offset, buf, row_size );
buf += row_size;
}
return 1;
}
static size_t input_func ( JDEC* jd, uint8_t* buff, size_t ndata )
{
io_source_t *io = jd->device;
if(!io) return 0;
if(io->type == SJPEG_IO_SOURCE_C_ARRAY) {
const uint32_t bytes_left = io->raw_sjpg_data_size - io->raw_sjpg_data_next_read_pos;
const uint32_t to_read = ndata <= bytes_left ? (uint32_t)ndata : bytes_left;
if (to_read == 0)
return 0;
if(buff) {
memcpy(buff, io->raw_sjpg_data + io->raw_sjpg_data_next_read_pos, to_read);
}
io->raw_sjpg_data_next_read_pos += to_read;
return to_read;
}
else if(io->type == SJPEG_IO_SOURCE_DISK) {
lv_fs_file_t* lv_file_p = &(io->lv_file);
if( buff ) {
uint32_t rn = 0;
lv_fs_read(lv_file_p, buff, (uint32_t)ndata, &rn);
return rn;
} else {
uint32_t pos;
lv_fs_tell(lv_file_p, &pos);
lv_fs_seek(lv_file_p, (uint32_t)(ndata + pos), LV_FS_SEEK_SET);
return ndata;
}
}
return 0;
}
/**
* Open SJPG image and return the decided image
* @param decoder pointer to the decoder where this function belongs
* @param dsc pointer to a descriptor which describes this decoding session
* @return LV_RES_OK: no error; LV_RES_INV: can't get the info
*/
static lv_res_t decoder_open( lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc )
{
LV_UNUSED(decoder);
lv_res_t lv_ret = LV_RES_OK;
if(dsc->src_type == LV_IMG_SRC_VARIABLE) {
uint8_t *data;
SJPEG* sjpeg = ( SJPEG* ) dsc->user_data;
if( sjpeg == NULL ) {
sjpeg = lv_mem_alloc( sizeof( SJPEG ) );
if( !sjpeg ) return LV_RES_INV;
memset(sjpeg, 0, sizeof(SJPEG));
dsc->user_data = sjpeg;
sjpeg->sjpeg_data = (uint8_t *)( ( lv_img_dsc_t* )(dsc->src) )->data;
sjpeg->sjpeg_data_size = ( ( lv_img_dsc_t* )(dsc->src) )->data_size;
}
if( !strncmp( ( char * ) sjpeg->sjpeg_data, "_SJPG__", strlen("_SJPG__") ) ) {
data = sjpeg->sjpeg_data;
data += 14;
sjpeg->sjpeg_x_res = *data++;
sjpeg->sjpeg_x_res |= *data++ << 8;
sjpeg->sjpeg_y_res = *data++;
sjpeg->sjpeg_y_res |= *data++ << 8;
sjpeg->sjpeg_total_frames = *data++;
sjpeg->sjpeg_total_frames |= *data++ << 8;
sjpeg->sjpeg_single_frame_height = *data++;
sjpeg->sjpeg_single_frame_height |= *data++ << 8;
sjpeg->frame_base_array = lv_mem_alloc( sizeof(uint8_t *) * sjpeg->sjpeg_total_frames );
if( ! sjpeg->frame_base_array ) {
lv_sjpg_cleanup( sjpeg );
sjpeg = NULL;
return LV_RES_INV;
}
sjpeg->frame_base_offset = NULL;
uint8_t *img_frame_base = data + sjpeg->sjpeg_total_frames *2;
sjpeg->frame_base_array[0] = img_frame_base;
for( int i = 1; i < sjpeg->sjpeg_total_frames; i++ ) {
int offset = *data++;
offset |= *data++ <<8;
sjpeg->frame_base_array[i] = sjpeg->frame_base_array[i-1] + offset;
}
sjpeg->sjpeg_cache_frame_index = -1;
sjpeg->frame_cache = (void *)lv_mem_alloc( sjpeg->sjpeg_x_res * sjpeg->sjpeg_single_frame_height * 3/*2*/ );
if( ! sjpeg->frame_cache ) {
lv_sjpg_cleanup( sjpeg );
sjpeg = NULL;
return LV_RES_INV;
}
sjpeg->io.img_cache_buff = sjpeg->frame_cache;
sjpeg->io.img_cache_x_res = sjpeg->sjpeg_x_res;
sjpeg->workb = lv_mem_alloc( TJPGD_WORKBUFF_SIZE );
if( ! sjpeg->workb ) {
lv_sjpg_cleanup( sjpeg );
sjpeg = NULL;
return LV_RES_INV;
}
sjpeg->tjpeg_jd = lv_mem_alloc( sizeof( JDEC ) );
if( ! sjpeg->tjpeg_jd ) {
lv_sjpg_cleanup( sjpeg );
sjpeg = NULL;
return LV_RES_INV;
}
sjpeg->io.type = SJPEG_IO_SOURCE_C_ARRAY;
sjpeg->io.lv_file.file_d = NULL;
dsc->img_data = NULL;
return lv_ret;
}
else if( is_jpg( sjpeg->sjpeg_data ) == true ) {
uint8_t *workb_temp = lv_mem_alloc( TJPGD_WORKBUFF_SIZE );
if( ! workb_temp ) {
lv_sjpg_cleanup( sjpeg );
sjpeg = NULL;
return LV_RES_INV;
}
io_source_t io_source_temp;
io_source_temp.type = SJPEG_IO_SOURCE_C_ARRAY;
io_source_temp.raw_sjpg_data = sjpeg->sjpeg_data;
io_source_temp.raw_sjpg_data_size = sjpeg->sjpeg_data_size;
io_source_temp.raw_sjpg_data_next_read_pos = 0;
JDEC jd_tmp;
JRESULT rc = jd_prepare( &jd_tmp, input_func, workb_temp, (size_t)TJPGD_WORKBUFF_SIZE, &io_source_temp);
lv_mem_free(workb_temp);
if(rc == JDR_OK ) {
sjpeg->sjpeg_x_res = jd_tmp.width;
sjpeg->sjpeg_y_res = jd_tmp.height;
sjpeg->sjpeg_total_frames = 1;
sjpeg->sjpeg_single_frame_height = jd_tmp.height;
sjpeg->frame_base_array = lv_mem_alloc( sizeof(uint8_t *) * sjpeg->sjpeg_total_frames );
if( ! sjpeg->frame_base_array ) {
lv_sjpg_cleanup( sjpeg );
sjpeg = NULL;
return LV_RES_INV;
}
sjpeg->frame_base_offset = NULL;
uint8_t *img_frame_base = sjpeg->sjpeg_data;
sjpeg->frame_base_array[0] = img_frame_base;
sjpeg->sjpeg_cache_frame_index = -1;
sjpeg->frame_cache = (void *)lv_mem_alloc( sjpeg->sjpeg_x_res * sjpeg->sjpeg_single_frame_height * 3 );
if( ! sjpeg->frame_cache ) {
lv_sjpg_cleanup( sjpeg );
sjpeg = NULL;
return LV_RES_INV;
}
sjpeg->io.img_cache_buff = sjpeg->frame_cache;
sjpeg->io.img_cache_x_res = sjpeg->sjpeg_x_res;
sjpeg->workb = lv_mem_alloc( TJPGD_WORKBUFF_SIZE );
if( ! sjpeg->workb ) {
lv_sjpg_cleanup( sjpeg );
sjpeg = NULL;
return LV_RES_INV;
}
sjpeg->tjpeg_jd = lv_mem_alloc( sizeof( JDEC ) );
if( ! sjpeg->tjpeg_jd ) {
lv_sjpg_cleanup( sjpeg );
sjpeg = NULL;
return LV_RES_INV;
}
sjpeg->io.type = SJPEG_IO_SOURCE_C_ARRAY;
sjpeg->io.lv_file.file_d = NULL;
dsc->img_data = NULL;
return lv_ret;
} else {
lv_ret = LV_RES_INV;
goto end;
}
end:
lv_mem_free(workb_temp);
return lv_ret;
}
}
else if(dsc->src_type == LV_IMG_SRC_FILE) {
/* If all fine, then the file will be kept open */
const char * fn = dsc->src;
uint8_t *data;
if(!strcmp(&fn[strlen(fn) - 5], ".sjpg")) {
uint8_t buff[22];
memset(buff, 0, sizeof(buff));
lv_fs_file_t lv_file;
lv_fs_res_t res = lv_fs_open(&lv_file , fn, LV_FS_MODE_RD);
if(res != LV_FS_RES_OK) {
return 78;
}
uint32_t rn;
res = lv_fs_read(&lv_file, buff, 22, &rn);
if(res != LV_FS_RES_OK || rn != 22 ) {
lv_fs_close(&lv_file);
return LV_RES_INV;
}
if(strcmp((char *)buff, "_SJPG__") == 0 ) {
SJPEG* sjpeg = ( SJPEG* ) dsc->user_data;
if(sjpeg == NULL) {
sjpeg = lv_mem_alloc(sizeof(SJPEG));
if( ! sjpeg ) {
lv_fs_close(&lv_file);
return LV_RES_INV;
}
memset(sjpeg, 0, sizeof(SJPEG));
dsc->user_data = sjpeg;
sjpeg->sjpeg_data = (uint8_t *)( (lv_img_dsc_t* )(dsc->src) )->data;
sjpeg->sjpeg_data_size = ( (lv_img_dsc_t* )(dsc->src) )->data_size;
}
data = buff;
data += 14;
sjpeg->sjpeg_x_res = *data++;
sjpeg->sjpeg_x_res |= *data++ << 8;
sjpeg->sjpeg_y_res = *data++;
sjpeg->sjpeg_y_res |= *data++ << 8;
sjpeg->sjpeg_total_frames = *data++;
sjpeg->sjpeg_total_frames |= *data++ << 8;
sjpeg->sjpeg_single_frame_height = *data++;
sjpeg->sjpeg_single_frame_height |= *data++ << 8;
sjpeg->frame_base_array = NULL;//lv_mem_alloc( sizeof(uint8_t *) * sjpeg->sjpeg_total_frames );
sjpeg->frame_base_offset = lv_mem_alloc( sizeof(int) * sjpeg->sjpeg_total_frames );
if( ! sjpeg->frame_base_offset ) {
lv_fs_close(&lv_file);
lv_sjpg_cleanup(sjpeg);
return LV_RES_INV;
}
int img_frame_start_offset = (SJPEG_FRAME_INFO_ARRAY_OFFSET + sjpeg->sjpeg_total_frames *2);
sjpeg->frame_base_offset[0] = img_frame_start_offset; //pointer used to save integer for now...
for( int i = 1; i < sjpeg->sjpeg_total_frames; i++ ) {
res = lv_fs_read(&lv_file, buff, 2, &rn);
if(res != LV_FS_RES_OK || rn != 2 ) {
lv_fs_close(&lv_file);
return LV_RES_INV;
}
data = buff;
int offset = *data++;
offset |= *data++ <<8;
sjpeg->frame_base_offset[i] = sjpeg->frame_base_offset[i-1] + offset;
}
sjpeg->sjpeg_cache_frame_index = -1; //INVALID AT BEGINNING for a forced compare mismatch at first time.
sjpeg->frame_cache = (void *)lv_mem_alloc( sjpeg->sjpeg_x_res * sjpeg->sjpeg_single_frame_height * 3 );
if( ! sjpeg->frame_cache ) {
lv_fs_close(&lv_file);
lv_sjpg_cleanup(sjpeg);
return LV_RES_INV;
}
sjpeg->io.img_cache_buff = sjpeg->frame_cache;
sjpeg->io.img_cache_x_res = sjpeg->sjpeg_x_res;
sjpeg->workb = lv_mem_alloc( TJPGD_WORKBUFF_SIZE );
if( ! sjpeg->workb ) {
lv_fs_close(&lv_file);
lv_sjpg_cleanup(sjpeg);
return LV_RES_INV;
}
sjpeg->tjpeg_jd = lv_mem_alloc( sizeof( JDEC ) );
if( ! sjpeg->tjpeg_jd ) {
lv_fs_close(&lv_file);
lv_sjpg_cleanup(sjpeg);
return LV_RES_INV;
}
sjpeg->io.type = SJPEG_IO_SOURCE_DISK;
sjpeg->io.lv_file = lv_file;
dsc->img_data = NULL;
return LV_RES_OK;
}
}
else if( !strcmp(&fn[strlen(fn) - 4], ".jpg" ) ) {
lv_fs_file_t lv_file;
lv_fs_res_t res = lv_fs_open( &lv_file , fn, LV_FS_MODE_RD );
if(res != LV_FS_RES_OK) {
return LV_RES_INV;
}
SJPEG* sjpeg = ( SJPEG* ) dsc->user_data;
if(sjpeg == NULL) {
sjpeg = lv_mem_alloc( sizeof( SJPEG ) );
if( ! sjpeg ) {
lv_fs_close( &lv_file );
return LV_RES_INV;
}
memset(sjpeg, 0, sizeof(SJPEG));
dsc->user_data = sjpeg;
sjpeg->sjpeg_data = (uint8_t *)( (lv_img_dsc_t* )(dsc->src) )->data;
sjpeg->sjpeg_data_size = ( (lv_img_dsc_t *)(dsc->src) )->data_size;
}
uint8_t *workb_temp = lv_mem_alloc( TJPGD_WORKBUFF_SIZE );
if( ! workb_temp ) {
lv_fs_close(&lv_file);
lv_sjpg_cleanup(sjpeg);
return LV_RES_INV;
}
io_source_t io_source_temp;
io_source_temp.type = SJPEG_IO_SOURCE_DISK;
io_source_temp.raw_sjpg_data_next_read_pos = 0;
io_source_temp.img_cache_buff = NULL;
io_source_temp.lv_file = lv_file;
JDEC jd_tmp;
JRESULT rc = jd_prepare( &jd_tmp, input_func, workb_temp, (size_t)TJPGD_WORKBUFF_SIZE, &io_source_temp);
lv_mem_free(workb_temp);
if(rc == JDR_OK ) {
sjpeg->sjpeg_x_res = jd_tmp.width;
sjpeg->sjpeg_y_res = jd_tmp.height;
sjpeg->sjpeg_total_frames = 1;
sjpeg->sjpeg_single_frame_height = jd_tmp.height;
sjpeg->frame_base_array = NULL;
sjpeg->frame_base_offset = lv_mem_alloc( sizeof(uint8_t *) * sjpeg->sjpeg_total_frames );
if( ! sjpeg->frame_base_offset ) {
lv_fs_close(&lv_file);
lv_sjpg_cleanup(sjpeg);
return LV_RES_INV;
}
int img_frame_start_offset = 0;
sjpeg->frame_base_offset[0] = img_frame_start_offset;
sjpeg->sjpeg_cache_frame_index = -1;
sjpeg->frame_cache = (void *)lv_mem_alloc( sjpeg->sjpeg_x_res * sjpeg->sjpeg_single_frame_height * 3 );
if( ! sjpeg->frame_cache ) {
lv_fs_close(&lv_file);
lv_sjpg_cleanup(sjpeg);
return LV_RES_INV;
}
sjpeg->io.img_cache_buff = sjpeg->frame_cache;
sjpeg->io.img_cache_x_res = sjpeg->sjpeg_x_res;
sjpeg->workb = lv_mem_alloc( TJPGD_WORKBUFF_SIZE );
if( ! sjpeg->workb ) {
lv_fs_close(&lv_file);
lv_sjpg_cleanup(sjpeg);
return LV_RES_INV;
}
sjpeg->tjpeg_jd = lv_mem_alloc( sizeof( JDEC ) );
if( ! sjpeg->tjpeg_jd ) {
lv_fs_close(&lv_file);
lv_sjpg_cleanup(sjpeg);
return LV_RES_INV;
}
sjpeg->io.type = SJPEG_IO_SOURCE_DISK;
sjpeg->io.lv_file = lv_file;
dsc->img_data = NULL;
return LV_RES_OK;
} else {
if(dsc->user_data) lv_mem_free(dsc->user_data);
lv_fs_close(&lv_file);
return LV_RES_INV;
}
}
}
return LV_RES_INV;
}
/**
* Decode `len` pixels starting from the given `x`, `y` coordinates and store them in `buf`.
* Required only if the "open" function can't open the whole decoded pixel array. (dsc->img_data == NULL)
* @param decoder pointer to the decoder the function associated with
* @param dsc pointer to decoder descriptor
* @param x start x coordinate
* @param y start y coordinate
* @param len number of pixels to decode
* @param buf a buffer to store the decoded pixels
* @return LV_RES_OK: ok; LV_RES_INV: failed
*/
static lv_res_t decoder_read_line( lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc, lv_coord_t x, lv_coord_t y, lv_coord_t len, uint8_t * buf )
{
LV_UNUSED(decoder);
if(dsc->src_type == LV_IMG_SRC_VARIABLE) {
SJPEG* sjpeg = ( SJPEG* ) dsc->user_data;
JRESULT rc;
int sjpeg_req_frame_index = y / sjpeg->sjpeg_single_frame_height;
/*If line not from cache, refresh cache */
if(sjpeg_req_frame_index != sjpeg->sjpeg_cache_frame_index) {
sjpeg->io.raw_sjpg_data = sjpeg->frame_base_array[ sjpeg_req_frame_index ];
if (sjpeg_req_frame_index == (sjpeg->sjpeg_total_frames - 1)) {
/*This is the last frame. */
const uint32_t frame_offset = (uint32_t)(sjpeg->io.raw_sjpg_data - sjpeg->sjpeg_data);
sjpeg->io.raw_sjpg_data_size = sjpeg->sjpeg_data_size - frame_offset;
} else {
sjpeg->io.raw_sjpg_data_size =
(uint32_t)(sjpeg->frame_base_array[sjpeg_req_frame_index + 1] - sjpeg->io.raw_sjpg_data);
}
sjpeg->io.raw_sjpg_data_next_read_pos = 0;
rc = jd_prepare( sjpeg->tjpeg_jd, input_func, sjpeg->workb, (size_t)TJPGD_WORKBUFF_SIZE, &(sjpeg->io));
if(rc != JDR_OK ) return LV_RES_INV;
rc = jd_decomp ( sjpeg->tjpeg_jd, img_data_cb, 0);
if(rc != JDR_OK ) return LV_RES_INV;
sjpeg->sjpeg_cache_frame_index = sjpeg_req_frame_index;
}
int offset = 0;
uint8_t *cache = (uint8_t *)sjpeg->frame_cache + x*3 + ( y % sjpeg->sjpeg_single_frame_height ) * sjpeg->sjpeg_x_res*3;
#if LV_COLOR_DEPTH == 32
for( int i = 0; i < len; i++ ) {
buf[offset + 3] = 0xff;
buf[offset + 2] = *cache++;
buf[offset + 1] = *cache++;
buf[offset + 0] = *cache++;
offset += 4;
}
#elif LV_COLOR_DEPTH == 16
for( int i = 0; i < len; i++ ) {
uint16_t col_16bit = (*cache++ & 0xf8) << 8;
col_16bit |= (*cache++ & 0xFC) << 3;
col_16bit |= (*cache++ >> 3);
#if LV_BIG_ENDIAN_SYSTEM == 1
buf[offset++] = col_16bit >> 8;
buf[offset++] = col_16bit & 0xff;
#else
buf[offset++] = col_16bit & 0xff;
buf[offset++] = col_16bit >> 8;
#endif // LV_BIG_ENDIAN_SYSTEM
}
#elif LV_COLOR_DEPTH == 8
for( int i = 0; i < len; i++ ) {
uint8_t col_8bit = (*cache++ & 0xC0);
col_8bit |= (*cache++ & 0xe0) >> 2;
col_8bit |= (*cache++ & 0xe0) >> 5;
buf[offset++] = col_8bit;
}
#else
#error Unsupported LV_COLOR_DEPTH
#endif // LV_COLOR_DEPTH
return LV_RES_OK;
}
else if(dsc->src_type == LV_IMG_SRC_FILE) {
SJPEG* sjpeg = ( SJPEG* ) dsc->user_data;
JRESULT rc;
int sjpeg_req_frame_index = y / sjpeg->sjpeg_single_frame_height;
lv_fs_file_t* lv_file_p = &(sjpeg->io.lv_file);
if(!lv_file_p) goto end;
/*If line not from cache, refresh cache */
if(sjpeg_req_frame_index != sjpeg->sjpeg_cache_frame_index) {
sjpeg->io.raw_sjpg_data_next_read_pos = (int)(sjpeg->frame_base_offset [ sjpeg_req_frame_index ]);
lv_fs_seek( &(sjpeg->io.lv_file), sjpeg->io.raw_sjpg_data_next_read_pos, LV_FS_SEEK_SET);
rc = jd_prepare( sjpeg->tjpeg_jd, input_func, sjpeg->workb, (size_t)TJPGD_WORKBUFF_SIZE, &(sjpeg->io));
if(rc != JDR_OK ) return LV_RES_INV;
rc = jd_decomp ( sjpeg->tjpeg_jd, img_data_cb, 0);
if(rc != JDR_OK ) return LV_RES_INV;
sjpeg->sjpeg_cache_frame_index = sjpeg_req_frame_index;
}
int offset = 0;
uint8_t *cache = (uint8_t *)sjpeg->frame_cache + x*3 + ( y % sjpeg->sjpeg_single_frame_height ) * sjpeg->sjpeg_x_res*3;
#if LV_COLOR_DEPTH == 32
for( int i = 0; i < len; i++ ) {
buf[offset + 3] = 0xff;
buf[offset + 2] = *cache++;
buf[offset + 1] = *cache++;
buf[offset + 0] = *cache++;
offset += 4;
}
#elif LV_COLOR_DEPTH == 16
for( int i = 0; i < len; i++ ) {
uint16_t col_8bit = (*cache++ & 0xf8) << 8;
col_8bit |= (*cache++ & 0xFC) << 3;
col_8bit |= (*cache++ >> 3);
#if LV_BIG_ENDIAN_SYSTEM == 1
buf[offset++] = col_8bit >> 8;
buf[offset++] = col_8bit & 0xff;
#else
buf[offset++] = col_8bit & 0xff;
buf[offset++] = col_8bit >> 8;
#endif // LV_BIG_ENDIAN_SYSTEM
}
#elif LV_COLOR_DEPTH == 8
for( int i = 0; i < len; i++ ) {
uint8_t col_8bit = (*cache++ & 0xC0);
col_8bit |= (*cache++ & 0xe0) >> 2;
col_8bit |= (*cache++ & 0xe0) >> 5;
buf[offset++] = col_8bit;
}
#else
#error Unsupported LV_COLOR_DEPTH
#endif // LV_COLOR_DEPTH
return LV_RES_OK;
}
end:
return LV_RES_INV;
}
/**
* Free the allocated resources
* @param decoder pointer to the decoder where this function belongs
* @param dsc pointer to a descriptor which describes this decoding session
*/
static void decoder_close( lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc )
{
LV_UNUSED(decoder);
/*Free all allocated data*/
SJPEG* sjpeg = ( SJPEG* ) dsc->user_data;
if(!sjpeg) return;
switch(dsc->src_type) {
case LV_IMG_SRC_FILE:
if(sjpeg->io.lv_file.file_d) {
lv_fs_close(&(sjpeg->io.lv_file));
}
lv_sjpg_cleanup(sjpeg);
break;
case LV_IMG_SRC_VARIABLE:
lv_sjpg_cleanup(sjpeg);
break;
default:
;
}
}
static int is_jpg( const uint8_t *raw_data )
{
const uint8_t jpg_signature[] = {0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46};
return memcmp( jpg_signature, raw_data, sizeof( jpg_signature ) ) == 0;
}
static void lv_sjpg_free( SJPEG* sjpeg )
{
if(sjpeg->frame_cache) lv_mem_free(sjpeg->frame_cache);
if(sjpeg->frame_base_array) lv_mem_free(sjpeg->frame_base_array);
if(sjpeg->frame_base_offset) lv_mem_free(sjpeg->frame_base_offset);
if(sjpeg->tjpeg_jd) lv_mem_free(sjpeg->tjpeg_jd);
if(sjpeg->workb) lv_mem_free(sjpeg->workb);
}
static void lv_sjpg_cleanup( SJPEG* sjpeg )
{
if(! sjpeg ) return;
lv_sjpg_free( sjpeg );
lv_mem_free( sjpeg );
}
#endif /*LV_USE_SJPG*/

View File

@@ -0,0 +1,43 @@
/**
* @file lv_sjpg.h
*
*/
#ifndef LV_SJPEG_H
#define LV_SJPEG_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#if LV_USE_SJPG
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
void lv_split_jpeg_init(void);
/**********************
* MACROS
**********************/
#endif /*LV_USE_SJPG*/
#ifdef __cplusplus
}
#endif
#endif /* LV_SJPEG_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,93 @@
/*----------------------------------------------------------------------------/
/ TJpgDec - Tiny JPEG Decompressor R0.03 include file (C)ChaN, 2021
/----------------------------------------------------------------------------*/
#ifndef DEF_TJPGDEC
#define DEF_TJPGDEC
#ifdef __cplusplus
extern "C" {
#endif
#include "../../../lv_conf_internal.h"
#if LV_USE_SJPG
#include "tjpgdcnf.h"
#include <string.h>
#include <stdint.h>
#if JD_FASTDECODE >= 1
typedef int16_t jd_yuv_t;
#else
typedef uint8_t jd_yuv_t;
#endif
/* Error code */
typedef enum {
JDR_OK = 0, /* 0: Succeeded */
JDR_INTR, /* 1: Interrupted by output function */
JDR_INP, /* 2: Device error or wrong termination of input stream */
JDR_MEM1, /* 3: Insufficient memory pool for the image */
JDR_MEM2, /* 4: Insufficient stream input buffer */
JDR_PAR, /* 5: Parameter error */
JDR_FMT1, /* 6: Data format error (may be broken data) */
JDR_FMT2, /* 7: Right format but not supported */
JDR_FMT3 /* 8: Not supported JPEG standard */
} JRESULT;
/* Rectangular region in the output image */
typedef struct {
uint16_t left; /* Left end */
uint16_t right; /* Right end */
uint16_t top; /* Top end */
uint16_t bottom; /* Bottom end */
} JRECT;
/* Decompressor object structure */
typedef struct JDEC JDEC;
struct JDEC {
size_t dctr; /* Number of bytes available in the input buffer */
uint8_t* dptr; /* Current data read ptr */
uint8_t* inbuf; /* Bit stream input buffer */
uint8_t dbit; /* Number of bits availavble in wreg or reading bit mask */
uint8_t scale; /* Output scaling ratio */
uint8_t msx, msy; /* MCU size in unit of block (width, height) */
uint8_t qtid[3]; /* Quantization table ID of each component, Y, Cb, Cr */
uint8_t ncomp; /* Number of color components 1:grayscale, 3:color */
int16_t dcv[3]; /* Previous DC element of each component */
uint16_t nrst; /* Restart inverval */
uint16_t width, height; /* Size of the input image (pixel) */
uint8_t* huffbits[2][2]; /* Huffman bit distribution tables [id][dcac] */
uint16_t* huffcode[2][2]; /* Huffman code word tables [id][dcac] */
uint8_t* huffdata[2][2]; /* Huffman decoded data tables [id][dcac] */
int32_t* qttbl[4]; /* Dequantizer tables [id] */
#if JD_FASTDECODE >= 1
uint32_t wreg; /* Working shift register */
uint8_t marker; /* Detected marker (0:None) */
#if JD_FASTDECODE == 2
uint8_t longofs[2][2]; /* Table offset of long code [id][dcac] */
uint16_t* hufflut_ac[2]; /* Fast huffman decode tables for AC short code [id] */
uint8_t* hufflut_dc[2]; /* Fast huffman decode tables for DC short code [id] */
#endif
#endif
void* workbuf; /* Working buffer for IDCT and RGB output */
jd_yuv_t* mcubuf; /* Working buffer for the MCU */
void* pool; /* Pointer to available memory pool */
size_t sz_pool; /* Size of momory pool (bytes available) */
size_t (*infunc)(JDEC*, uint8_t*, size_t); /* Pointer to jpeg stream input function */
void* device; /* Pointer to I/O device identifiler for the session */
};
/* TJpgDec API functions */
JRESULT jd_prepare (JDEC* jd, size_t (*infunc)(JDEC*,uint8_t*,size_t), void* pool, size_t sz_pool, void* dev);
JRESULT jd_decomp (JDEC* jd, int (*outfunc)(JDEC*,void*,JRECT*), uint8_t scale);
#endif /*LV_USE_SJPG*/
#ifdef __cplusplus
}
#endif
#endif /* _TJPGDEC */

View File

@@ -0,0 +1,33 @@
/*----------------------------------------------*/
/* TJpgDec System Configurations R0.03 */
/*----------------------------------------------*/
#define JD_SZBUF 512
/* Specifies size of stream input buffer */
#define JD_FORMAT 0
/* Specifies output pixel format.
/ 0: RGB888 (24-bit/pix)
/ 1: RGB565 (16-bit/pix)
/ 2: Grayscale (8-bit/pix)
*/
#define JD_USE_SCALE 1
/* Switches output descaling feature.
/ 0: Disable
/ 1: Enable
*/
#define JD_TBLCLIP 1
/* Use table conversion for saturation arithmetic. A bit faster, but increases 1 KB of code size.
/ 0: Disable
/ 1: Enable
*/
#define JD_FASTDECODE 0
/* Optimization level
/ 0: Basic optimization. Suitable for 8/16-bit MCUs.
/ 1: + 32-bit barrel shifter. Suitable for 32-bit MCUs.
/ 2: + Table conversion for huffman decoding (wants 6 << HUFF_BIT bytes of RAM)
*/

View File

@@ -0,0 +1,82 @@
/**
* @file lv_extra.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../lvgl.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_extra_init(void)
{
#if LV_USE_FLEX
lv_flex_init();
#endif
#if LV_USE_GRID
lv_grid_init();
#endif
#if LV_USE_FS_FATFS != '\0'
lv_fs_fatfs_init();
#endif
#if LV_USE_FS_STDIO != '\0'
lv_fs_stdio_init();
#endif
#if LV_USE_FS_POSIX != '\0'
lv_fs_posix_init();
#endif
#if LV_USE_FS_WIN32 != '\0'
lv_fs_win32_init();
#endif
#if LV_USE_PNG
lv_png_init();
#endif
#if LV_USE_SJPG
lv_split_jpeg_init();
#endif
#if LV_USE_BMP
lv_bmp_init();
#endif
#if LV_USE_FREETYPE
/*Init freetype library
*Cache max 64 faces and 1 size*/
lv_freetype_init(0, 0, LV_FREETYPE_CACHE_SIZE);
#endif
}
/**********************
* STATIC FUNCTIONS
**********************/

View File

@@ -0,0 +1,42 @@
/**
* @file lv_extra.h
*
*/
#ifndef LV_EXTRA_H
#define LV_EXTRA_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Initialize the extra components
*/
void lv_extra_init(void);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_EXTRA_H*/

View File

@@ -0,0 +1,38 @@
/**
* @file lv_others.h
*
*/
#ifndef LV_OTHERS_H
#define LV_OTHERS_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "snapshot/lv_snapshot.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_OTHERS_H*/

View File

@@ -0,0 +1,228 @@
/**
* @file lv_snapshot.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_snapshot.h"
#if LV_USE_SNAPSHOT
#include <stdbool.h>
#include "../../../core/lv_disp.h"
#include "../../../core/lv_refr.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/** Get the buffer needed for object snapshot image.
*
* @param obj The object to generate snapshot.
* @param cf color format for generated image.
*
* @return the buffer size needed in bytes
*/
uint32_t lv_snapshot_buf_size_needed(lv_obj_t * obj, lv_img_cf_t cf)
{
switch(cf) {
case LV_IMG_CF_TRUE_COLOR_ALPHA:
case LV_IMG_CF_ALPHA_1BIT:
case LV_IMG_CF_ALPHA_2BIT:
case LV_IMG_CF_ALPHA_4BIT:
case LV_IMG_CF_ALPHA_8BIT:
break;
default:
return 0;
}
lv_obj_update_layout(obj);
/*Width and height determine snapshot image size.*/
lv_coord_t w = lv_obj_get_width(obj);
lv_coord_t h = lv_obj_get_height(obj);
lv_coord_t ext_size = _lv_obj_get_ext_draw_size(obj);
w += ext_size * 2;
h += ext_size * 2;
uint8_t px_size = lv_img_cf_get_px_size(cf);
return w * h * ((px_size + 7) >> 3);
}
/** Take snapshot for object with its children, save image info to provided buffer.
*
* @param obj The object to generate snapshot.
* @param cf color format for generated image.
* @param dsc image descriptor to store the image result.
* @param buf the buffer to store image data.
* @param buff_size provided buffer size in bytes.
*
* @return LV_RES_OK on success, LV_RES_INV on error.
*/
lv_res_t lv_snapshot_take_to_buf(lv_obj_t * obj, lv_img_cf_t cf, lv_img_dsc_t * dsc, void * buf, uint32_t buff_size)
{
LV_ASSERT(dsc);
LV_ASSERT(buf);
switch(cf) {
case LV_IMG_CF_TRUE_COLOR_ALPHA:
case LV_IMG_CF_ALPHA_1BIT:
case LV_IMG_CF_ALPHA_2BIT:
case LV_IMG_CF_ALPHA_4BIT:
case LV_IMG_CF_ALPHA_8BIT:
break;
default:
return LV_RES_INV;
}
if(lv_snapshot_buf_size_needed(obj, cf) > buff_size)
return LV_RES_INV;
/*Width and height determine snapshot image size.*/
lv_coord_t w = lv_obj_get_width(obj);
lv_coord_t h = lv_obj_get_height(obj);
lv_coord_t ext_size = _lv_obj_get_ext_draw_size(obj);
w += ext_size * 2;
h += ext_size * 2;
/*Backup obj original info.*/
lv_obj_t * parent_old = lv_obj_get_parent(obj);
lv_area_t coords_bkp;
lv_area_copy(&coords_bkp, &obj->coords);
lv_memset(buf, 0x00, buff_size);
lv_memset_00(dsc, sizeof(lv_img_dsc_t));
/*We are safe to use stack for below variables since disp will be
* unregistered when function returns. */
lv_disp_t * disp;
lv_disp_drv_t driver;
lv_disp_draw_buf_t draw_buf;
lv_disp_draw_buf_init(&draw_buf, buf, NULL, w * h);
lv_disp_drv_init(&driver);
driver.draw_buf = &draw_buf;
driver.hor_res = w;
driver.ver_res = h;
lv_disp_drv_use_generic_set_px_cb(&driver, cf);
disp = lv_disp_drv_register(&driver);
if(disp == NULL) {
return LV_RES_INV;
}
/*Make background transparent */
lv_disp_set_bg_opa(disp, LV_OPA_TRANSP);
/*Move obj to newly created disp and refresh it. */
lv_obj_t * screen = lv_disp_get_scr_act(disp);
lv_obj_remove_style_all(screen);
lv_obj_allocate_spec_attr(screen);
screen->spec_attr->child_cnt = 1;
screen->spec_attr->children = &obj;
obj->parent = screen;
disp->inv_p = 0;
obj->coords.x2 = w - ext_size - 1;
obj->coords.x1 = ext_size;
obj->coords.y2 = h - ext_size - 1;
obj->coords.y1 = ext_size;
lv_obj_invalidate(obj);
/*Don't call lv_refr_now to avoid animation disruption */
_lv_disp_refr_timer(disp->refr_timer);
/*Restore obj original parameters and clean up*/
obj->parent = parent_old;
screen->spec_attr->child_cnt = 0;
screen->spec_attr->children = NULL;
lv_disp_remove(disp);
lv_area_copy(&obj->coords, &coords_bkp);
dsc->data = buf;
dsc->header.w = lv_area_get_width(&draw_buf.area);
dsc->header.h = lv_area_get_height(&draw_buf.area);
dsc->header.cf = cf;
return LV_RES_OK;
}
/** Take snapshot for object with its children, alloc the memory needed.
*
* @param obj The object to generate snapshot.
* @param cf color format for generated image.
*
* @return a pointer to a image descriptor, or NULL if failed.
*/
lv_img_dsc_t * lv_snapshot_take(lv_obj_t * obj, lv_img_cf_t cf)
{
uint32_t buff_size = lv_snapshot_buf_size_needed(obj, cf);
void * buf = lv_mem_alloc(buff_size);
if(buf == NULL) {
return NULL;
}
lv_img_dsc_t * dsc = lv_mem_alloc(sizeof(lv_img_dsc_t));
if(dsc == NULL) {
lv_mem_free(buf);
return NULL;
}
if(lv_snapshot_take_to_buf(obj, cf, dsc, buf, buff_size) == LV_RES_INV) {
lv_mem_free(buf);
lv_mem_free(dsc);
return NULL;
}
return dsc;
}
/** Free the snapshot image returned by @ref lv_snapshot_take
*
* It will firstly free the data image takes, then the image descriptor.
*
* @param dsc The image descriptor generated by lv_snapshot_take.
*
*/
void lv_snapshot_free(lv_img_dsc_t * dsc)
{
if(!dsc)
return;
if(dsc->data)
lv_mem_free((void *)dsc->data);
lv_mem_free(dsc);
}
/**********************
* STATIC FUNCTIONS
**********************/
#endif /*LV_USE_SNAPSHOT*/

View File

@@ -0,0 +1,84 @@
/**
* @file lv_snapshot.h
*
*/
#ifndef LV_SNAPSHOT_H
#define LV_SNAPSHOT_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include <stdint.h>
#include <stddef.h>
#include "../../../lv_conf_internal.h"
#include "../../../core/lv_obj.h"
/*********************
* DEFINES
*********************/
#if LV_USE_SNAPSHOT
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/** Take snapshot for object with its children.
*
* @param obj The object to generate snapshot.
* @param cf color format for generated image.
*
* @return a pointer to a image descriptor, or NULL if failed.
*/
lv_img_dsc_t * lv_snapshot_take(lv_obj_t * obj, lv_img_cf_t cf);
/** Free the snapshot image returned by @ref lv_snapshot_take
*
* It will firstly free the data image takes, then the image descriptor.
*
* @param dsc The image descriptor generated by lv_snapshot_take.
*
*/
void lv_snapshot_free(lv_img_dsc_t * dsc);
/** Get the buffer needed for object snapshot image.
*
* @param obj The object to generate snapshot.
* @param cf color format for generated image.
*
* @return the buffer size needed in bytes
*/
uint32_t lv_snapshot_buf_size_needed(lv_obj_t * obj, lv_img_cf_t cf);
/** Take snapshot for object with its children, save image info to provided buffer.
*
* @param obj The object to generate snapshot.
* @param cf color format for generated image.
* @param dsc image descriptor to store the image result.
* @param buff the buffer to store image data.
* @param buff_size provided buffer size in bytes.
*
* @return LV_RES_OK on success, LV_RES_INV on error.
*/
lv_res_t lv_snapshot_take_to_buf(lv_obj_t * obj, lv_img_cf_t cf, lv_img_dsc_t * dsc, void * buf, uint32_t buff_size);
/**********************
* MACROS
**********************/
#endif /*LV_USE_SNAPSHOT*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif

View File

@@ -0,0 +1,424 @@
/**
* @file lv_theme_basic.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../../lvgl.h" /*To see all the widgets*/
#if LV_USE_THEME_BASIC
#include "lv_theme_basic.h"
#include "../../../misc/lv_gc.h"
/*********************
* DEFINES
*********************/
#define COLOR_WHITE lv_color_white()
#define COLOR_LIGHT lv_palette_lighten(LV_PALETTE_GREY, 3)
#define COLOR_MID lv_palette_lighten(LV_PALETTE_GREY, 1)
#define COLOR_DARK lv_palette_main(LV_PALETTE_GREY)
#define COLOR_DIM lv_palette_darken(LV_PALETTE_GREY, 2)
#define PAD_DEF LV_DPX(5)
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_style_t scr;
lv_style_t light;
lv_style_t dark;
lv_style_t scrollbar;
lv_style_t pressed;
lv_style_t disabled;
lv_style_t pad_zero;
#if LV_USE_TEXTAREA
lv_style_t ta_cursor;
#endif
} my_theme_styles_t;
/**********************
* STATIC PROTOTYPES
**********************/
static void style_init_reset(lv_style_t * style);
static void theme_apply(lv_theme_t * th, lv_obj_t * obj);
/**********************
* STATIC VARIABLES
**********************/
static my_theme_styles_t * styles;
static lv_theme_t theme;
static bool inited;
/**********************
* MACROS
**********************/
/**********************
* STATIC FUNCTIONS
**********************/
static lv_color_t dark_color_filter_cb(const lv_color_filter_dsc_t * f, lv_color_t c, lv_opa_t opa)
{
LV_UNUSED(f);
return lv_color_darken(c, opa);
}
static lv_color_t grey_filter_cb(const lv_color_filter_dsc_t * f, lv_color_t color, lv_opa_t opa)
{
LV_UNUSED(f);
return lv_color_mix(lv_color_white(), color, opa);
}
static void style_init(void)
{
style_init_reset(&styles->scrollbar);
lv_style_set_bg_opa(&styles->scrollbar, LV_OPA_COVER);
lv_style_set_bg_color(&styles->scrollbar, lv_palette_darken(LV_PALETTE_GREY, 2));
lv_style_set_width(&styles->scrollbar, PAD_DEF);
style_init_reset(&styles->scr);
lv_style_set_bg_opa(&styles->scr, LV_OPA_COVER);
lv_style_set_bg_color(&styles->scr, COLOR_WHITE);
lv_style_set_text_color(&styles->scr, COLOR_DIM);
lv_style_set_pad_row(&styles->scr, PAD_DEF / 2);
lv_style_set_pad_column(&styles->scr, PAD_DEF / 2);
style_init_reset(&styles->light);
lv_style_set_bg_opa(&styles->light, LV_OPA_COVER);
lv_style_set_bg_color(&styles->light, COLOR_LIGHT);
lv_style_set_border_color(&styles->light, COLOR_MID);
lv_style_set_border_width(&styles->light, 1);
lv_style_set_pad_all(&styles->light, PAD_DEF);
lv_style_set_pad_gap(&styles->light, PAD_DEF / 2);
lv_style_set_line_width(&styles->light, LV_DPX(2));
lv_style_set_line_color(&styles->light, COLOR_MID);
lv_style_set_arc_width(&styles->light, LV_DPX(2));
lv_style_set_arc_color(&styles->light, COLOR_MID);
style_init_reset(&styles->dark);
lv_style_set_bg_opa(&styles->dark, LV_OPA_COVER);
lv_style_set_bg_color(&styles->dark, COLOR_DARK);
lv_style_set_border_color(&styles->dark, COLOR_DIM);
lv_style_set_border_width(&styles->dark, 1);
lv_style_set_pad_all(&styles->dark, PAD_DEF);
lv_style_set_pad_gap(&styles->dark, PAD_DEF / 2);
lv_style_set_line_width(&styles->dark, LV_DPX(2));
lv_style_set_line_color(&styles->dark, COLOR_DIM);
lv_style_set_arc_width(&styles->dark, LV_DPX(2));
lv_style_set_arc_color(&styles->dark, COLOR_DIM);
static lv_color_filter_dsc_t dark_filter;
lv_color_filter_dsc_init(&dark_filter, dark_color_filter_cb);
style_init_reset(&styles->pressed);
lv_style_set_color_filter_dsc(&styles->pressed, &dark_filter);
lv_style_set_color_filter_opa(&styles->pressed, 35);
static lv_color_filter_dsc_t grey_filter;
lv_color_filter_dsc_init(&grey_filter, grey_filter_cb);
style_init_reset(&styles->disabled);
lv_style_set_color_filter_dsc(&styles->disabled, &grey_filter);
lv_style_set_color_filter_opa(&styles->disabled, LV_OPA_70);
style_init_reset(&styles->pad_zero);
lv_style_set_pad_all(&styles->pad_zero, 0);
lv_style_set_pad_gap(&styles->pad_zero, 0);
#if LV_USE_TEXTAREA
style_init_reset(&styles->ta_cursor);
lv_style_set_border_side(&styles->ta_cursor, LV_BORDER_SIDE_LEFT);
lv_style_set_border_color(&styles->ta_cursor, COLOR_DIM);
lv_style_set_border_width(&styles->ta_cursor, 2);
lv_style_set_bg_opa(&styles->ta_cursor, LV_OPA_TRANSP);
lv_style_set_anim_time(&styles->ta_cursor, 500);
#endif
}
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_theme_t * lv_theme_basic_init(lv_disp_t * disp)
{
/*This trick is required only to avoid the garbage collection of
*styles' data if LVGL is used in a binding (e.g. Micropython)
*In a general case styles could be in simple `static lv_style_t my_style...` variables*/
if(!inited) {
LV_GC_ROOT(_lv_theme_default_styles) = lv_mem_alloc(sizeof(my_theme_styles_t));
styles = (my_theme_styles_t *)LV_GC_ROOT(_lv_theme_default_styles);
}
theme.disp = disp;
theme.font_small = LV_FONT_DEFAULT;
theme.font_normal = LV_FONT_DEFAULT;
theme.font_large = LV_FONT_DEFAULT;
theme.apply_cb = theme_apply;
style_init();
inited = true;
if(disp == NULL || lv_disp_get_theme(disp) == &theme) lv_obj_report_style_change(NULL);
return (lv_theme_t *)&theme;
}
static void theme_apply(lv_theme_t * th, lv_obj_t * obj)
{
LV_UNUSED(th);
if(lv_obj_get_parent(obj) == NULL) {
lv_obj_add_style(obj, &styles->scr, 0);
lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR);
return;
}
if(lv_obj_check_type(obj, &lv_obj_class)) {
#if LV_USE_TABVIEW
lv_obj_t * parent = lv_obj_get_parent(obj);
/*Tabview content area*/
if(lv_obj_check_type(parent, &lv_tabview_class)) {
lv_obj_add_style(obj, &styles->light, 0);
return;
}
/*Tabview pages*/
else if(lv_obj_check_type(lv_obj_get_parent(parent), &lv_tabview_class)) {
lv_obj_add_style(obj, &styles->light, 0);
lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR);
return;
}
#endif
#if LV_USE_WIN
/*Header*/
if(lv_obj_get_index(obj) == 0 && lv_obj_check_type(lv_obj_get_parent(obj), &lv_win_class)) {
lv_obj_add_style(obj, &styles->light, 0);
return;
}
/*Content*/
else if(lv_obj_get_index(obj) == 1 && lv_obj_check_type(lv_obj_get_parent(obj), &lv_win_class)) {
lv_obj_add_style(obj, &styles->light, 0);
lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR);
return;
}
#endif
lv_obj_add_style(obj, &styles->light, 0);
lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR);
}
#if LV_USE_BTN
else if(lv_obj_check_type(obj, &lv_btn_class)) {
lv_obj_add_style(obj, &styles->dark, 0);
lv_obj_add_style(obj, &styles->pressed, LV_STATE_PRESSED);
lv_obj_add_style(obj, &styles->disabled, LV_STATE_DISABLED);
}
#endif
#if LV_USE_BTNMATRIX
else if(lv_obj_check_type(obj, &lv_btnmatrix_class)) {
#if LV_USE_MSGBOX
if(lv_obj_check_type(lv_obj_get_parent(obj), &lv_msgbox_class)) {
lv_obj_add_style(obj, &styles->light, LV_PART_ITEMS);
lv_obj_add_style(obj, &styles->pressed, LV_PART_ITEMS | LV_STATE_PRESSED);
return;
}
#endif
#if LV_USE_TABVIEW
if(lv_obj_check_type(lv_obj_get_parent(obj), &lv_tabview_class)) {
lv_obj_add_style(obj, &styles->light, LV_PART_ITEMS);
lv_obj_add_style(obj, &styles->pressed, LV_PART_ITEMS | LV_STATE_PRESSED);
return;
}
#endif
lv_obj_add_style(obj, &styles->light, 0);
lv_obj_add_style(obj, &styles->dark, LV_PART_ITEMS);
lv_obj_add_style(obj, &styles->pressed, LV_PART_ITEMS | LV_STATE_PRESSED);
}
#endif
#if LV_USE_BAR
else if(lv_obj_check_type(obj, &lv_bar_class)) {
lv_obj_add_style(obj, &styles->light, 0);
lv_obj_add_style(obj, &styles->pad_zero, 0);
lv_obj_add_style(obj, &styles->dark, LV_PART_INDICATOR);
}
#endif
#if LV_USE_SLIDER
else if(lv_obj_check_type(obj, &lv_slider_class)) {
lv_obj_add_style(obj, &styles->light, 0);
lv_obj_add_style(obj, &styles->pad_zero, 0);
lv_obj_add_style(obj, &styles->dark, LV_PART_INDICATOR);
lv_obj_add_style(obj, &styles->dark, LV_PART_KNOB);
}
#endif
#if LV_USE_TABLE
else if(lv_obj_check_type(obj, &lv_table_class)) {
lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR);
lv_obj_add_style(obj, &styles->light, LV_PART_ITEMS);
lv_obj_add_style(obj, &styles->pressed, LV_PART_ITEMS | LV_STATE_PRESSED);
}
#endif
#if LV_USE_CHECKBOX
else if(lv_obj_check_type(obj, &lv_checkbox_class)) {
lv_obj_add_style(obj, &styles->light, LV_PART_INDICATOR);
lv_obj_add_style(obj, &styles->disabled, LV_PART_INDICATOR | LV_STATE_DISABLED);
lv_obj_add_style(obj, &styles->dark, LV_PART_INDICATOR | LV_STATE_CHECKED);
lv_obj_add_style(obj, &styles->pressed, LV_PART_INDICATOR | LV_STATE_PRESSED);
}
#endif
#if LV_USE_SWITCH
else if(lv_obj_check_type(obj, &lv_switch_class)) {
lv_obj_add_style(obj, &styles->light, 0);
lv_obj_add_style(obj, &styles->pad_zero, 0);
lv_obj_add_style(obj, &styles->dark, LV_PART_INDICATOR);
lv_obj_add_style(obj, &styles->dark, LV_PART_KNOB);
lv_obj_add_style(obj, &styles->pad_zero, LV_PART_KNOB);
}
#endif
#if LV_USE_CHART
else if(lv_obj_check_type(obj, &lv_chart_class)) {
lv_obj_add_style(obj, &styles->light, 0);
lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR);
lv_obj_add_style(obj, &styles->light, LV_PART_ITEMS);
lv_obj_add_style(obj, &styles->light, LV_PART_TICKS);
lv_obj_add_style(obj, &styles->light, LV_PART_CURSOR);
}
#endif
#if LV_USE_ROLLER
else if(lv_obj_check_type(obj, &lv_roller_class)) {
lv_obj_add_style(obj, &styles->light, 0);
lv_obj_add_style(obj, &styles->dark, LV_PART_SELECTED);
}
#endif
#if LV_USE_DROPDOWN
else if(lv_obj_check_type(obj, &lv_dropdown_class)) {
lv_obj_add_style(obj, &styles->light, 0);
lv_obj_add_style(obj, &styles->pressed, LV_STATE_PRESSED);
}
else if(lv_obj_check_type(obj, &lv_dropdownlist_class)) {
lv_obj_add_style(obj, &styles->light, 0);
lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR);
lv_obj_add_style(obj, &styles->light, LV_PART_SELECTED);
lv_obj_add_style(obj, &styles->dark, LV_PART_SELECTED | LV_STATE_CHECKED);
lv_obj_add_style(obj, &styles->pressed, LV_PART_SELECTED | LV_STATE_PRESSED);
}
#endif
#if LV_USE_ARC
else if(lv_obj_check_type(obj, &lv_arc_class)) {
lv_obj_add_style(obj, &styles->light, 0);
lv_obj_add_style(obj, &styles->dark, LV_PART_INDICATOR);
lv_obj_add_style(obj, &styles->pad_zero, LV_PART_INDICATOR);
lv_obj_add_style(obj, &styles->dark, LV_PART_KNOB);
}
#endif
#if LV_USE_METER
else if(lv_obj_check_type(obj, &lv_meter_class)) {
lv_obj_add_style(obj, &styles->light, 0);
}
#endif
#if LV_USE_TEXTAREA
else if(lv_obj_check_type(obj, &lv_textarea_class)) {
lv_obj_add_style(obj, &styles->light, 0);
lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR);
lv_obj_add_style(obj, &styles->ta_cursor, LV_PART_CURSOR);
lv_obj_add_style(obj, &styles->light, LV_PART_TEXTAREA_PLACEHOLDER);
}
#endif
#if LV_USE_CALENDAR
else if(lv_obj_check_type(obj, &lv_calendar_class)) {
lv_obj_add_style(obj, &styles->light, 0);
lv_obj_add_style(obj, &styles->light, LV_PART_ITEMS | LV_STATE_PRESSED);
lv_obj_add_style(obj, &styles->pressed, LV_PART_ITEMS | LV_STATE_PRESSED);
lv_obj_add_style(obj, &styles->disabled, LV_PART_ITEMS | LV_STATE_DISABLED);
}
#endif
#if LV_USE_KEYBOARD
else if(lv_obj_check_type(obj, &lv_keyboard_class)) {
lv_obj_add_style(obj, &styles->light, 0);
lv_obj_add_style(obj, &styles->light, LV_PART_ITEMS);
lv_obj_add_style(obj, &styles->pressed, LV_PART_ITEMS | LV_STATE_PRESSED);
}
#endif
#if LV_USE_LIST
else if(lv_obj_check_type(obj, &lv_list_class)) {
lv_obj_add_style(obj, &styles->light, 0);
lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR);
return;
}
else if(lv_obj_check_type(obj, &lv_list_text_class)) {
}
else if(lv_obj_check_type(obj, &lv_list_btn_class)) {
lv_obj_add_style(obj, &styles->dark, 0);
lv_obj_add_style(obj, &styles->pressed, LV_STATE_PRESSED);
}
#endif
#if LV_USE_MSGBOX
else if(lv_obj_check_type(obj, &lv_msgbox_class)) {
lv_obj_add_style(obj, &styles->light, 0);
return;
}
#endif
#if LV_USE_SPINBOX
else if(lv_obj_check_type(obj, &lv_spinbox_class)) {
lv_obj_add_style(obj, &styles->light, 0);
lv_obj_add_style(obj, &styles->dark, LV_PART_CURSOR);
}
#endif
#if LV_USE_TILEVIEW
else if(lv_obj_check_type(obj, &lv_tileview_class)) {
lv_obj_add_style(obj, &styles->scr, 0);
lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR);
}
else if(lv_obj_check_type(obj, &lv_tileview_tile_class)) {
lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR);
}
#endif
#if LV_USE_COLORWHEEL
else if(lv_obj_check_type(obj, &lv_colorwheel_class)) {
lv_obj_add_style(obj, &styles->light, 0);
lv_obj_add_style(obj, &styles->light, LV_PART_KNOB);
}
#endif
#if LV_USE_LED
else if(lv_obj_check_type(obj, &lv_led_class)) {
lv_obj_add_style(obj, &styles->light, 0);
}
#endif
}
/**********************
* STATIC FUNCTIONS
**********************/
static void style_init_reset(lv_style_t * style)
{
if(inited) lv_style_reset(style);
else lv_style_init(style);
}
#endif

View File

@@ -0,0 +1,49 @@
/**
* @file lv_theme_basic.h
*
*/
#ifndef LV_THEME_BASIC_H
#define LV_THEME_BASIC_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../core/lv_obj.h"
#if LV_USE_THEME_BASIC
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Initialize the theme
* @param disp pointer to display to attach the theme
* @return a pointer to reference this theme later
*/
lv_theme_t * lv_theme_basic_init(lv_disp_t * disp);
/**********************
* MACROS
**********************/
#endif
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_THEME_BASIC_H*/

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,64 @@
/**
* @file lv_theme_default.h
*
*/
#ifndef LV_THEME_DEFAULT_H
#define LV_THEME_DEFAULT_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../core/lv_obj.h"
#if LV_USE_THEME_DEFAULT
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Initialize the theme
* @param color_primary the primary color of the theme
* @param color_secondary the secondary color for the theme
* @param font pointer to a font to use.
* @return a pointer to reference this theme later
*/
lv_theme_t * lv_theme_default_init(lv_disp_t * disp, lv_color_t color_primary, lv_color_t color_secondary, bool dark,
const lv_font_t * font);
/**
* Get default theme
* @return a pointer to default theme, or NULL if this is not initialized
*/
lv_theme_t * lv_theme_default_get(void);
/**
* Check if default theme is initialized
* @return true if default theme is initialized, false otherwise
*/
bool lv_theme_default_is_inited(void);
/**********************
* MACROS
**********************/
#endif
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_THEME_DEFAULT_H*/

View File

@@ -0,0 +1,40 @@
/**
* @file lv_themes.h
*
*/
#ifndef LV_THEMES_H
#define LV_THEMES_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "default/lv_theme_default.h"
#include "mono/lv_theme_mono.h"
#include "basic/lv_theme_basic.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_THEMES_H*/

View File

@@ -0,0 +1,494 @@
/**
* @file lv_theme_mono.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../../lvgl.h"
#if LV_USE_THEME_MONO
#include "lv_theme_mono.h"
#include "../../../misc/lv_gc.h"
/*********************
* DEFINES
*********************/
#define COLOR_FG dark_bg ? lv_color_white() : lv_color_black()
#define COLOR_BG dark_bg ? lv_color_black() : lv_color_white()
#define BORDER_W_NORMAL 1
#define BORDER_W_PR 3
#define BORDER_W_DIS 0
#define BORDER_W_FOCUS 1
#define BORDER_W_EDIT 2
#define PAD_DEF 4
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_style_t scr;
lv_style_t card;
lv_style_t scrollbar;
lv_style_t btn;
lv_style_t pr;
lv_style_t inv;
lv_style_t disabled;
lv_style_t focus;
lv_style_t edit;
lv_style_t pad_gap;
lv_style_t pad_zero;
lv_style_t no_radius;
lv_style_t radius_circle;
lv_style_t large_border;
lv_style_t large_line_space;
lv_style_t underline;
#if LV_USE_TEXTAREA
lv_style_t ta_cursor;
#endif
} my_theme_styles_t;
/**********************
* STATIC PROTOTYPES
**********************/
static void style_init_reset(lv_style_t * style);
static void theme_apply(lv_theme_t * th, lv_obj_t * obj);
/**********************
* STATIC VARIABLES
**********************/
static my_theme_styles_t * styles;
static lv_theme_t theme;
static bool inited;
/**********************
* MACROS
**********************/
/**********************
* STATIC FUNCTIONS
**********************/
static void style_init(bool dark_bg, const lv_font_t * font)
{
style_init_reset(&styles->scrollbar);
lv_style_set_bg_opa(&styles->scrollbar, LV_OPA_COVER);
lv_style_set_bg_color(&styles->scrollbar, COLOR_FG);
lv_style_set_width(&styles->scrollbar, PAD_DEF);
style_init_reset(&styles->scr);
lv_style_set_bg_opa(&styles->scr, LV_OPA_COVER);
lv_style_set_bg_color(&styles->scr, COLOR_BG);
lv_style_set_text_color(&styles->scr, COLOR_FG);
lv_style_set_pad_row(&styles->scr, PAD_DEF);
lv_style_set_pad_column(&styles->scr, PAD_DEF);
lv_style_set_text_font(&styles->scr, font);
style_init_reset(&styles->card);
lv_style_set_bg_opa(&styles->card, LV_OPA_COVER);
lv_style_set_bg_color(&styles->card, COLOR_BG);
lv_style_set_border_color(&styles->card, COLOR_FG);
lv_style_set_radius(&styles->card, 2);
lv_style_set_border_width(&styles->card, BORDER_W_NORMAL);
lv_style_set_pad_all(&styles->card, PAD_DEF);
lv_style_set_pad_gap(&styles->card, PAD_DEF);
lv_style_set_text_color(&styles->card, COLOR_FG);
lv_style_set_line_width(&styles->card, 2);
lv_style_set_line_color(&styles->card, COLOR_FG);
lv_style_set_arc_width(&styles->card, 2);
lv_style_set_arc_color(&styles->card, COLOR_FG);
lv_style_set_outline_color(&styles->card, COLOR_FG);
lv_style_set_anim_time(&styles->card, 300);
style_init_reset(&styles->pr);
lv_style_set_border_width(&styles->pr, BORDER_W_PR);
style_init_reset(&styles->inv);
lv_style_set_bg_opa(&styles->inv, LV_OPA_COVER);
lv_style_set_bg_color(&styles->inv, COLOR_FG);
lv_style_set_border_color(&styles->inv, COLOR_BG);
lv_style_set_line_color(&styles->inv, COLOR_BG);
lv_style_set_arc_color(&styles->inv, COLOR_BG);
lv_style_set_text_color(&styles->inv, COLOR_BG);
lv_style_set_outline_color(&styles->inv, COLOR_BG);
style_init_reset(&styles->disabled);
lv_style_set_border_width(&styles->disabled, BORDER_W_DIS);
style_init_reset(&styles->focus);
lv_style_set_outline_width(&styles->focus, 1);
lv_style_set_outline_pad(&styles->focus, BORDER_W_FOCUS);
style_init_reset(&styles->edit);
lv_style_set_outline_width(&styles->edit, BORDER_W_EDIT);
style_init_reset(&styles->large_border);
lv_style_set_border_width(&styles->large_border, BORDER_W_EDIT);
style_init_reset(&styles->pad_gap);
lv_style_set_pad_gap(&styles->pad_gap, PAD_DEF);
style_init_reset(&styles->pad_zero);
lv_style_set_pad_all(&styles->pad_zero, 0);
lv_style_set_pad_gap(&styles->pad_zero, 0);
style_init_reset(&styles->no_radius);
lv_style_set_radius(&styles->no_radius, 0);
style_init_reset(&styles->radius_circle);
lv_style_set_radius(&styles->radius_circle, LV_RADIUS_CIRCLE);
style_init_reset(&styles->large_line_space);
lv_style_set_text_line_space(&styles->large_line_space, 6);
style_init_reset(&styles->underline);
lv_style_set_text_decor(&styles->underline, LV_TEXT_DECOR_UNDERLINE);
#if LV_USE_TEXTAREA
style_init_reset(&styles->ta_cursor);
lv_style_set_border_side(&styles->ta_cursor, LV_BORDER_SIDE_LEFT);
lv_style_set_border_color(&styles->ta_cursor, COLOR_FG);
lv_style_set_border_width(&styles->ta_cursor, 2);
lv_style_set_bg_opa(&styles->ta_cursor, LV_OPA_TRANSP);
lv_style_set_anim_time(&styles->ta_cursor, 500);
#endif
}
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_theme_t * lv_theme_mono_init(lv_disp_t * disp, bool dark_bg, const lv_font_t * font)
{
/*This trick is required only to avoid the garbage collection of
*styles' data if LVGL is used in a binding (e.g. Micropython)
*In a general case styles could be in simple `static lv_style_t my_style...` variables*/
if(!inited) {
LV_GC_ROOT(_lv_theme_default_styles) = lv_mem_alloc(sizeof(my_theme_styles_t));
styles = (my_theme_styles_t *)LV_GC_ROOT(_lv_theme_default_styles);
}
theme.disp = disp;
theme.font_small = LV_FONT_DEFAULT;
theme.font_normal = LV_FONT_DEFAULT;
theme.font_large = LV_FONT_DEFAULT;
theme.apply_cb = theme_apply;
style_init(dark_bg, font);
inited = true;
if(disp == NULL || lv_disp_get_theme(disp) == &theme) lv_obj_report_style_change(NULL);
return (lv_theme_t *)&theme;
}
static void theme_apply(lv_theme_t * th, lv_obj_t * obj)
{
LV_UNUSED(th);
if(lv_obj_get_parent(obj) == NULL) {
lv_obj_add_style(obj, &styles->scr, 0);
lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR);
return;
}
if(lv_obj_check_type(obj, &lv_obj_class)) {
#if LV_USE_TABVIEW
lv_obj_t * parent = lv_obj_get_parent(obj);
/*Tabview content area*/
if(lv_obj_check_type(parent, &lv_tabview_class)) {
return;
}
/*Tabview pages*/
else if(lv_obj_check_type(lv_obj_get_parent(parent), &lv_tabview_class)) {
lv_obj_add_style(obj, &styles->card, 0);
lv_obj_add_style(obj, &styles->no_radius, 0);
lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR);
return;
}
#endif
#if LV_USE_WIN
/*Header*/
if(lv_obj_get_index(obj) == 0 && lv_obj_check_type(lv_obj_get_parent(obj), &lv_win_class)) {
lv_obj_add_style(obj, &styles->card, 0);
lv_obj_add_style(obj, &styles->no_radius, 0);
return;
}
/*Content*/
else if(lv_obj_get_index(obj) == 1 && lv_obj_check_type(lv_obj_get_parent(obj), &lv_win_class)) {
lv_obj_add_style(obj, &styles->card, 0);
lv_obj_add_style(obj, &styles->no_radius, 0);
lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR);
return;
}
#endif
lv_obj_add_style(obj, &styles->card, 0);
lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR);
}
#if LV_USE_BTN
else if(lv_obj_check_type(obj, &lv_btn_class)) {
lv_obj_add_style(obj, &styles->card, 0);
lv_obj_add_style(obj, &styles->pr, LV_STATE_PRESSED);
lv_obj_add_style(obj, &styles->inv, LV_STATE_CHECKED);
lv_obj_add_style(obj, &styles->disabled, LV_STATE_DISABLED);
lv_obj_add_style(obj, &styles->focus, LV_STATE_FOCUS_KEY);
lv_obj_add_style(obj, &styles->edit, LV_STATE_EDITED);
}
#endif
#if LV_USE_BTNMATRIX
else if(lv_obj_check_type(obj, &lv_btnmatrix_class)) {
#if LV_USE_MSGBOX
if(lv_obj_check_type(lv_obj_get_parent(obj), &lv_msgbox_class)) {
lv_obj_add_style(obj, &styles->pad_gap, 0);
lv_obj_add_style(obj, &styles->card, LV_PART_ITEMS);
lv_obj_add_style(obj, &styles->pr, LV_PART_ITEMS | LV_STATE_PRESSED);
lv_obj_add_style(obj, &styles->disabled, LV_PART_ITEMS | LV_STATE_DISABLED);
lv_obj_add_style(obj, &styles->underline, LV_PART_ITEMS | LV_STATE_FOCUS_KEY);
lv_obj_add_style(obj, &styles->large_border, LV_PART_ITEMS | LV_STATE_FOCUS_KEY);
return;
}
#endif
#if LV_USE_TABVIEW
if(lv_obj_check_type(lv_obj_get_parent(obj), &lv_tabview_class)) {
lv_obj_add_style(obj, &styles->pad_gap, 0);
lv_obj_add_style(obj, &styles->card, LV_PART_ITEMS);
lv_obj_add_style(obj, &styles->pr, LV_PART_ITEMS | LV_STATE_PRESSED);
lv_obj_add_style(obj, &styles->inv, LV_PART_ITEMS | LV_STATE_CHECKED);
lv_obj_add_style(obj, &styles->disabled, LV_PART_ITEMS | LV_STATE_DISABLED);
lv_obj_add_style(obj, &styles->focus, LV_STATE_FOCUS_KEY);
lv_obj_add_style(obj, &styles->underline, LV_PART_ITEMS | LV_STATE_FOCUS_KEY);
lv_obj_add_style(obj, &styles->large_border, LV_PART_ITEMS | LV_STATE_FOCUS_KEY);
return;
}
#endif
lv_obj_add_style(obj, &styles->card, 0);
lv_obj_add_style(obj, &styles->focus, LV_STATE_FOCUS_KEY);
lv_obj_add_style(obj, &styles->card, LV_PART_ITEMS);
lv_obj_add_style(obj, &styles->pr, LV_PART_ITEMS | LV_STATE_PRESSED);
lv_obj_add_style(obj, &styles->inv, LV_PART_ITEMS | LV_STATE_CHECKED);
lv_obj_add_style(obj, &styles->disabled, LV_PART_ITEMS | LV_STATE_DISABLED);
lv_obj_add_style(obj, &styles->underline, LV_PART_ITEMS | LV_STATE_FOCUS_KEY);
lv_obj_add_style(obj, &styles->large_border, LV_PART_ITEMS | LV_STATE_FOCUS_KEY);
}
#endif
#if LV_USE_BAR
else if(lv_obj_check_type(obj, &lv_bar_class)) {
lv_obj_add_style(obj, &styles->card, 0);
lv_obj_add_style(obj, &styles->pad_zero, 0);
lv_obj_add_style(obj, &styles->inv, LV_PART_INDICATOR);
lv_obj_add_style(obj, &styles->focus, LV_STATE_FOCUS_KEY);
}
#endif
#if LV_USE_SLIDER
else if(lv_obj_check_type(obj, &lv_slider_class)) {
lv_obj_add_style(obj, &styles->card, 0);
lv_obj_add_style(obj, &styles->pad_zero, 0);
lv_obj_add_style(obj, &styles->inv, LV_PART_INDICATOR);
lv_obj_add_style(obj, &styles->card, LV_PART_KNOB);
lv_obj_add_style(obj, &styles->radius_circle, LV_PART_KNOB);
lv_obj_add_style(obj, &styles->focus, LV_STATE_FOCUS_KEY);
lv_obj_add_style(obj, &styles->edit, LV_STATE_EDITED);
}
#endif
#if LV_USE_TABLE
else if(lv_obj_check_type(obj, &lv_table_class)) {
lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR);
lv_obj_add_style(obj, &styles->card, LV_PART_ITEMS);
lv_obj_add_style(obj, &styles->no_radius, LV_PART_ITEMS);
lv_obj_add_style(obj, &styles->pr, LV_PART_ITEMS | LV_STATE_PRESSED);
lv_obj_add_style(obj, &styles->focus, LV_STATE_FOCUS_KEY);
lv_obj_add_style(obj, &styles->inv, LV_PART_ITEMS | LV_STATE_FOCUS_KEY);
lv_obj_add_style(obj, &styles->edit, LV_STATE_EDITED);
}
#endif
#if LV_USE_CHECKBOX
else if(lv_obj_check_type(obj, &lv_checkbox_class)) {
lv_obj_add_style(obj, &styles->pad_gap, LV_PART_MAIN);
lv_obj_add_style(obj, &styles->card, LV_PART_INDICATOR);
lv_obj_add_style(obj, &styles->disabled, LV_PART_INDICATOR | LV_STATE_DISABLED);
lv_obj_add_style(obj, &styles->inv, LV_PART_INDICATOR | LV_STATE_CHECKED);
lv_obj_add_style(obj, &styles->pr, LV_PART_INDICATOR | LV_STATE_PRESSED);
lv_obj_add_style(obj, &styles->focus, LV_STATE_FOCUS_KEY);
lv_obj_add_style(obj, &styles->edit, LV_STATE_EDITED);
}
#endif
#if LV_USE_SWITCH
else if(lv_obj_check_type(obj, &lv_switch_class)) {
lv_obj_add_style(obj, &styles->card, 0);
lv_obj_add_style(obj, &styles->radius_circle, 0);
lv_obj_add_style(obj, &styles->pad_zero, 0);
lv_obj_add_style(obj, &styles->inv, LV_PART_INDICATOR);
lv_obj_add_style(obj, &styles->radius_circle, LV_PART_INDICATOR);
lv_obj_add_style(obj, &styles->card, LV_PART_KNOB);
lv_obj_add_style(obj, &styles->radius_circle, LV_PART_KNOB);
lv_obj_add_style(obj, &styles->pad_zero, LV_PART_KNOB);
lv_obj_add_style(obj, &styles->focus, LV_STATE_FOCUS_KEY);
lv_obj_add_style(obj, &styles->edit, LV_STATE_EDITED);
}
#endif
#if LV_USE_CHART
else if(lv_obj_check_type(obj, &lv_chart_class)) {
lv_obj_add_style(obj, &styles->card, 0);
lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR);
lv_obj_add_style(obj, &styles->card, LV_PART_ITEMS);
lv_obj_add_style(obj, &styles->card, LV_PART_TICKS);
lv_obj_add_style(obj, &styles->card, LV_PART_CURSOR);
lv_obj_add_style(obj, &styles->focus, LV_STATE_FOCUS_KEY);
}
#endif
#if LV_USE_ROLLER
else if(lv_obj_check_type(obj, &lv_roller_class)) {
lv_obj_add_style(obj, &styles->card, 0);
lv_obj_add_style(obj, &styles->large_line_space, 0);
lv_obj_add_style(obj, &styles->inv, LV_PART_SELECTED);
lv_obj_add_style(obj, &styles->focus, LV_STATE_FOCUS_KEY);
lv_obj_add_style(obj, &styles->edit, LV_STATE_EDITED);
}
#endif
#if LV_USE_DROPDOWN
else if(lv_obj_check_type(obj, &lv_dropdown_class)) {
lv_obj_add_style(obj, &styles->card, 0);
lv_obj_add_style(obj, &styles->pr, LV_STATE_PRESSED);
lv_obj_add_style(obj, &styles->focus, LV_STATE_FOCUS_KEY);
lv_obj_add_style(obj, &styles->edit, LV_STATE_EDITED);
}
else if(lv_obj_check_type(obj, &lv_dropdownlist_class)) {
lv_obj_add_style(obj, &styles->card, 0);
lv_obj_add_style(obj, &styles->large_line_space, 0);
lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR);
lv_obj_add_style(obj, &styles->inv, LV_PART_SELECTED | LV_STATE_CHECKED);
lv_obj_add_style(obj, &styles->pr, LV_PART_SELECTED | LV_STATE_PRESSED);
lv_obj_add_style(obj, &styles->focus, LV_STATE_FOCUS_KEY);
lv_obj_add_style(obj, &styles->edit, LV_STATE_EDITED);
}
#endif
#if LV_USE_ARC
else if(lv_obj_check_type(obj, &lv_arc_class)) {
lv_obj_add_style(obj, &styles->card, 0);
lv_obj_add_style(obj, &styles->inv, LV_PART_INDICATOR);
lv_obj_add_style(obj, &styles->pad_zero, LV_PART_INDICATOR);
lv_obj_add_style(obj, &styles->card, LV_PART_KNOB);
lv_obj_add_style(obj, &styles->radius_circle, LV_PART_KNOB);
lv_obj_add_style(obj, &styles->focus, LV_STATE_FOCUS_KEY);
lv_obj_add_style(obj, &styles->edit, LV_STATE_EDITED);
}
#endif
#if LV_USE_METER
else if(lv_obj_check_type(obj, &lv_meter_class)) {
lv_obj_add_style(obj, &styles->card, 0);
}
#endif
#if LV_USE_TEXTAREA
else if(lv_obj_check_type(obj, &lv_textarea_class)) {
lv_obj_add_style(obj, &styles->card, 0);
lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR);
lv_obj_add_style(obj, &styles->ta_cursor, LV_PART_CURSOR | LV_STATE_FOCUSED);
lv_obj_add_style(obj, &styles->focus, LV_STATE_FOCUSED);
lv_obj_add_style(obj, &styles->edit, LV_STATE_EDITED);
}
#endif
#if LV_USE_CALENDAR
else if(lv_obj_check_type(obj, &lv_calendar_class)) {
lv_obj_add_style(obj, &styles->card, 0);
lv_obj_add_style(obj, &styles->no_radius, 0);
lv_obj_add_style(obj, &styles->pr, LV_PART_ITEMS | LV_STATE_PRESSED);
lv_obj_add_style(obj, &styles->disabled, LV_PART_ITEMS | LV_STATE_DISABLED);
lv_obj_add_style(obj, &styles->focus, LV_STATE_FOCUS_KEY);
lv_obj_add_style(obj, &styles->edit, LV_STATE_EDITED);
lv_obj_add_style(obj, &styles->large_border, LV_PART_ITEMS | LV_STATE_FOCUS_KEY);
}
#endif
#if LV_USE_KEYBOARD
else if(lv_obj_check_type(obj, &lv_keyboard_class)) {
lv_obj_add_style(obj, &styles->card, 0);
lv_obj_add_style(obj, &styles->card, LV_PART_ITEMS);
lv_obj_add_style(obj, &styles->pr, LV_PART_ITEMS | LV_STATE_PRESSED);
lv_obj_add_style(obj, &styles->inv, LV_PART_ITEMS | LV_STATE_CHECKED);
lv_obj_add_style(obj, &styles->focus, LV_STATE_FOCUS_KEY);
lv_obj_add_style(obj, &styles->edit, LV_STATE_EDITED);
lv_obj_add_style(obj, &styles->large_border, LV_PART_ITEMS | LV_STATE_EDITED);
}
#endif
#if LV_USE_LIST
else if(lv_obj_check_type(obj, &lv_list_class)) {
lv_obj_add_style(obj, &styles->card, 0);
lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR);
return;
}
else if(lv_obj_check_type(obj, &lv_list_text_class)) {
}
else if(lv_obj_check_type(obj, &lv_list_btn_class)) {
lv_obj_add_style(obj, &styles->card, 0);
lv_obj_add_style(obj, &styles->pr, LV_STATE_PRESSED);
lv_obj_add_style(obj, &styles->focus, LV_STATE_FOCUS_KEY);
lv_obj_add_style(obj, &styles->large_border, LV_STATE_EDITED);
}
#endif
#if LV_USE_MSGBOX
else if(lv_obj_check_type(obj, &lv_msgbox_class)) {
lv_obj_add_style(obj, &styles->card, 0);
return;
}
#endif
#if LV_USE_SPINBOX
else if(lv_obj_check_type(obj, &lv_spinbox_class)) {
lv_obj_add_style(obj, &styles->card, 0);
lv_obj_add_style(obj, &styles->inv, LV_PART_CURSOR);
lv_obj_add_style(obj, &styles->focus, LV_STATE_FOCUS_KEY);
lv_obj_add_style(obj, &styles->edit, LV_STATE_EDITED);
}
#endif
#if LV_USE_TILEVIEW
else if(lv_obj_check_type(obj, &lv_tileview_class)) {
lv_obj_add_style(obj, &styles->scr, 0);
lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR);
}
else if(lv_obj_check_type(obj, &lv_tileview_tile_class)) {
lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR);
}
#endif
#if LV_USE_LED
else if(lv_obj_check_type(obj, &lv_led_class)) {
lv_obj_add_style(obj, &styles->card, 0);
}
#endif
}
/**********************
* STATIC FUNCTIONS
**********************/
static void style_init_reset(lv_style_t * style)
{
if(inited) lv_style_reset(style);
else lv_style_init(style);
}
#endif

View File

@@ -0,0 +1,51 @@
/**
* @file lv_theme_mono.h
*
*/
#ifndef LV_USE_THEME_MONO_H
#define LV_USE_THEME_MONO_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../core/lv_obj.h"
#if LV_USE_THEME_MONO
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Initialize the theme
* @param color_primary the primary color of the theme
* @param color_secondary the secondary color for the theme
* @param font pointer to a font to use.
* @return a pointer to reference this theme later
*/
lv_theme_t * lv_theme_mono_init(lv_disp_t * disp, bool dark_bg, const lv_font_t * font);
/**********************
* MACROS
**********************/
#endif
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_THEME_DEFAULT_H*/

View File

@@ -0,0 +1,138 @@
/**
* @file lv_animimg.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_animimg.h"
#if LV_USE_ANIMIMG != 0
/*Testing of dependencies*/
#if LV_USE_IMG == 0
#error "lv_animimg: lv_img is required. Enable it in lv_conf.h (LV_USE_IMG 1) "
#endif
#include "../../../misc/lv_assert.h"
#include "../../../draw/lv_img_decoder.h"
#include "../../../misc/lv_fs.h"
#include "../../../misc/lv_txt.h"
#include "../../../misc/lv_math.h"
#include "../../../misc/lv_log.h"
#include "../../../misc/lv_anim.h"
/*********************
* DEFINES
*********************/
#define LV_OBJX_NAME "lv_animimg"
#define MY_CLASS &lv_animimg_class
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void index_change(lv_obj_t * obj, int32_t index);
static void lv_animimg_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
/**********************
* STATIC VARIABLES
**********************/
const lv_obj_class_t lv_animimg_class = {
.constructor_cb = lv_animimg_constructor,
.instance_size = sizeof(lv_animimg_t),
.base_class = &lv_img_class
};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_obj_t * lv_animimg_create(lv_obj_t * parent)
{
LV_LOG_INFO("begin");
lv_obj_t * obj = lv_obj_class_create_obj(&lv_animimg_class, parent);
lv_obj_class_init_obj(obj);
return obj;
}
void lv_animimg_set_src(lv_obj_t * obj, lv_img_dsc_t * dsc[], uint8_t num)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_animimg_t * animimg = (lv_animimg_t *)obj;
animimg->dsc = dsc;
animimg->pic_count = num;
lv_anim_set_values(&animimg->anim, 0, num);
}
void lv_animimg_start(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_animimg_t * animimg = (lv_animimg_t *)obj;
lv_anim_start(&animimg->anim);
}
/*=====================
* Setter functions
*====================*/
void lv_animimg_set_duration(lv_obj_t * obj, uint32_t duration)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_animimg_t * animimg = (lv_animimg_t *)obj;
lv_anim_set_time(&animimg->anim, duration);
lv_anim_set_playback_delay(&animimg->anim, duration);
}
void lv_animimg_set_repeat_count(lv_obj_t * obj, uint16_t count)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_animimg_t * animimg = (lv_animimg_t *)obj;
lv_anim_set_repeat_count(&animimg->anim, count);
}
/*=====================
* Getter functions
*====================*/
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_animimg_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_TRACE_OBJ_CREATE("begin");
LV_UNUSED(class_p);
lv_animimg_t * animimg = (lv_animimg_t *)obj;
animimg->dsc = NULL;
animimg->pic_count = -1;
//initial animation
lv_anim_init(&animimg->anim);
lv_anim_set_var(&animimg->anim, obj);
lv_anim_set_time(&animimg->anim, 30);
lv_anim_set_exec_cb(&animimg->anim, (lv_anim_exec_xcb_t)index_change);
lv_anim_set_values(&animimg->anim, 0, 1);
lv_anim_set_repeat_count(&animimg->anim, LV_ANIM_REPEAT_INFINITE);
}
static void index_change(lv_obj_t * obj, int32_t index)
{
lv_coord_t idx;
lv_animimg_t * animimg = (lv_animimg_t *)obj;
idx = index % animimg->pic_count;
lv_img_set_src(obj, animimg->dsc[idx]);
}
#endif

View File

@@ -0,0 +1,103 @@
/**
* @file lv_animimg.h
*
*/
#ifndef LV_ANIM_IMG_H
#define LV_ANIM_IMG_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../lvgl.h"
#if LV_USE_ANIMIMG != 0
/*Testing of dependencies*/
#if LV_USE_IMG == 0
#error "lv_animimg: lv_img is required. Enable it in lv_conf.h (LV_USE_IMG 1)"
#endif
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
extern const lv_obj_class_t lv_animimg_class;
/*Data of image*/
typedef struct {
lv_img_t img;
lv_anim_t anim;
/*picture sequence */
lv_img_dsc_t ** dsc;
int8_t pic_count;
} lv_animimg_t;
/*Image parts*/
enum {
LV_ANIM_IMG_PART_MAIN,
};
typedef uint8_t lv_animimg_part_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create an animation 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 animation image object
*/
lv_obj_t * lv_animimg_create(lv_obj_t * parent);
/*=====================
* Setter functions
*====================*/
/**
* Set the image animation images source.
* @param img pointer to an animation image object
* @param dsc pointer to a series images
* @param num images' number
*/
void lv_animimg_set_src(lv_obj_t * img, lv_img_dsc_t * dsc[], uint8_t num);
/**
* Startup the image animation.
* @param img pointer to an animation image object
*/
void lv_animimg_start(lv_obj_t * obj);
/**
* Set the image animation duration time. unit:ms
* @param img pointer to an animation image object
*/
void lv_animimg_set_duration(lv_obj_t * img, uint32_t duration);
/**
* Set the image animation reapeatly play times.
* @param img pointer to an animation image object
*/
void lv_animimg_set_repeat_count(lv_obj_t * img, uint16_t count);
/*=====================
* Getter functions
*====================*/
#endif /*LV_USE_ANIMIMG*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_ANIM_IMG_H*/

View File

@@ -0,0 +1,401 @@
/**
* @file lv_calendar.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_calendar.h"
#include "../../../lvgl.h"
#if LV_USE_CALENDAR
#include "../../../misc/lv_assert.h"
/*********************
* DEFINES
*********************/
#define LV_CALENDAR_CTRL_TODAY LV_BTNMATRIX_CTRL_CUSTOM_1
#define LV_CALENDAR_CTRL_HIGHLIGHT LV_BTNMATRIX_CTRL_CUSTOM_2
#define MY_CLASS &lv_calendar_class
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_calendar_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void draw_part_begin_event_cb(lv_event_t * e);
static uint8_t get_day_of_week(uint32_t year, uint32_t month, uint32_t day);
static uint8_t get_month_length(int32_t year, int32_t month);
static uint8_t is_leap_year(uint32_t year);
static void highlight_update(lv_obj_t * calendar);
/**********************
* STATIC VARIABLES
**********************/
const lv_obj_class_t lv_calendar_class = {
.constructor_cb = lv_calendar_constructor,
.width_def = (LV_DPI_DEF * 3) / 2,
.height_def = (LV_DPI_DEF * 3) / 2,
.group_def = LV_OBJ_CLASS_GROUP_DEF_TRUE,
.instance_size = sizeof(lv_calendar_t),
.base_class = &lv_obj_class
};
static const char * day_names_def[7] = LV_CALENDAR_DEFAULT_DAY_NAMES;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_obj_t * lv_calendar_create(lv_obj_t * parent)
{
LV_LOG_INFO("begin");
lv_obj_t * obj = lv_obj_class_create_obj(&lv_calendar_class, parent);
lv_obj_class_init_obj(obj);
return obj;
}
/*=====================
* Setter functions
*====================*/
void lv_calendar_set_day_names(lv_obj_t * obj, const char * day_names[])
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_calendar_t * calendar = (lv_calendar_t *)obj;
uint32_t i;
for(i = 0; i < 7; i++) {
calendar->map[i] = day_names[i];
}
lv_obj_invalidate(obj);
}
void lv_calendar_set_today_date(lv_obj_t * obj, uint32_t year, uint32_t month, uint32_t day)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_calendar_t * calendar = (lv_calendar_t *)obj;
calendar->today.year = year;
calendar->today.month = month;
calendar->today.day = day;
highlight_update(obj);
}
void lv_calendar_set_highlighted_dates(lv_obj_t * obj, lv_calendar_date_t highlighted[], uint16_t date_num)
{
LV_ASSERT_NULL(highlighted);
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_calendar_t * calendar = (lv_calendar_t *)obj;
calendar->highlighted_dates = highlighted;
calendar->highlighted_dates_num = date_num;
highlight_update(obj);
}
void lv_calendar_set_showed_date(lv_obj_t * obj, uint32_t year, uint32_t month)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_calendar_t * calendar = (lv_calendar_t *)obj;
calendar->showed_date.year = year;
calendar->showed_date.month = month;
calendar->showed_date.day = 1;
lv_calendar_date_t d;
d.year = calendar->showed_date.year;
d.month = calendar->showed_date.month;
d.day = calendar->showed_date.day;
uint8_t i;
/*Remove the disabled state but revert it for day names*/
lv_btnmatrix_clear_btn_ctrl_all(calendar->btnm, LV_BTNMATRIX_CTRL_DISABLED);
for(i = 0; i < 7; i++) {
lv_btnmatrix_set_btn_ctrl(calendar->btnm, i, LV_BTNMATRIX_CTRL_DISABLED);
}
uint8_t act_mo_len = get_month_length(d.year, d.month);
uint8_t day_first = get_day_of_week(d.year, d.month, 1);
uint8_t c;
for(i = day_first, c = 1; i < act_mo_len + day_first; i++, c++) {
lv_snprintf(calendar->nums[i], sizeof(calendar->nums[0]), "%d", c);
}
uint8_t prev_mo_len = get_month_length(d.year, d.month - 1);
for(i = 0, c = prev_mo_len - day_first + 1; i < day_first; i++, c++) {
lv_snprintf(calendar->nums[i], sizeof(calendar->nums[0]), "%d", c);
lv_btnmatrix_set_btn_ctrl(calendar->btnm, i + 7, LV_BTNMATRIX_CTRL_DISABLED);
}
for(i = day_first + act_mo_len, c = 1; i < 6 * 7; i++, c++) {
lv_snprintf(calendar->nums[i], sizeof(calendar->nums[0]), "%d", c);
lv_btnmatrix_set_btn_ctrl(calendar->btnm, i + 7, LV_BTNMATRIX_CTRL_DISABLED);
}
highlight_update(obj);
/*Reset the focused button if the days changes*/
if(lv_btnmatrix_get_selected_btn(calendar->btnm) != LV_BTNMATRIX_BTN_NONE) {
lv_btnmatrix_set_selected_btn(calendar->btnm, day_first + 7);
}
lv_obj_invalidate(obj);
/* The children of the calendar are probably headers.
* Notify them to let the headers updated to the new date*/
uint32_t child_cnt = lv_obj_get_child_cnt(obj);
for(i = 0; i < child_cnt; i++) {
lv_obj_t * child = lv_obj_get_child(obj, i);
if(child == calendar->btnm) continue;
lv_event_send(child, LV_EVENT_VALUE_CHANGED, obj);
}
}
/*=====================
* Getter functions
*====================*/
lv_obj_t * lv_calendar_get_btnmatrix(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
const lv_calendar_t * calendar = (lv_calendar_t *)obj;
return calendar->btnm;
}
const lv_calendar_date_t * lv_calendar_get_today_date(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
const lv_calendar_t * calendar = (lv_calendar_t *)obj;
return &calendar->today;
}
const lv_calendar_date_t * lv_calendar_get_showed_date(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
const lv_calendar_t * calendar = (lv_calendar_t *)obj;
return &calendar->showed_date;
}
lv_calendar_date_t * lv_calendar_get_highlighted_dates(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_calendar_t * calendar = (lv_calendar_t *)obj;
return calendar->highlighted_dates;
}
uint16_t lv_calendar_get_highlighted_dates_num(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_calendar_t * calendar = (lv_calendar_t *)obj;
return calendar->highlighted_dates_num;
}
lv_res_t lv_calendar_get_pressed_date(const lv_obj_t * obj, lv_calendar_date_t * date)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_calendar_t * calendar = (lv_calendar_t *)obj;
uint16_t d = lv_btnmatrix_get_selected_btn(calendar->btnm);
if(d == LV_BTNMATRIX_BTN_NONE) {
date->year = 0;
date->month = 0;
date->day = 0;
return LV_RES_INV;
}
const char * txt = lv_btnmatrix_get_btn_text(calendar->btnm, lv_btnmatrix_get_selected_btn(calendar->btnm));
if(txt[1] == 0) date->day = txt[0] - '0';
else date->day = (txt[0] - '0') * 10 + (txt[1] - '0');
date->year = calendar->showed_date.year;
date->month = calendar->showed_date.month;
return LV_RES_OK;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_calendar_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
lv_calendar_t * calendar = (lv_calendar_t *)obj;
/*Initialize the allocated 'ext'*/
calendar->today.year = 2020;
calendar->today.month = 1;
calendar->today.day = 1;
calendar->showed_date.year = 2020;
calendar->showed_date.month = 1;
calendar->showed_date.day = 1;
calendar->highlighted_dates = NULL;
calendar->highlighted_dates_num = 0;
lv_memset_00(calendar->nums, sizeof(calendar->nums));
uint8_t i;
uint8_t j = 0;
for(i = 0; i < 8 * 7; i++) {
/*Every 8th string is "\n"*/
if(i != 0 && (i + 1) % 8 == 0) {
calendar->map[i] = "\n";
}
else if(i < 8) {
calendar->map[i] = day_names_def[i];
}
else {
calendar->nums[j][0] = 'x';
calendar->map[i] = calendar->nums[j];
j++;
}
}
calendar->map[8 * 7 - 1] = "";
calendar->btnm = lv_btnmatrix_create(obj);
lv_btnmatrix_set_map(calendar->btnm, calendar->map);
lv_btnmatrix_set_btn_ctrl_all(calendar->btnm, LV_BTNMATRIX_CTRL_CLICK_TRIG | LV_BTNMATRIX_CTRL_NO_REPEAT);
lv_obj_add_event_cb(calendar->btnm, draw_part_begin_event_cb, LV_EVENT_DRAW_PART_BEGIN, NULL);
lv_obj_set_width(calendar->btnm, lv_pct(100));
lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_COLUMN);
lv_obj_set_flex_grow(calendar->btnm, 1);
lv_calendar_set_showed_date(obj, calendar->showed_date.year, calendar->showed_date.month);
lv_calendar_set_today_date(obj, calendar->today.year, calendar->today.month, calendar->today.day);
lv_obj_add_flag(calendar->btnm, LV_OBJ_FLAG_EVENT_BUBBLE);
}
static void draw_part_begin_event_cb(lv_event_t * e)
{
lv_obj_t * obj = lv_event_get_target(e);
lv_obj_draw_part_dsc_t * dsc = lv_event_get_param(e);
if(dsc->part == LV_PART_ITEMS) {
/*Day name styles*/
if(dsc->id < 7) {
dsc->rect_dsc->bg_opa = LV_OPA_TRANSP;
dsc->rect_dsc->border_opa = LV_OPA_TRANSP;
}
else if(lv_btnmatrix_has_btn_ctrl(obj, dsc->id, LV_BTNMATRIX_CTRL_DISABLED)) {
dsc->rect_dsc->bg_opa = LV_OPA_TRANSP;
dsc->rect_dsc->border_opa = LV_OPA_TRANSP;
dsc->label_dsc->color = lv_palette_main(LV_PALETTE_GREY);
}
if(lv_btnmatrix_has_btn_ctrl(obj, dsc->id, LV_CALENDAR_CTRL_HIGHLIGHT)) {
dsc->rect_dsc->bg_opa = LV_OPA_40;
dsc->rect_dsc->bg_color = lv_theme_get_color_primary(obj);
if(lv_btnmatrix_get_selected_btn(obj) == dsc->id) {
dsc->rect_dsc->bg_opa = LV_OPA_70;
}
}
if(lv_btnmatrix_has_btn_ctrl(obj, dsc->id, LV_CALENDAR_CTRL_TODAY)) {
dsc->rect_dsc->border_opa = LV_OPA_COVER;
dsc->rect_dsc->border_color = lv_theme_get_color_primary(obj);
dsc->rect_dsc->border_width += 1;
}
}
}
/**
* Get the number of days in a month
* @param year a year
* @param month a month. The range is basically [1..12] but [-11..0] or [13..24] is also
* supported to handle next/prev. year
* @return [28..31]
*/
static uint8_t get_month_length(int32_t year, int32_t month)
{
month--;
if(month < 0) {
year--; /*Already in the previous year (won't be less then -12 to skip a whole year)*/
month = 12 + month; /*`month` is negative, the result will be < 12*/
}
if(month >= 12) {
year++;
month -= 12;
}
/*month == 1 is february*/
return (month == 1) ? (28 + is_leap_year(year)) : 31 - month % 7 % 2;
}
/**
* Tells whether a year is leap year or not
* @param year a year
* @return 0: not leap year; 1: leap year
*/
static uint8_t is_leap_year(uint32_t year)
{
return (year % 4) || ((year % 100 == 0) && (year % 400)) ? 0 : 1;
}
/**
* Get the day of the week
* @param year a year
* @param month a month [1..12]
* @param day a day [1..32]
* @return [0..6] which means [Sun..Sat] or [Mon..Sun] depending on LV_CALENDAR_WEEK_STARTS_MONDAY
*/
static uint8_t get_day_of_week(uint32_t year, uint32_t month, uint32_t day)
{
uint32_t a = month < 3 ? 1 : 0;
uint32_t b = year - a;
#if LV_CALENDAR_WEEK_STARTS_MONDAY
uint32_t day_of_week = (day + (31 * (month - 2 + 12 * a) / 12) + b + (b / 4) - (b / 100) + (b / 400) - 1) % 7;
#else
uint32_t day_of_week = (day + (31 * (month - 2 + 12 * a) / 12) + b + (b / 4) - (b / 100) + (b / 400)) % 7;
#endif
return day_of_week ;
}
static void highlight_update(lv_obj_t * obj)
{
lv_calendar_t * calendar = (lv_calendar_t *)obj;
uint16_t i;
/*Clear all kind of selection*/
lv_btnmatrix_clear_btn_ctrl_all(calendar->btnm, LV_CALENDAR_CTRL_TODAY | LV_CALENDAR_CTRL_HIGHLIGHT);
uint8_t day_first = get_day_of_week(calendar->showed_date.year, calendar->showed_date.month, 1);
if(calendar->highlighted_dates) {
for(i = 0; i < calendar->highlighted_dates_num; i++) {
if(calendar->highlighted_dates[i].year == calendar->showed_date.year &&
calendar->highlighted_dates[i].month == calendar->showed_date.month) {
lv_btnmatrix_set_btn_ctrl(calendar->btnm, calendar->highlighted_dates[i].day - 1 + day_first + 7, LV_CALENDAR_CTRL_HIGHLIGHT);
}
}
}
if(calendar->showed_date.year == calendar->today.year && calendar->showed_date.month == calendar->today.month) {
lv_btnmatrix_set_btn_ctrl(calendar->btnm, calendar->today.day - 1 + day_first + 7, LV_CALENDAR_CTRL_TODAY);
}
}
#endif /*LV_USE_CALENDAR*/

View File

@@ -0,0 +1,164 @@
/**
* @file lv_calendar.h
*
*/
#ifndef LV_CALENDAR_H
#define LV_CALENDAR_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../widgets/lv_btnmatrix.h"
#if LV_USE_CALENDAR
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**
* Represents a date on the calendar object (platform-agnostic).
*/
typedef struct {
uint16_t year;
int8_t month; /** 1..12*/
int8_t day; /** 1..31*/
} lv_calendar_date_t;
/*Data of calendar*/
typedef struct {
lv_obj_t obj;
lv_obj_t * btnm;
/*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)*/
uint16_t highlighted_dates_num; /*Number of elements in `highlighted_days`*/
const char * map[8 * 7];
char nums [7 * 6][4];
} lv_calendar_t;
extern const lv_obj_class_t lv_calendar_class;
/**********************
* GLOBAL PROTOTYPES
**********************/
lv_obj_t * lv_calendar_create(lv_obj_t * parent);
/*======================
* Add/remove functions
*=====================*/
/*=====================
* Setter functions
*====================*/
/**
* Set the today's date
* @param obj pointer to a calendar object
* @param year today's year
* @param month today's month [1..12]
* @param day today's day [1..31]
*/
void lv_calendar_set_today_date(lv_obj_t * obj, uint32_t year, uint32_t month, uint32_t day);
/**
* Set the currently showed
* @param obj pointer to a calendar object
* @param year today's year
* @param month today's month [1..12]
*/
void lv_calendar_set_showed_date(lv_obj_t * obj, uint32_t year, uint32_t month);
/**
* Set the the highlighted dates
* @param obj pointer to a calendar object
* @param highlighted pointer to an `lv_calendar_date_t` array containing the dates.
* Only the pointer will be saved so this variable can't be local which will be destroyed later.
* @param date_num number of dates in the array
*/
void lv_calendar_set_highlighted_dates(lv_obj_t * obj, lv_calendar_date_t highlighted[], uint16_t date_num);
/**
* Set the name of the days
* @param obj 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 * obj, const char ** day_names);
/*=====================
* Getter functions
*====================*/
/**
* Get the button matrix object of the calendar.
* It shows the dates and day names.
* @param obj pointer to a calendar object
* @return pointer to a the button matrix
*/
lv_obj_t * lv_calendar_get_btnmatrix(const lv_obj_t * obj);
/**
* 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.
*/
const 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.
*/
const lv_calendar_date_t * lv_calendar_get_showed_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 currently pressed day
* @param calendar pointer to a calendar object
* @param date store the pressed date here
* @return LV_RES_OK: there is a valid pressed date; LV_RES_INV: there is no pressed data
*/
lv_res_t lv_calendar_get_pressed_date(const lv_obj_t * calendar, lv_calendar_date_t * date);
/*=====================
* Other functions
*====================*/
/**********************
* MACROS
**********************/
#endif /*LV_USE_CALENDAR*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_CALENDAR_H*/

View File

@@ -0,0 +1,149 @@
/**
* @file lv_calendar_header_arrow.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_calendar_header_arrow.h"
#if LV_USE_CALENDAR_HEADER_ARROW
#include "lv_calendar.h"
#include "../../../widgets/lv_btn.h"
#include "../../../widgets/lv_label.h"
#include "../../layouts/flex/lv_flex.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void my_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void month_event_cb(lv_event_t * e);
static void value_changed_event_cb(lv_event_t * e);
/**********************
* STATIC VARIABLES
**********************/
const lv_obj_class_t lv_calendar_header_arrow_class = {
.base_class = &lv_obj_class,
.constructor_cb = my_constructor,
.width_def = LV_PCT(100),
.height_def = LV_DPI_DEF / 3
};
static const char * month_names_def[12] = LV_CALENDAR_DEFAULT_MONTH_NAMES;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_obj_t * lv_calendar_header_arrow_create(lv_obj_t * parent)
{
lv_obj_t * obj = lv_obj_class_create_obj(&lv_calendar_header_arrow_class, parent);
lv_obj_class_init_obj(obj);
return obj;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void my_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_TRACE_OBJ_CREATE("begin");
LV_UNUSED(class_p);
lv_obj_move_to_index(obj, 0);
lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_ROW);
lv_obj_set_flex_align(obj, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_START);
lv_obj_t * mo_prev = lv_btn_create(obj);
lv_obj_set_style_bg_img_src(mo_prev, LV_SYMBOL_LEFT, 0);
lv_obj_set_height(mo_prev, lv_pct(100));
lv_obj_update_layout(mo_prev);
lv_coord_t btn_size = lv_obj_get_height(mo_prev);
lv_obj_set_width(mo_prev, btn_size);
lv_obj_add_event_cb(mo_prev, month_event_cb, LV_EVENT_CLICKED, NULL);
lv_obj_clear_flag(mo_prev, LV_OBJ_FLAG_CLICK_FOCUSABLE);
lv_obj_t * label = lv_label_create(obj);
lv_label_set_long_mode(label, LV_LABEL_LONG_SCROLL_CIRCULAR);
lv_obj_set_style_text_align(label, LV_TEXT_ALIGN_CENTER, 0);
lv_obj_set_flex_grow(label, 1);
lv_obj_t * mo_next = lv_btn_create(obj);
lv_obj_set_style_bg_img_src(mo_next, LV_SYMBOL_RIGHT, 0);
lv_obj_set_size(mo_next, btn_size, btn_size);
lv_obj_add_event_cb(mo_next, month_event_cb, LV_EVENT_CLICKED, NULL);
lv_obj_clear_flag(mo_next, LV_OBJ_FLAG_CLICK_FOCUSABLE);
lv_obj_add_event_cb(obj, value_changed_event_cb, LV_EVENT_VALUE_CHANGED, NULL);
/*Refresh the drop downs*/
lv_event_send(obj, LV_EVENT_VALUE_CHANGED, NULL);
}
static void month_event_cb(lv_event_t * e)
{
lv_obj_t * btn = lv_event_get_target(e);
lv_obj_t * header = lv_obj_get_parent(btn);
lv_obj_t * calendar = lv_obj_get_parent(header);
const lv_calendar_date_t * d;
d = lv_calendar_get_showed_date(calendar);
lv_calendar_date_t newd = *d;
/*The last child is the right button*/
if(lv_obj_get_child(header, 0) == btn) {
if(newd.month == 1) {
newd.month = 12;
newd.year --;
}
else {
newd.month --;
}
}
else {
if(newd.month == 12) {
newd.month = 1;
newd.year ++;
}
else {
newd.month ++;
}
}
lv_calendar_set_showed_date(calendar, newd.year, newd.month);
lv_obj_t * label = lv_obj_get_child(header, 1);
lv_label_set_text_fmt(label, "%d %s", newd.year, month_names_def[newd.month - 1]);
}
static void value_changed_event_cb(lv_event_t * e)
{
lv_obj_t * header = lv_event_get_target(e);
lv_obj_t * calendar = lv_obj_get_parent(header);
const lv_calendar_date_t * cur_date = lv_calendar_get_showed_date(calendar);
lv_obj_t * label = lv_obj_get_child(header, 1);
lv_label_set_text_fmt(label, "%d %s", cur_date->year, month_names_def[cur_date->month - 1]);
}
#endif /*LV_USE_CALENDAR_HEADER_ARROW*/

View File

@@ -0,0 +1,49 @@
/**
* @file lv_calendar_header_arrow.h
*
*/
#ifndef LV_CALENDAR_HEADER_ARROW_H
#define LV_CALENDAR_HEADER_ARROW_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../core/lv_obj.h"
#if LV_USE_CALENDAR_HEADER_ARROW
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
extern const lv_obj_class_t lv_calendar_header_arrow_class;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a calendar header with drop-drowns to select the year and month
* @param parent pointer to a calendar object.
* @return the created header
*/
lv_obj_t * lv_calendar_header_arrow_create(lv_obj_t * parent);
/**********************
* MACROS
**********************/
#endif /*LV_USE_CALENDAR_HEADER_ARROW*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_CALENDAR_H*/

View File

@@ -0,0 +1,142 @@
/**
* @file lv_calendar_obj_dropdown.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_calendar_header_dropdown.h"
#if LV_USE_CALENDAR_HEADER_DROPDOWN
#include "lv_calendar.h"
#include "../../../widgets/lv_dropdown.h"
#include "../../layouts/flex/lv_flex.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void my_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void year_event_cb(lv_event_t * e);
static void month_event_cb(lv_event_t * e);
static void value_changed_event_cb(lv_event_t * e);
/**********************
* STATIC VARIABLES
**********************/
const lv_obj_class_t lv_calendar_header_dropdown_class = {
.base_class = &lv_obj_class,
.width_def = LV_PCT(100),
.height_def = LV_SIZE_CONTENT,
.constructor_cb = my_constructor
};
static const char * month_list = "01\n02\n03\n04\n05\n06\n07\n08\n09\n10\n11\n12";
static const char * year_list = {
"2023\n2022\n2021\n"
"2020\n2019\n2018\n2017\n2016\n2015\n2014\n2013\n2012\n2011\n2010\n2009\n2008\n2007\n2006\n2005\n2004\n2003\n2002\n2001\n"
"2000\n1999\n1998\n1997\n1996\n1995\n1994\n1993\n1992\n1991\n1990\n1989\n1988\n1987\n1986\n1985\n1984\n1983\n1982\n1981\n"
"1980\n1979\n1978\n1977\n1976\n1975\n1974\n1973\n1972\n1971\n1970\n1969\n1968\n1967\n1966\n1965\n1964\n1963\n1962\n1961\n"
"1960\n1959\n1958\n1957\n1956\n1955\n1954\n1953\n1952\n1951\n1950\n1949\n1948\n1947\n1946\n1945\n1944\n1943\n1942\n1941\n"
"1940\n1939\n1938\n1937\n1936\n1935\n1934\n1933\n1932\n1931\n1930\n1929\n1928\n1927\n1926\n1925\n1924\n1923\n1922\n1921\n"
"1920\n1919\n1918\n1917\n1916\n1915\n1914\n1913\n1912\n1911\n1910\n1909\n1908\n1907\n1906\n1905\n1904\n1903\n1902\n1901"
};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_obj_t * lv_calendar_header_dropdown_create(lv_obj_t * parent)
{
lv_obj_t * obj = lv_obj_class_create_obj(&lv_calendar_header_dropdown_class, parent);
lv_obj_class_init_obj(obj);
return obj;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void my_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_TRACE_OBJ_CREATE("begin");
LV_UNUSED(class_p);
lv_obj_t * calendar = lv_obj_get_parent(obj);
lv_obj_move_to_index(obj, 0);
lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_ROW);
lv_obj_t * year_dd = lv_dropdown_create(obj);
lv_dropdown_set_options(year_dd, year_list);
lv_obj_add_event_cb(year_dd, year_event_cb, LV_EVENT_VALUE_CHANGED, calendar);
lv_obj_set_flex_grow(year_dd, 1);
lv_obj_t * month_dd = lv_dropdown_create(obj);
lv_dropdown_set_options(month_dd, month_list);
lv_obj_add_event_cb(month_dd, month_event_cb, LV_EVENT_VALUE_CHANGED, calendar);
lv_obj_set_flex_grow(month_dd, 1);
lv_obj_add_event_cb(obj, value_changed_event_cb, LV_EVENT_VALUE_CHANGED, NULL);
/*Refresh the drop downs*/
lv_event_send(obj, LV_EVENT_VALUE_CHANGED, NULL);
}
static void month_event_cb(lv_event_t * e)
{
lv_obj_t * dropdown = lv_event_get_target(e);
lv_obj_t * calendar = lv_event_get_user_data(e);
uint16_t sel = lv_dropdown_get_selected(dropdown);
const lv_calendar_date_t * d;
d = lv_calendar_get_showed_date(calendar);
lv_calendar_date_t newd = *d;
newd.month = sel + 1;
lv_calendar_set_showed_date(calendar, newd.year, newd.month);
}
static void year_event_cb(lv_event_t * e)
{
lv_obj_t * dropdown = lv_event_get_target(e);
lv_obj_t * calendar = lv_event_get_user_data(e);
uint16_t sel = lv_dropdown_get_selected(dropdown);
const lv_calendar_date_t * d;
d = lv_calendar_get_showed_date(calendar);
lv_calendar_date_t newd = *d;
newd.year = 2023 - sel;
lv_calendar_set_showed_date(calendar, newd.year, newd.month);
}
static void value_changed_event_cb(lv_event_t * e)
{
lv_obj_t * header = lv_event_get_target(e);
lv_obj_t * calendar = lv_obj_get_parent(header);
const lv_calendar_date_t * cur_date = lv_calendar_get_showed_date(calendar);
lv_obj_t * year_dd = lv_obj_get_child(header, 0);
lv_dropdown_set_selected(year_dd, 2023 - cur_date->year);
lv_obj_t * month_dd = lv_obj_get_child(header, 1);
lv_dropdown_set_selected(month_dd, cur_date->month - 1);
}
#endif /*LV_USE_CALENDAR_HEADER_ARROW*/

View File

@@ -0,0 +1,49 @@
/**
* @file lv_calendar_header_dropdown.h
*
*/
#ifndef LV_CALENDAR_HEADER_DROPDOWN_H
#define LV_CALENDAR_HEADER_DROPDOWN_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../core/lv_obj.h"
#if LV_USE_CALENDAR_HEADER_DROPDOWN
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
extern const lv_obj_class_t lv_calendar_header_dropdown_class;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a calendar header with drop-drowns to select the year and month
* @param parent pointer to a calendar object.
* @return the created header
*/
lv_obj_t * lv_calendar_header_dropdown_create(lv_obj_t * parent);
/**********************
* MACROS
**********************/
#endif /*LV_USE_CALENDAR_HEADER_ARROW*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_CALENDAR_H*/

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,456 @@
/**
* @file lv_chart.h
*
*/
#ifndef LV_CHART_H
#define LV_CHART_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../lvgl.h"
#if LV_USE_CHART != 0
/*********************
* DEFINES
*********************/
/**Default value of points. Can be used to not draw a point*/
#define LV_CHART_POINT_NONE (INT16_MAX)
LV_EXPORT_CONST_INT(LV_CHART_POINT_NONE);
/**********************
* TYPEDEFS
**********************/
/**
* Chart types
*/
enum {
LV_CHART_TYPE_NONE, /**< Don't draw the series*/
LV_CHART_TYPE_LINE, /**< Connect the points with lines*/
LV_CHART_TYPE_BAR, /**< Draw columns*/
LV_CHART_TYPE_SCATTER, /**< Draw points and lines in 2D (x,y coordinates)*/
};
typedef uint8_t lv_chart_type_t;
/**
* Chart update mode for `lv_chart_set_next`
*/
enum {
LV_CHART_UPDATE_MODE_SHIFT, /**< Shift old data to the left and add the new one the right*/
LV_CHART_UPDATE_MODE_CIRCULAR, /**< Add the new data in a circular way*/
};
typedef uint8_t lv_chart_update_mode_t;
/**
* Enumeration of the axis'
*/
enum {
LV_CHART_AXIS_PRIMARY_Y = 0x00,
LV_CHART_AXIS_SECONDARY_Y = 0x01,
LV_CHART_AXIS_PRIMARY_X = 0x02,
LV_CHART_AXIS_SECONDARY_X = 0x04,
_LV_CHART_AXIS_LAST
};
typedef uint8_t lv_chart_axis_t;
/**
* Descriptor a chart series
*/
typedef struct {
lv_coord_t * x_points;
lv_coord_t * y_points;
lv_color_t color;
uint16_t start_point;
uint8_t hidden : 1;
uint8_t x_ext_buf_assigned : 1;
uint8_t y_ext_buf_assigned : 1;
uint8_t x_axis_sec : 1;
uint8_t y_axis_sec : 1;
} lv_chart_series_t;
typedef struct {
lv_point_t pos;
uint16_t point_id;
lv_color_t color;
lv_chart_series_t * ser;
lv_dir_t dir;
uint8_t pos_set: 1; /*1: pos is set; 0: point_id is set*/
} lv_chart_cursor_t;
typedef struct {
lv_coord_t major_len;
lv_coord_t minor_len;
lv_coord_t draw_size;
uint32_t minor_cnt : 15;
uint32_t major_cnt : 15;
uint32_t label_en : 1;
} lv_chart_tick_dsc_t;
typedef struct {
lv_obj_t obj;
lv_ll_t series_ll; /**< Linked list for the series (stores lv_chart_series_t)*/
lv_ll_t cursor_ll; /**< Linked list for the cursors (stores lv_chart_cursor_t)*/
lv_chart_tick_dsc_t tick[4];
lv_coord_t ymin[2];
lv_coord_t ymax[2];
lv_coord_t xmin[2];
lv_coord_t xmax[2];
uint16_t pressed_point_id;
uint16_t hdiv_cnt; /**< Number of horizontal division lines*/
uint16_t vdiv_cnt; /**< Number of vertical division lines*/
uint16_t point_cnt; /**< Point number in a data line*/
uint16_t zoom_x;
uint16_t zoom_y;
lv_chart_type_t type : 3; /**< Line or column chart*/
lv_chart_update_mode_t update_mode : 1;
} lv_chart_t;
extern const lv_obj_class_t lv_chart_class;
/**
* `type` field in `lv_obj_draw_part_dsc_t` if `class_p = lv_chart_class`
* Used in `LV_EVENT_DRAW_PART_BEGIN` and `LV_EVENT_DRAW_PART_END`
*/
typedef enum {
LV_CHART_DRAW_PART_DIV_LINE_INIT, /**< Used before/after drawn the div lines*/
LV_CHART_DRAW_PART_DIV_LINE_HOR, /**< Used for each horizontal division lines*/
LV_CHART_DRAW_PART_DIV_LINE_VER, /**< Used for each vertical division lines*/
LV_CHART_DRAW_PART_LINE_AND_POINT, /**< Used on line and scatter charts for lines and points*/
LV_CHART_DRAW_PART_BAR, /**< Used on bar charts for the rectangles*/
LV_CHART_DRAW_PART_CURSOR, /**< Used on cursor lines and points*/
LV_CHART_DRAW_PART_TICK_LABEL, /**< Used on tick lines and labels*/
} lv_chart_draw_part_type_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a chart objects
* @param parent pointer to an object, it will be the parent of the new chart
* @return pointer to the created chart
*/
lv_obj_t * lv_chart_create(lv_obj_t * parent);
/**
* Set a new type for a chart
* @param obj 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 * obj, lv_chart_type_t type);
/**
* Set the number of points on a data line on a chart
* @param obj pointer to a chart object
* @param cnt new number of points on the data lines
*/
void lv_chart_set_point_count(lv_obj_t * obj, uint16_t cnt);
/**
* Set the minimal and maximal y values on an axis
* @param obj pointer to a chart object
* @param axis `LV_CHART_AXIS_PRIMARY_Y` or `LV_CHART_AXIS_SECONDARY_Y`
* @param min minimum value of the y axis
* @param max maximum value of the y axis
*/
void lv_chart_set_range(lv_obj_t * obj, lv_chart_axis_t axis, lv_coord_t min, lv_coord_t max);
/**
* Set update mode of the chart object. Affects
* @param obj pointer to a chart object
* @param mode the update mode
*/
void lv_chart_set_update_mode(lv_obj_t * obj, lv_chart_update_mode_t update_mode);
/**
* Set the number of horizontal and vertical division lines
* @param obj pointer to a chart 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 * obj, uint8_t hdiv, uint8_t vdiv);
/**
* Zoom into the chart in X direction
* @param obj pointer to a chart object
* @param zoom_x zoom in x direction. LV_ZOOM_NONE or 256 for no zoom, 512 double zoom
*/
void lv_chart_set_zoom_x(lv_obj_t * obj, uint16_t zoom_x);
/**
* Zoom into the chart in Y direction
* @param obj pointer to a chart object
* @param zoom_y zoom in y direction. LV_ZOOM_NONE or 256 for no zoom, 512 double zoom
*/
void lv_chart_set_zoom_y(lv_obj_t * obj, uint16_t zoom_y);
/**
* Get X zoom of a chart
* @param obj pointer to a chart object
* @return the X zoom value
*/
uint16_t lv_chart_get_zoom_x(const lv_obj_t * obj);
/**
* Get Y zoom of a chart
* @param obj pointer to a chart object
* @return the Y zoom value
*/
uint16_t lv_chart_get_zoom_y(const lv_obj_t * obj);
/**
* Set the number of tick lines on an axis
* @param obj pointer to a chart object
* @param axis an axis which ticks count should be set
* @param major_len length of major ticks
* @param minor_len length of minor ticks
* @param major_cnt number of major ticks on the axis
* @param minor_cnt number of minor ticks between two major ticks
* @param label_en true: enable label drawing on major ticks
* @param draw_size extra size required to draw the tick and labels
* (start with 20 px and increase if the ticks/labels are clipped)
*/
void lv_chart_set_axis_tick(lv_obj_t * obj, lv_chart_axis_t axis, lv_coord_t major_len, lv_coord_t minor_len,
lv_coord_t major_cnt, lv_coord_t minor_cnt, bool label_en, lv_coord_t draw_size);
/**
* Get the type of a chart
* @param obj 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 * obj);
/**
* 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_count(const lv_obj_t * obj);
/**
* Get the current index of the x-axis start point in the data array
* @param chart pointer to a chart object
* @param ser pointer to a data series on 'chart'
* @return the index of the current x start point in the data array
*/
uint16_t lv_chart_get_x_start_point(const lv_obj_t * obj, lv_chart_series_t * ser);
/**
* Get the position of a point to the chart.
* @param chart pointer to a chart object
* @param ser pointer to series
* @param id the index.
* @param p_out store the result position here
*/
void lv_chart_get_point_pos_by_id(lv_obj_t * obj, lv_chart_series_t * ser, uint16_t id, lv_point_t * p_out);
/**
* Refresh a chart if its data line has changed
* @param chart pointer to chart object
*/
void lv_chart_refresh(lv_obj_t * obj);
/*======================
* Series
*=====================*/
/**
* Allocate and add a data series to the chart
* @param obj pointer to a chart object
* @param color color of the data series
* @param axis the y axis to which the series should be attached (::LV_CHART_AXIS_PRIMARY_Y or ::LV_CHART_AXIS_SECONDARY_Y)
* @return pointer to the allocated data series
*/
lv_chart_series_t * lv_chart_add_series(lv_obj_t * obj, lv_color_t color, lv_chart_axis_t axis);
/**
* Deallocate and remove a data series from a chart
* @param chart pointer to a chart object
* @param series pointer to a data series on 'chart'
*/
void lv_chart_remove_series(lv_obj_t * obj, lv_chart_series_t * series);
/**
* Hide/Unhide a single series of a chart.
* @param obj pointer to a chart object.
* @param series pointer to a series object
* @param hide true: hide the series
*/
void lv_chart_hide_series(lv_obj_t * chart, lv_chart_series_t * series, bool hide);
/**
* Change the color of a series
* @param obj pointer to a chart object.
* @param series pointer to a series object
* @param color the new color of the series
*/
void lv_chart_set_series_color(lv_obj_t * chart, lv_chart_series_t * series, lv_color_t color);
/**
* Set the index of the x-axis start point in the data array.
* This point will be considers the first (left) point and the other points will be drawn after it.
* @param obj pointer to a chart object
* @param ser pointer to a data series on 'chart'
* @param id the index of the x point in the data array
*/
void lv_chart_set_x_start_point(lv_obj_t * obj, lv_chart_series_t * ser, uint16_t id);
/**
* Get the next series.
* @param chart pointer to a chart
* @param ser the previous series or NULL to get the first
* @return the next series or NULL if there is no more.
*/
lv_chart_series_t * lv_chart_get_series_next(const lv_obj_t * chart, const lv_chart_series_t * ser);
/*=====================
* Cursor
*====================*/
/**
* Add a cursor with a given color
* @param obj pointer to chart object
* @param color color of the cursor
* @param dir direction of the cursor. `LV_DIR_RIGHT/LEFT/TOP/DOWN/HOR/VER/ALL`. OR-ed values are possible
* @return pointer to the created cursor
*/
lv_chart_cursor_t * lv_chart_add_cursor(lv_obj_t * obj, lv_color_t color, lv_dir_t dir);
/**
* Set the coordinate of the cursor with respect to the paddings
* @param obj pointer to a chart object
* @param cursor pointer to the cursor
* @param pos the new coordinate of cursor relative the the chart
*/
void lv_chart_set_cursor_pos(lv_obj_t * chart, lv_chart_cursor_t * cursor, lv_point_t * pos);
/**
* Stick the cursor to a point
* @param obj pointer to a chart object
* @param cursor pointer to the cursor
* @param ser pointer to a series
* @param point_id the point's index or `LV_CHART_POINT_NONE` to not assign to any points.
*/
void lv_chart_set_cursor_point(lv_obj_t * chart, lv_chart_cursor_t * cursor, lv_chart_series_t * ser,
uint16_t point_id);
/**
* Get the coordinate of the cursor with respect to the paddings
* @param obj pointer to a chart object
* @param cursor pointer to cursor
* @return coordinate of the cursor as lv_point_t
*/
lv_point_t lv_chart_get_cursor_point(lv_obj_t * chart, lv_chart_cursor_t * cursor);
/*=====================
* Set/Get value(s)
*====================*/
/**
* Initialize all data points of a series with a value
* @param obj pointer to chart object
* @param ser pointer to a data series on 'chart'
* @param value the new value for all points. `LV_CHART_POINT_NONE` can be used to hide the points.
*/
void lv_chart_set_all_value(lv_obj_t * obj, lv_chart_series_t * ser, lv_coord_t value);
/**
* Set the next point's Y value according to the update mode policy.
* @param obj pointer to chart object
* @param ser pointer to a data series on 'chart'
* @param value the new value of the next data
*/
void lv_chart_set_next_value(lv_obj_t * obj, lv_chart_series_t * ser, lv_coord_t value);
/**
* Set the next point's X and Y value according to the update mode policy.
* @param obj pointer to chart object
* @param ser pointer to a data series on 'chart'
* @param x_value the new X value of the next data
* @param y_value the new Y value of the next data
*/
void lv_chart_set_next_value2(lv_obj_t * obj, lv_chart_series_t * ser, lv_coord_t x_value, lv_coord_t y_value);
/**
* Set an individual point's y value of a chart's series directly based on its index
* @param obj pointer to a chart object
* @param ser pointer to a data series on 'chart'
* @param id the index of the x point in the array
* @param value value to assign to array point
*/
void lv_chart_set_value_by_id(lv_obj_t * obj, lv_chart_series_t * ser, uint16_t id, lv_coord_t value);
/**
* Set an individual point's x and y value of a chart's series directly based on its index
* Can be used only with `LV_CHART_TYPE_SCATTER`.
* @param obj pointer to chart object
* @param ser pointer to a data series on 'chart'
* @param id the index of the x point in the array
* @param x_value the new X value of the next data
* @param y_value the new Y value of the next data
*/
void lv_chart_set_value_by_id2(lv_obj_t * obj, lv_chart_series_t * ser, uint16_t id, lv_coord_t x_value,
lv_coord_t y_value);
/**
* Set an external array for the y data points to use for the chart
* NOTE: It is the users responsibility to make sure the `point_cnt` matches the external array size.
* @param obj pointer to a chart object
* @param ser pointer to a data series on 'chart'
* @param array external array of points for chart
*/
void lv_chart_set_ext_y_array(lv_obj_t * obj, lv_chart_series_t * ser, lv_coord_t array[]);
/**
* Set an external array for the x data points to use for the chart
* NOTE: It is the users responsibility to make sure the `point_cnt` matches the external array size.
* @param obj pointer to a chart object
* @param ser pointer to a data series on 'chart'
* @param array external array of points for chart
*/
void lv_chart_set_ext_x_array(lv_obj_t * obj, lv_chart_series_t * ser, lv_coord_t array[]);
/**
* Get the array of y values of a series
* @param obj pointer to a chart object
* @param ser pointer to a data series on 'chart'
* @return the array of values with 'point_count' elements
*/
lv_coord_t * lv_chart_get_y_array(const lv_obj_t * obj, lv_chart_series_t * ser);
/**
* Get the array of x values of a series
* @param obj pointer to a chart object
* @param ser pointer to a data series on 'chart'
* @return the array of values with 'point_count' elements
*/
lv_coord_t * lv_chart_get_x_array(const lv_obj_t * obj, lv_chart_series_t * ser);
/**
* Get the index of the currently pressed point. It's the same for every series.
* @param obj pointer to a chart object
* @return the index of the point [0 .. point count] or LV_CHART_POINT_ID_NONE if no point is being pressed
*/
uint32_t lv_chart_get_pressed_point(const lv_obj_t * obj);
/**********************
* MACROS
**********************/
#endif /*LV_USE_CHART*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_CHART_H*/

View File

@@ -0,0 +1,709 @@
/**
* @file lv_colorwheel.c
*
* Based on the work of @AloyseTech and @paulpv.
*/
/*********************
* INCLUDES
*********************/
#include "lv_colorwheel.h"
#if LV_USE_COLORWHEEL
#include "../../../misc/lv_assert.h"
/*********************
* DEFINES
*********************/
#define MY_CLASS &lv_colorwheel_class
#define LV_CPICKER_DEF_QF 3
/**
* The OUTER_MASK_WIDTH define is required to assist with the placing of a mask over the outer ring of the widget as when the
* multicoloured radial lines are calculated for the outer ring of the widget their lengths are jittering because of the
* integer based arithmetic. From tests the maximum delta was found to be 2 so the current value is set to 3 to achieve
* appropriate masking.
*/
#define OUTER_MASK_WIDTH 3
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_colorwheel_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_colorwheel_event(const lv_obj_class_t * class_p, lv_event_t * e);
static void draw_disc_grad(lv_event_t * e);
static void draw_knob(lv_event_t * e);
static void invalidate_knob(lv_obj_t * obj);
static lv_area_t get_knob_area(lv_obj_t * obj);
static void next_color_mode(lv_obj_t * obj);
static lv_res_t double_click_reset(lv_obj_t * obj);
static void refr_knob_pos(lv_obj_t * obj);
static lv_color_t angle_to_mode_color_fast(lv_obj_t * obj, uint16_t angle);
static uint16_t get_angle(lv_obj_t * obj);
/**********************
* STATIC VARIABLES
**********************/
const lv_obj_class_t lv_colorwheel_class = {.instance_size = sizeof(lv_colorwheel_t), .base_class = &lv_obj_class,
.constructor_cb = lv_colorwheel_constructor,
.event_cb = lv_colorwheel_event,
.width_def = LV_DPI_DEF * 2,
.height_def = LV_DPI_DEF * 2,
.editable = LV_OBJ_CLASS_EDITABLE_TRUE,
};
static bool create_knob_recolor;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Create a color_picker object
* @param par pointer to an object, it will be the parent of the new color_picker
* @return pointer to the created color_picker
*/
lv_obj_t * lv_colorwheel_create(lv_obj_t * parent, bool knob_recolor)
{
LV_LOG_INFO("begin");
create_knob_recolor = knob_recolor;
lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
lv_obj_class_init_obj(obj);
return obj;
}
/*=====================
* Setter functions
*====================*/
/**
* Set the current hsv of a color wheel.
* @param colorwheel pointer to color wheel object
* @param color current selected hsv
* @return true if changed, otherwise false
*/
bool lv_colorwheel_set_hsv(lv_obj_t * obj, lv_color_hsv_t hsv)
{
if(hsv.h > 360) hsv.h %= 360;
if(hsv.s > 100) hsv.s = 100;
if(hsv.v > 100) hsv.v = 100;
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_colorwheel_t * colorwheel = (lv_colorwheel_t *)obj;
if(colorwheel->hsv.h == hsv.h && colorwheel->hsv.s == hsv.s && colorwheel->hsv.v == hsv.v) return false;
colorwheel->hsv = hsv;
refr_knob_pos(obj);
lv_obj_invalidate(obj);
return true;
}
/**
* Set the current color of a color wheel.
* @param colorwheel pointer to color wheel object
* @param color current selected color
* @return true if changed, otherwise false
*/
bool lv_colorwheel_set_rgb(lv_obj_t * obj, lv_color_t color)
{
lv_color32_t c32;
c32.full = lv_color_to32(color);
return lv_colorwheel_set_hsv(obj, lv_color_rgb_to_hsv(c32.ch.red, c32.ch.green, c32.ch.blue));
}
/**
* Set the current color mode.
* @param colorwheel pointer to color wheel object
* @param mode color mode (hue/sat/val)
*/
void lv_colorwheel_set_mode(lv_obj_t * obj, lv_colorwheel_mode_t mode)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_colorwheel_t * colorwheel = (lv_colorwheel_t *)obj;
colorwheel->mode = mode;
refr_knob_pos(obj);
lv_obj_invalidate(obj);
}
/**
* Set if the color mode is changed on long press on center
* @param colorwheel pointer to color wheel object
* @param fixed color mode cannot be changed on long press
*/
void lv_colorwheel_set_mode_fixed(lv_obj_t * obj, bool fixed)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_colorwheel_t * colorwheel = (lv_colorwheel_t *)obj;
colorwheel->mode_fixed = fixed;
}
/*=====================
* Getter functions
*====================*/
/**
* Get the current selected hsv of a color wheel.
* @param colorwheel pointer to color wheel object
* @return current selected hsv
*/
lv_color_hsv_t lv_colorwheel_get_hsv(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_colorwheel_t * colorwheel = (lv_colorwheel_t *)obj;
return colorwheel->hsv;
}
/**
* Get the current selected color of a color wheel.
* @param colorwheel pointer to color wheel object
* @return color current selected color
*/
lv_color_t lv_colorwheel_get_rgb(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_colorwheel_t * colorwheel = (lv_colorwheel_t *)obj;
return lv_color_hsv_to_rgb(colorwheel->hsv.h, colorwheel->hsv.s, colorwheel->hsv.v);
}
/**
* Get the current color mode.
* @param colorwheel pointer to color wheel object
* @return color mode (hue/sat/val)
*/
lv_colorwheel_mode_t lv_colorwheel_get_color_mode(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_colorwheel_t * colorwheel = (lv_colorwheel_t *)obj;
return colorwheel->mode;
}
/**
* Get if the color mode is changed on long press on center
* @param colorwheel pointer to color wheel object
* @return mode cannot be changed on long press
*/
bool lv_colorwheel_get_color_mode_fixed(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_colorwheel_t * colorwheel = (lv_colorwheel_t *)obj;
return colorwheel->mode_fixed;
}
/*=====================
* Other functions
*====================*/
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_colorwheel_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
lv_colorwheel_t * colorwheel = (lv_colorwheel_t *)obj;
colorwheel->hsv.h = 0;
colorwheel->hsv.s = 100;
colorwheel->hsv.v = 100;
colorwheel->mode = LV_COLORWHEEL_MODE_HUE;
colorwheel->mode_fixed = 0;
colorwheel->last_click_time = 0;
colorwheel->last_change_time = 0;
colorwheel->knob.recolor = create_knob_recolor;
lv_obj_add_flag(obj, LV_OBJ_FLAG_ADV_HITTEST);
lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLL_CHAIN);
refr_knob_pos(obj);
}
static void draw_disc_grad(lv_event_t * e)
{
lv_obj_t * obj = lv_event_get_target(e);
const lv_area_t * clip_area = lv_event_get_param(e);
lv_coord_t w = lv_obj_get_width(obj);
lv_coord_t h = lv_obj_get_height(obj);
lv_coord_t cx = obj->coords.x1 + w / 2;
lv_coord_t cy = obj->coords.y1 + h / 2;
lv_coord_t r = w / 2;
lv_draw_line_dsc_t line_dsc;
lv_draw_line_dsc_init(&line_dsc);
lv_obj_init_draw_line_dsc(obj, LV_PART_MAIN, &line_dsc);
line_dsc.width = (r * 628 / (256 / LV_CPICKER_DEF_QF)) / 100;
line_dsc.width += 2;
uint16_t i;
uint32_t a = 0;
lv_coord_t cir_w = lv_obj_get_style_arc_width(obj, LV_PART_MAIN);
#if LV_DRAW_COMPLEX
/*Mask outer and inner ring of widget to tidy up ragged edges of lines while drawing outer ring*/
lv_draw_mask_radius_param_t mask_out_param;
lv_draw_mask_radius_init(&mask_out_param, &obj->coords, LV_RADIUS_CIRCLE, false);
int16_t mask_out_id = lv_draw_mask_add(&mask_out_param, 0);
lv_area_t mask_area;
lv_area_copy(&mask_area, &obj->coords);
mask_area.x1 += cir_w;
mask_area.x2 -= cir_w;
mask_area.y1 += cir_w;
mask_area.y2 -= cir_w;
lv_draw_mask_radius_param_t mask_in_param;
lv_draw_mask_radius_init(&mask_in_param, &mask_area, LV_RADIUS_CIRCLE, true);
int16_t mask_in_id = lv_draw_mask_add(&mask_in_param, 0);
/*The inner and outer line ends will be masked out.
*So make lines a little bit longer because the masking makes a more even result*/
lv_coord_t cir_w_extra = line_dsc.width;
#else
lv_coord_t cir_w_extra = 0;
#endif
for(i = 0; i <= 256; i += LV_CPICKER_DEF_QF, a += 360 * LV_CPICKER_DEF_QF) {
line_dsc.color = angle_to_mode_color_fast(obj, i);
uint16_t angle_trigo = (uint16_t)(a >> 8); /*i * 360 / 256 is the scale to apply, but we can skip multiplication here*/
lv_point_t p[2];
p[0].x = cx + ((r + cir_w_extra) * lv_trigo_sin(angle_trigo) >> LV_TRIGO_SHIFT);
p[0].y = cy + ((r + cir_w_extra) * lv_trigo_cos(angle_trigo) >> LV_TRIGO_SHIFT);
p[1].x = cx + ((r - cir_w - cir_w_extra) * lv_trigo_sin(angle_trigo) >> LV_TRIGO_SHIFT);
p[1].y = cy + ((r - cir_w - cir_w_extra) * lv_trigo_cos(angle_trigo) >> LV_TRIGO_SHIFT);
lv_draw_line(&p[0], &p[1], clip_area, &line_dsc);
}
#if LV_DRAW_COMPLEX
lv_draw_mask_free_param(&mask_out_param);
lv_draw_mask_free_param(&mask_in_param);
lv_draw_mask_remove_id(mask_out_id);
lv_draw_mask_remove_id(mask_in_id);
#endif
}
static void draw_knob(lv_event_t * e)
{
lv_obj_t * obj = lv_event_get_target(e);
const lv_area_t * clip_area = lv_event_get_param(e);
lv_colorwheel_t * colorwheel = (lv_colorwheel_t *)obj;
lv_draw_rect_dsc_t cir_dsc;
lv_draw_rect_dsc_init(&cir_dsc);
lv_obj_init_draw_rect_dsc(obj, LV_PART_KNOB, &cir_dsc);
cir_dsc.radius = LV_RADIUS_CIRCLE;
if(colorwheel->knob.recolor) {
cir_dsc.bg_color = lv_colorwheel_get_rgb(obj);
}
lv_area_t knob_area = get_knob_area(obj);
lv_draw_rect(&knob_area, clip_area, &cir_dsc);
}
static void invalidate_knob(lv_obj_t * obj)
{
lv_area_t knob_area = get_knob_area(obj);
lv_obj_invalidate_area(obj, &knob_area);
}
static lv_area_t get_knob_area(lv_obj_t * obj)
{
lv_colorwheel_t * colorwheel = (lv_colorwheel_t *)obj;
/*Get knob's radius*/
uint16_t r = 0;
r = lv_obj_get_style_arc_width(obj, LV_PART_MAIN) / 2;
lv_coord_t left = lv_obj_get_style_pad_left(obj, LV_PART_KNOB);
lv_coord_t right = lv_obj_get_style_pad_right(obj, LV_PART_KNOB);
lv_coord_t top = lv_obj_get_style_pad_top(obj, LV_PART_KNOB);
lv_coord_t bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_KNOB);
lv_area_t knob_area;
knob_area.x1 = obj->coords.x1 + colorwheel->knob.pos.x - r - left;
knob_area.y1 = obj->coords.y1 + colorwheel->knob.pos.y - r - right;
knob_area.x2 = obj->coords.x1 + colorwheel->knob.pos.x + r + top;
knob_area.y2 = obj->coords.y1 + colorwheel->knob.pos.y + r + bottom;
return knob_area;
}
static void lv_colorwheel_event(const lv_obj_class_t * class_p, lv_event_t * e)
{
LV_UNUSED(class_p);
/*Call the ancestor's event handler*/
lv_res_t res = lv_obj_event_base(MY_CLASS, e);
if(res != LV_RES_OK) return;
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * obj = lv_event_get_target(e);
lv_colorwheel_t * colorwheel = (lv_colorwheel_t *)obj;
if(code == LV_EVENT_REFR_EXT_DRAW_SIZE) {
lv_coord_t left = lv_obj_get_style_pad_left(obj, LV_PART_KNOB);
lv_coord_t right = lv_obj_get_style_pad_right(obj, LV_PART_KNOB);
lv_coord_t top = lv_obj_get_style_pad_top(obj, LV_PART_KNOB);
lv_coord_t bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_KNOB);
lv_coord_t knob_pad = LV_MAX4(left, right, top, bottom) + 2;
lv_coord_t * s = lv_event_get_param(e);
*s = LV_MAX(*s, knob_pad);
}
else if(code == LV_EVENT_SIZE_CHANGED) {
void * param = lv_event_get_param(e);
/*Refresh extended draw area to make knob visible*/
if(lv_obj_get_width(obj) != lv_area_get_width(param) ||
lv_obj_get_height(obj) != lv_area_get_height(param)) {
refr_knob_pos(obj);
}
}
else if(code == LV_EVENT_STYLE_CHANGED) {
/*Refresh extended draw area to make knob visible*/
refr_knob_pos(obj);
}
else if(code == LV_EVENT_KEY) {
uint32_t c = *((uint32_t *)lv_event_get_param(e)); /*uint32_t because can be UTF-8*/
if(c == LV_KEY_RIGHT || c == LV_KEY_UP) {
lv_color_hsv_t hsv_cur;
hsv_cur = colorwheel->hsv;
switch(colorwheel->mode) {
case LV_COLORWHEEL_MODE_HUE:
hsv_cur.h = (colorwheel->hsv.h + 1) % 360;
break;
case LV_COLORWHEEL_MODE_SATURATION:
hsv_cur.s = (colorwheel->hsv.s + 1) % 100;
break;
case LV_COLORWHEEL_MODE_VALUE:
hsv_cur.v = (colorwheel->hsv.v + 1) % 100;
break;
}
if(lv_colorwheel_set_hsv(obj, hsv_cur)) {
res = lv_event_send(obj, LV_EVENT_VALUE_CHANGED, NULL);
if(res != LV_RES_OK) return;
}
}
else if(c == LV_KEY_LEFT || c == LV_KEY_DOWN) {
lv_color_hsv_t hsv_cur;
hsv_cur = colorwheel->hsv;
switch(colorwheel->mode) {
case LV_COLORWHEEL_MODE_HUE:
hsv_cur.h = colorwheel->hsv.h > 0 ? (colorwheel->hsv.h - 1) : 360;
break;
case LV_COLORWHEEL_MODE_SATURATION:
hsv_cur.s = colorwheel->hsv.s > 0 ? (colorwheel->hsv.s - 1) : 100;
break;
case LV_COLORWHEEL_MODE_VALUE:
hsv_cur.v = colorwheel->hsv.v > 0 ? (colorwheel->hsv.v - 1) : 100;
break;
}
if(lv_colorwheel_set_hsv(obj, hsv_cur)) {
res = lv_event_send(obj, LV_EVENT_VALUE_CHANGED, NULL);
if(res != LV_RES_OK) return;
}
}
}
else if(code == LV_EVENT_PRESSED) {
colorwheel->last_change_time = lv_tick_get();
lv_indev_get_point(lv_indev_get_act(), &colorwheel->last_press_point);
res = double_click_reset(obj);
if(res != LV_RES_OK) return;
}
else if(code == LV_EVENT_PRESSING) {
lv_indev_t * indev = lv_indev_get_act();
if(indev == NULL) return;
lv_indev_type_t indev_type = lv_indev_get_type(indev);
lv_point_t p;
if(indev_type == LV_INDEV_TYPE_ENCODER || indev_type == LV_INDEV_TYPE_KEYPAD) {
p.x = obj->coords.x1 + lv_obj_get_width(obj) / 2;
p.y = obj->coords.y1 + lv_obj_get_height(obj) / 2;
}
else {
lv_indev_get_point(indev, &p);
}
lv_coord_t drag_limit = indev->driver->scroll_limit;
if((LV_ABS(p.x - colorwheel->last_press_point.x) > drag_limit) ||
(LV_ABS(p.y - colorwheel->last_press_point.y) > drag_limit)) {
colorwheel->last_change_time = lv_tick_get();
colorwheel->last_press_point.x = p.x;
colorwheel->last_press_point.y = p.y;
}
p.x -= obj->coords.x1;
p.y -= obj->coords.y1;
/*Ignore pressing in the inner area*/
uint16_t w = lv_obj_get_width(obj);
int16_t angle = 0;
lv_coord_t cir_w = lv_obj_get_style_arc_width(obj, LV_PART_MAIN);
lv_coord_t r_in = w / 2;
p.x -= r_in;
p.y -= r_in;
bool on_ring = true;
r_in -= cir_w;
if(r_in > LV_DPI_DEF / 2) {
lv_coord_t inner = cir_w / 2;
r_in -= inner;
if(r_in < LV_DPI_DEF / 2) r_in = LV_DPI_DEF / 2;
}
if(p.x * p.x + p.y * p.y < r_in * r_in) {
on_ring = false;
}
/*If the inner area is being pressed, go to the next color mode on long press*/
uint32_t diff = lv_tick_elaps(colorwheel->last_change_time);
if(!on_ring && diff > indev->driver->long_press_time && !colorwheel->mode_fixed) {
next_color_mode(obj);
lv_indev_wait_release(lv_indev_get_act());
return;
}
/*Set the angle only if pressed on the ring*/
if(!on_ring) return;
angle = lv_atan2(p.x, p.y) % 360;
lv_color_hsv_t hsv_cur;
hsv_cur = colorwheel->hsv;
switch(colorwheel->mode) {
case LV_COLORWHEEL_MODE_HUE:
hsv_cur.h = angle;
break;
case LV_COLORWHEEL_MODE_SATURATION:
hsv_cur.s = (angle * 100) / 360;
break;
case LV_COLORWHEEL_MODE_VALUE:
hsv_cur.v = (angle * 100) / 360;
break;
}
if(lv_colorwheel_set_hsv(obj, hsv_cur)) {
res = lv_event_send(obj, LV_EVENT_VALUE_CHANGED, NULL);
if(res != LV_RES_OK) return;
}
}
else if(code == LV_EVENT_HIT_TEST) {
lv_hit_test_info_t * info = lv_event_get_param(e);;
/*Valid clicks can be only in the circle*/
info->res = _lv_area_is_point_on(&obj->coords, info->point, LV_RADIUS_CIRCLE);
}
else if(code == LV_EVENT_DRAW_MAIN) {
draw_disc_grad(e);
draw_knob(e);
}
else if(code == LV_EVENT_COVER_CHECK) {
lv_cover_check_info_t * info = lv_event_get_param(e);
if(info->res != LV_COVER_RES_MASKED) info->res = LV_COVER_RES_NOT_COVER;
}
}
static void next_color_mode(lv_obj_t * obj)
{
lv_colorwheel_t * colorwheel = (lv_colorwheel_t *)obj;
colorwheel->mode = (colorwheel->mode + 1) % 3;
refr_knob_pos(obj);
lv_obj_invalidate(obj);
}
static void refr_knob_pos(lv_obj_t * obj)
{
invalidate_knob(obj);
lv_colorwheel_t * colorwheel = (lv_colorwheel_t *)obj;
lv_coord_t w = lv_obj_get_width(obj);
lv_coord_t scale_w = lv_obj_get_style_arc_width(obj, LV_PART_MAIN);
lv_coord_t r = (w - scale_w) / 2;
uint16_t angle = get_angle(obj);
colorwheel->knob.pos.x = (((int32_t)r * lv_trigo_sin(angle)) >> LV_TRIGO_SHIFT);
colorwheel->knob.pos.y = (((int32_t)r * lv_trigo_cos(angle)) >> LV_TRIGO_SHIFT);
colorwheel->knob.pos.x = colorwheel->knob.pos.x + w / 2;
colorwheel->knob.pos.y = colorwheel->knob.pos.y + w / 2;
invalidate_knob(obj);
}
static lv_res_t double_click_reset(lv_obj_t * obj)
{
lv_colorwheel_t * colorwheel = (lv_colorwheel_t *)obj;
lv_indev_t * indev = lv_indev_get_act();
/*Double clicked? Use long press time as double click time out*/
if(lv_tick_elaps(colorwheel->last_click_time) < indev->driver->long_press_time) {
lv_color_hsv_t hsv_cur;
hsv_cur = colorwheel->hsv;
switch(colorwheel->mode) {
case LV_COLORWHEEL_MODE_HUE:
hsv_cur.h = 0;
break;
case LV_COLORWHEEL_MODE_SATURATION:
hsv_cur.s = 100;
break;
case LV_COLORWHEEL_MODE_VALUE:
hsv_cur.v = 100;
break;
}
lv_indev_wait_release(indev);
if(lv_colorwheel_set_hsv(obj, hsv_cur)) {
lv_res_t res = lv_event_send(obj, LV_EVENT_VALUE_CHANGED, NULL);
if(res != LV_RES_OK) return res;
}
}
colorwheel->last_click_time = lv_tick_get();
return LV_RES_OK;
}
#define SWAPPTR(A, B) do { uint8_t * t = A; A = B; B = t; } while(0)
#define HSV_PTR_SWAP(sextant,r,g,b) if((sextant) & 2) { SWAPPTR((r), (b)); } if((sextant) & 4) { SWAPPTR((g), (b)); } if(!((sextant) & 6)) { \
if(!((sextant) & 1)) { SWAPPTR((r), (g)); } } else { if((sextant) & 1) { SWAPPTR((r), (g)); } }
/**
* Based on the idea from https://www.vagrearg.org/content/hsvrgb
* Here we want to compute an approximate RGB value from a HSV input color space. We don't want to be accurate
* (for that, there's lv_color_hsv_to_rgb), but we want to be fast.
*
* Few tricks are used here: Hue is in range [0; 6 * 256] (so that the sextant is in the high byte and the fractional part is in the low byte)
* both s and v are in [0; 255] range (very convenient to avoid divisions).
*
* We fold all symmetry by swapping the R, G, B pointers so that the code is the same for all sextants.
* We replace division by 255 by a division by 256, a.k.a a shift right by 8 bits.
* This is wrong, but since this is only used to compute the pixels on the screen and not the final color, it's ok.
*/
static void fast_hsv2rgb(uint16_t h, uint8_t s, uint8_t v, uint8_t * r, uint8_t * g, uint8_t * b);
static void fast_hsv2rgb(uint16_t h, uint8_t s, uint8_t v, uint8_t * r, uint8_t * g, uint8_t * b)
{
if(!s) {
*r = *g = *b = v;
return;
}
uint8_t sextant = h >> 8;
HSV_PTR_SWAP(sextant, r, g, b); /*Swap pointers so the conversion code is the same*/
*g = v;
uint8_t bb = ~s;
uint16_t ww = v * bb; /*Don't try to be precise, but instead, be fast*/
*b = ww >> 8;
uint8_t h_frac = h & 0xff;
if(!(sextant & 1)) {
/*Up slope*/
ww = !h_frac ? ((uint16_t)s << 8) : (s * (uint8_t)(-h_frac)); /*Skip multiply if not required*/
}
else {
/*Down slope*/
ww = s * h_frac;
}
bb = ww >> 8;
bb = ~bb;
ww = v * bb;
*r = ww >> 8;
}
static lv_color_t angle_to_mode_color_fast(lv_obj_t * obj, uint16_t angle)
{
lv_colorwheel_t * ext = (lv_colorwheel_t *)obj;
uint8_t r = 0, g = 0, b = 0;
static uint16_t h = 0;
static uint8_t s = 0, v = 0, m = 255;
switch(ext->mode) {
default:
case LV_COLORWHEEL_MODE_HUE:
/*Don't recompute costly scaling if it does not change*/
if(m != ext->mode) {
s = (uint8_t)(((uint16_t)ext->hsv.s * 51) / 20);
v = (uint8_t)(((uint16_t)ext->hsv.v * 51) / 20);
m = ext->mode;
}
fast_hsv2rgb(angle * 6, s, v, &r, &g,
&b); /*A smart compiler will replace x * 6 by (x << 2) + (x << 1) if it's more efficient*/
break;
case LV_COLORWHEEL_MODE_SATURATION:
/*Don't recompute costly scaling if it does not change*/
if(m != ext->mode) {
h = (uint16_t)(((uint32_t)ext->hsv.h * 6 * 256) / 360);
v = (uint8_t)(((uint16_t)ext->hsv.v * 51) / 20);
m = ext->mode;
}
fast_hsv2rgb(h, angle, v, &r, &g, &b);
break;
case LV_COLORWHEEL_MODE_VALUE:
/*Don't recompute costly scaling if it does not change*/
if(m != ext->mode) {
h = (uint16_t)(((uint32_t)ext->hsv.h * 6 * 256) / 360);
s = (uint8_t)(((uint16_t)ext->hsv.s * 51) / 20);
m = ext->mode;
}
fast_hsv2rgb(h, s, angle, &r, &g, &b);
break;
}
return lv_color_make(r, g, b);
}
static uint16_t get_angle(lv_obj_t * obj)
{
lv_colorwheel_t * colorwheel = (lv_colorwheel_t *)obj;
uint16_t angle;
switch(colorwheel->mode) {
default:
case LV_COLORWHEEL_MODE_HUE:
angle = colorwheel->hsv.h;
break;
case LV_COLORWHEEL_MODE_SATURATION:
angle = (colorwheel->hsv.s * 360) / 100;
break;
case LV_COLORWHEEL_MODE_VALUE:
angle = (colorwheel->hsv.v * 360) / 100 ;
break;
}
return angle;
}
#endif /*LV_USE_COLORWHEEL*/

View File

@@ -0,0 +1,142 @@
/**
* @file lv_colorwheel.h
*
*/
#ifndef LV_COLORWHEEL_H
#define LV_COLORWHEEL_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../lvgl.h"
#if LV_USE_COLORWHEEL
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
enum {
LV_COLORWHEEL_MODE_HUE,
LV_COLORWHEEL_MODE_SATURATION,
LV_COLORWHEEL_MODE_VALUE
};
typedef uint8_t lv_colorwheel_mode_t;
/*Data of color picker*/
typedef struct {
lv_obj_t obj;
lv_color_hsv_t hsv;
struct {
lv_point_t pos;
uint8_t recolor : 1;
} knob;
uint32_t last_click_time;
uint32_t last_change_time;
lv_point_t last_press_point;
lv_colorwheel_mode_t mode : 2;
uint8_t mode_fixed : 1;
} lv_colorwheel_t;
extern const lv_obj_class_t lv_colorwheel_class;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a color picker objects with disc shape
* @param parent pointer to an object, it will be the parent of the new color picker
* @param knob_recolor true: set the knob's color to the current color
* @return pointer to the created color picker
*/
lv_obj_t * lv_colorwheel_create(lv_obj_t * parent, bool knob_recolor);
/*=====================
* Setter functions
*====================*/
/**
* Set the current hsv of a color wheel.
* @param colorwheel pointer to color wheel object
* @param color current selected hsv
* @return true if changed, otherwise false
*/
bool lv_colorwheel_set_hsv(lv_obj_t * obj, lv_color_hsv_t hsv);
/**
* Set the current color of a color wheel.
* @param colorwheel pointer to color wheel object
* @param color current selected color
* @return true if changed, otherwise false
*/
bool lv_colorwheel_set_rgb(lv_obj_t * obj, lv_color_t color);
/**
* Set the current color mode.
* @param colorwheel pointer to color wheel object
* @param mode color mode (hue/sat/val)
*/
void lv_colorwheel_set_mode(lv_obj_t * obj, lv_colorwheel_mode_t mode);
/**
* Set if the color mode is changed on long press on center
* @param colorwheel pointer to color wheel object
* @param fixed color mode cannot be changed on long press
*/
void lv_colorwheel_set_mode_fixed(lv_obj_t * obj, bool fixed);
/*=====================
* Getter functions
*====================*/
/**
* Get the current selected hsv of a color wheel.
* @param colorwheel pointer to color wheel object
* @return current selected hsv
*/
lv_color_hsv_t lv_colorwheel_get_hsv(lv_obj_t * obj);
/**
* Get the current selected color of a color wheel.
* @param colorwheel pointer to color wheel object
* @return color current selected color
*/
lv_color_t lv_colorwheel_get_rgb(lv_obj_t * obj);
/**
* Get the current color mode.
* @param colorwheel pointer to color wheel object
* @return color mode (hue/sat/val)
*/
lv_colorwheel_mode_t lv_colorwheel_get_color_mode(lv_obj_t * obj);
/**
* Get if the color mode is changed on long press on center
* @param colorwheel pointer to color wheel object
* @return mode cannot be changed on long press
*/
bool lv_colorwheel_get_color_mode_fixed(lv_obj_t * obj);
/**********************
* MACROS
**********************/
#endif /*LV_USE_COLORWHEEL*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_COLORWHEEL_H*/

View File

@@ -0,0 +1,371 @@
/**
* @file lv_imgbtn.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_imgbtn.h"
#if LV_USE_IMGBTN != 0
/*********************
* DEFINES
*********************/
#define MY_CLASS &lv_imgbtn_class
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_imgbtn_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void draw_main(lv_event_t * e);
static void lv_imgbtn_event(const lv_obj_class_t * class_p, lv_event_t * e);
static void refr_img(lv_obj_t * imgbtn);
static lv_imgbtn_state_t suggest_state(lv_obj_t * imgbtn, lv_imgbtn_state_t state);
lv_imgbtn_state_t get_state(const lv_obj_t * imgbtn);
/**********************
* STATIC VARIABLES
**********************/
const lv_obj_class_t lv_imgbtn_class = {
.base_class = &lv_obj_class,
.instance_size = sizeof(lv_imgbtn_t),
.constructor_cb = lv_imgbtn_constructor,
.event_cb = lv_imgbtn_event,
};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Create a image button object
* @param par pointer to an object, it will be the parent of the new image button
* @return pointer to the created image button
*/
lv_obj_t * lv_imgbtn_create(lv_obj_t * parent)
{
LV_LOG_INFO("begin");
lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
lv_obj_class_init_obj(obj);
return obj;
}
/*=====================
* Setter functions
*====================*/
/**
* Set 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
* @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 * obj, lv_imgbtn_state_t state, const void * src_left, const void * src_mid,
const void * src_right)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_imgbtn_t * imgbtn = (lv_imgbtn_t *)obj;
imgbtn->img_src_left[state] = src_left;
imgbtn->img_src_mid[state] = src_mid;
imgbtn->img_src_right[state] = src_right;
refr_img(obj);
}
void lv_imgbtn_set_state(lv_obj_t * obj, lv_imgbtn_state_t state)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_state_t obj_state = LV_STATE_DEFAULT;
if(state == LV_IMGBTN_STATE_PRESSED || state == LV_IMGBTN_STATE_CHECKED_PRESSED) obj_state |= LV_STATE_PRESSED;
if(state == LV_IMGBTN_STATE_DISABLED || state == LV_IMGBTN_STATE_CHECKED_DISABLED) obj_state |= LV_STATE_DISABLED;
if(state == LV_IMGBTN_STATE_CHECKED_DISABLED || state == LV_IMGBTN_STATE_CHECKED_PRESSED || state == LV_IMGBTN_STATE_CHECKED_RELEASED) {
obj_state |= LV_STATE_CHECKED;
}
lv_obj_clear_state(obj, LV_STATE_CHECKED | LV_STATE_PRESSED | LV_STATE_DISABLED);
lv_obj_add_state(obj, obj_state);
refr_img(obj);
}
/*=====================
* Getter functions
*====================*/
/**
* 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 * obj, lv_imgbtn_state_t state)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_imgbtn_t * imgbtn = (lv_imgbtn_t *)obj;
return imgbtn->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 * obj, lv_imgbtn_state_t state)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_imgbtn_t * imgbtn = (lv_imgbtn_t *)obj;
return imgbtn->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 * obj, lv_imgbtn_state_t state)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_imgbtn_t * imgbtn = (lv_imgbtn_t *)obj;
return imgbtn->img_src_right[state];
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_imgbtn_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
lv_imgbtn_t * imgbtn = (lv_imgbtn_t *)obj;
/*Initialize the allocated 'ext'*/
lv_memset_00((void *)imgbtn->img_src_mid, sizeof(imgbtn->img_src_mid));
lv_memset_00(imgbtn->img_src_left, sizeof(imgbtn->img_src_left));
lv_memset_00(imgbtn->img_src_right, sizeof(imgbtn->img_src_right));
imgbtn->act_cf = LV_IMG_CF_UNKNOWN;
}
static void lv_imgbtn_event(const lv_obj_class_t * class_p, lv_event_t * e)
{
LV_UNUSED(class_p);
lv_res_t res = lv_obj_event_base(&lv_imgbtn_class, e);
if(res != LV_RES_OK) return;
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * obj = lv_event_get_target(e);
if(code == LV_EVENT_PRESSED || code == LV_EVENT_RELEASED || code == LV_EVENT_PRESS_LOST) {
refr_img(obj);
}
else if(code == LV_EVENT_DRAW_MAIN) {
draw_main(e);
}
else if(code == LV_EVENT_COVER_CHECK) {
lv_cover_check_info_t * info = lv_event_get_param(e);
if(info->res != LV_COVER_RES_MASKED) info->res = LV_COVER_RES_NOT_COVER;
}
else if(code == LV_EVENT_GET_SELF_SIZE) {
lv_point_t * p = lv_event_get_self_size_info(e);
lv_imgbtn_t * imgbtn = (lv_imgbtn_t *)obj;
lv_imgbtn_state_t state = suggest_state(obj, get_state(obj));
if(imgbtn->img_src_left[state] == NULL &&
imgbtn->img_src_mid[state] != NULL &&
imgbtn->img_src_right[state] == NULL) {
lv_img_header_t header;
lv_img_decoder_get_info(imgbtn->img_src_mid[state], &header);
p->x = LV_MAX(p->x, header.w);
}
}
}
static void draw_main(lv_event_t * e)
{
lv_obj_t * obj = lv_event_get_target(e);
lv_imgbtn_t * imgbtn = (lv_imgbtn_t *)obj;
const lv_area_t * clip_area = lv_event_get_param(e);
/*Just draw_main an image*/
lv_imgbtn_state_t state = suggest_state(obj, get_state(obj));
/*Simply draw the middle src if no tiled*/
const void * src = imgbtn->img_src_left[state];
lv_coord_t tw = lv_obj_get_style_transform_width(obj, LV_PART_MAIN);
lv_coord_t th = lv_obj_get_style_transform_height(obj, LV_PART_MAIN);
lv_area_t coords;
lv_area_copy(&coords, &obj->coords);
coords.x1 -= tw;
coords.x2 += tw;
coords.y1 -= th;
coords.y2 += th;
lv_draw_img_dsc_t img_dsc;
lv_draw_img_dsc_init(&img_dsc);
lv_obj_init_draw_img_dsc(obj, LV_PART_MAIN, &img_dsc);
lv_img_header_t header;
lv_area_t coords_part;
lv_coord_t left_w = 0;
lv_coord_t right_w = 0;
if(src) {
lv_img_decoder_get_info(src, &header);
left_w = header.w;
coords_part.x1 = coords.x1;
coords_part.y1 = coords.y1;
coords_part.x2 = coords.x1 + header.w - 1;
coords_part.y2 = coords.y1 + header.h - 1;
lv_draw_img(&coords_part, clip_area, src, &img_dsc);
}
src = imgbtn->img_src_right[state];
if(src) {
lv_img_decoder_get_info(src, &header);
right_w = header.w;
coords_part.x1 = coords.x2 - header.w + 1;
coords_part.y1 = coords.y1;
coords_part.x2 = coords.x2;
coords_part.y2 = coords.y1 + header.h - 1;
lv_draw_img(&coords_part, clip_area, src, &img_dsc);
}
src = imgbtn->img_src_mid[state];
if(src) {
lv_area_t clip_center_area;
clip_center_area.x1 = coords.x1 + left_w;
clip_center_area.x2 = coords.x2 - right_w;
clip_center_area.y1 = coords.y1;
clip_center_area.y2 = coords.y2;
bool comm_res;
comm_res = _lv_area_intersect(&clip_center_area, &clip_center_area, clip_area);
if(comm_res) {
lv_coord_t i;
lv_img_decoder_get_info(src, &header);
coords_part.x1 = coords.x1 + left_w;
coords_part.y1 = coords.y1;
coords_part.x2 = coords_part.x1 + header.w - 1;
coords_part.y2 = coords_part.y1 + header.h - 1;
for(i = coords_part.x1; i < (lv_coord_t)(clip_center_area.x2 + header.w - 1); i += header.w) {
lv_draw_img(&coords_part, &clip_center_area, src, &img_dsc);
coords_part.x1 = coords_part.x2 + 1;
coords_part.x2 += header.w;
}
}
}
}
static void refr_img(lv_obj_t * obj)
{
lv_imgbtn_t * imgbtn = (lv_imgbtn_t *)obj;
lv_imgbtn_state_t state = suggest_state(obj, get_state(obj));
lv_img_header_t header;
const void * src = imgbtn->img_src_mid[state];
if(src == NULL) return;
lv_res_t info_res = LV_RES_OK;
info_res = lv_img_decoder_get_info(src, &header);
if(info_res == LV_RES_OK) {
imgbtn->act_cf = header.cf;
lv_obj_refresh_self_size(obj);
lv_obj_set_height(obj, header.h); /*Keep the user defined width*/
}
else {
imgbtn->act_cf = LV_IMG_CF_UNKNOWN;
}
lv_obj_invalidate(obj);
}
/**
* If `src` is not defined for the current state try to get a state which is related to the current but has `src`.
* E.g. if the PRESSED src is not set but the RELEASED does, use the RELEASED.
* @param imgbtn pointer to an image button
* @param state the state to convert
* @return the suggested state
*/
static lv_imgbtn_state_t suggest_state(lv_obj_t * obj, lv_imgbtn_state_t state)
{
lv_imgbtn_t * imgbtn = (lv_imgbtn_t *)obj;
if(imgbtn->img_src_mid[state] == NULL) {
switch(state) {
case LV_IMGBTN_STATE_PRESSED:
if(imgbtn->img_src_mid[LV_IMGBTN_STATE_RELEASED]) return LV_IMGBTN_STATE_RELEASED;
break;
case LV_IMGBTN_STATE_CHECKED_RELEASED:
if(imgbtn->img_src_mid[LV_IMGBTN_STATE_RELEASED]) return LV_IMGBTN_STATE_RELEASED;
break;
case LV_IMGBTN_STATE_CHECKED_PRESSED:
if(imgbtn->img_src_mid[LV_IMGBTN_STATE_CHECKED_RELEASED]) return LV_IMGBTN_STATE_CHECKED_RELEASED;
if(imgbtn->img_src_mid[LV_IMGBTN_STATE_PRESSED]) return LV_IMGBTN_STATE_PRESSED;
if(imgbtn->img_src_mid[LV_IMGBTN_STATE_RELEASED]) return LV_IMGBTN_STATE_RELEASED;
break;
case LV_IMGBTN_STATE_DISABLED:
if(imgbtn->img_src_mid[LV_IMGBTN_STATE_RELEASED]) return LV_IMGBTN_STATE_RELEASED;
break;
case LV_IMGBTN_STATE_CHECKED_DISABLED:
if(imgbtn->img_src_mid[LV_IMGBTN_STATE_CHECKED_RELEASED]) return LV_IMGBTN_STATE_CHECKED_RELEASED;
if(imgbtn->img_src_mid[LV_IMGBTN_STATE_RELEASED]) return LV_IMGBTN_STATE_RELEASED;
break;
default:
break;
}
}
return state;
}
lv_imgbtn_state_t get_state(const lv_obj_t * imgbtn)
{
LV_ASSERT_OBJ(imgbtn, MY_CLASS);
lv_state_t obj_state = lv_obj_get_state(imgbtn);
if(obj_state & LV_STATE_DISABLED) {
if(obj_state & LV_STATE_CHECKED) return LV_IMGBTN_STATE_CHECKED_DISABLED;
else return LV_IMGBTN_STATE_DISABLED;
}
if(obj_state & LV_STATE_CHECKED) {
if(obj_state & LV_STATE_PRESSED) return LV_IMGBTN_STATE_CHECKED_PRESSED;
else return LV_IMGBTN_STATE_CHECKED_RELEASED;
}
else {
if(obj_state & LV_STATE_PRESSED) return LV_IMGBTN_STATE_PRESSED;
else return LV_IMGBTN_STATE_RELEASED;
}
}
#endif

View File

@@ -0,0 +1,131 @@
/**
* @file lv_imgbtn.h
*
*/
#ifndef LV_IMGBTN_H
#define LV_IMGBTN_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../lvgl.h"
#if LV_USE_IMGBTN != 0
/*********************
* DEFINES
*********************/
typedef enum {
LV_IMGBTN_STATE_RELEASED,
LV_IMGBTN_STATE_PRESSED,
LV_IMGBTN_STATE_DISABLED,
LV_IMGBTN_STATE_CHECKED_RELEASED,
LV_IMGBTN_STATE_CHECKED_PRESSED,
LV_IMGBTN_STATE_CHECKED_DISABLED,
_LV_IMGBTN_STATE_NUM,
} lv_imgbtn_state_t;
/**********************
* TYPEDEFS
**********************/
/*Data of image button*/
typedef struct {
lv_obj_t obj;
const void * img_src_mid[_LV_IMGBTN_STATE_NUM]; /*Store center images to each state*/
const void * img_src_left[_LV_IMGBTN_STATE_NUM]; /*Store left side images to each state*/
const void * img_src_right[_LV_IMGBTN_STATE_NUM]; /*Store right side images to each state*/
lv_img_cf_t act_cf; /*Color format of the currently active image*/
} lv_imgbtn_t;
extern const lv_obj_class_t lv_imgbtn_class;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a image button objects
* @param par pointer to an object, it will be the parent of the new image button
* @return pointer to the created image button
*/
lv_obj_t * lv_imgbtn_create(lv_obj_t * parent);
/*======================
* Add/remove functions
*=====================*/
/*=====================
* Setter functions
*====================*/
/**
* 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
* @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_imgbtn_state_t state, const void * src_left, const void * src_mid,
const void * src_right);
/**
* Use this function instead of `lv_obj_add/clear_state` to set a state manually
* @param imgbtn pointer to an image button object
* @param state the new state
*/
void lv_imgbtn_set_state(lv_obj_t * imgbtn, lv_imgbtn_state_t state);
/*=====================
* Getter functions
*====================*/
/**
* 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_imgbtn_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_imgbtn_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_imgbtn_state_t state);
/*=====================
* Other functions
*====================*/
/**********************
* MACROS
**********************/
#endif /*LV_USE_IMGBTN*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_IMGBTN_H*/

View File

@@ -0,0 +1,429 @@
/**
* @file lv_keyboard.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_keyboard.h"
#if LV_USE_KEYBOARD
#include "../../../widgets/lv_textarea.h"
#include "../../../misc/lv_assert.h"
#include <stdlib.h>
/*********************
* DEFINES
*********************/
#define MY_CLASS &lv_keyboard_class
#define LV_KB_BTN(width) LV_BTNMATRIX_CTRL_POPOVER | width
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_keyboard_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_keyboard_update_map(lv_obj_t * obj);
static void lv_keyboard_update_ctrl_map(lv_obj_t * obj);
/**********************
* STATIC VARIABLES
**********************/
const lv_obj_class_t lv_keyboard_class = {
.constructor_cb = lv_keyboard_constructor,
.width_def = LV_PCT(100),
.height_def = LV_PCT(50),
.instance_size = sizeof(lv_keyboard_t),
.editable = 1,
.base_class = &lv_btnmatrix_class
};
static const char * const default_kb_map_lc[] = {"1#", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", LV_SYMBOL_BACKSPACE, "\n",
"ABC", "a", "s", "d", "f", "g", "h", "j", "k", "l", LV_SYMBOL_NEW_LINE, "\n",
"_", "-", "z", "x", "c", "v", "b", "n", "m", ".", ",", ":", "\n",
LV_SYMBOL_KEYBOARD, LV_SYMBOL_LEFT, " ", LV_SYMBOL_RIGHT, LV_SYMBOL_OK, ""
};
static const lv_btnmatrix_ctrl_t default_kb_ctrl_lc_map[] = {
LV_KEYBOARD_CTRL_BTN_FLAGS | 5, LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), LV_BTNMATRIX_CTRL_CHECKED | 7,
LV_KEYBOARD_CTRL_BTN_FLAGS | 6, LV_KB_BTN(3), LV_KB_BTN(3), LV_KB_BTN(3), LV_KB_BTN(3), LV_KB_BTN(3), LV_KB_BTN(3), LV_KB_BTN(3), LV_KB_BTN(3), LV_KB_BTN(3), LV_BTNMATRIX_CTRL_CHECKED | 7,
LV_BTNMATRIX_CTRL_CHECKED | LV_KB_BTN(1), LV_BTNMATRIX_CTRL_CHECKED | LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_BTNMATRIX_CTRL_CHECKED | LV_KB_BTN(1), LV_BTNMATRIX_CTRL_CHECKED | LV_KB_BTN(1), LV_BTNMATRIX_CTRL_CHECKED | LV_KB_BTN(1),
LV_KEYBOARD_CTRL_BTN_FLAGS | 2, LV_BTNMATRIX_CTRL_CHECKED | 2, 6, LV_BTNMATRIX_CTRL_CHECKED | 2, LV_KEYBOARD_CTRL_BTN_FLAGS | 2
};
static const char * const default_kb_map_uc[] = {"1#", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", LV_SYMBOL_BACKSPACE, "\n",
"abc", "A", "S", "D", "F", "G", "H", "J", "K", "L", LV_SYMBOL_NEW_LINE, "\n",
"_", "-", "Z", "X", "C", "V", "B", "N", "M", ".", ",", ":", "\n",
LV_SYMBOL_KEYBOARD, LV_SYMBOL_LEFT, " ", LV_SYMBOL_RIGHT, LV_SYMBOL_OK, ""
};
static const lv_btnmatrix_ctrl_t default_kb_ctrl_uc_map[] = {
LV_KEYBOARD_CTRL_BTN_FLAGS | 5, LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), LV_KB_BTN(4), LV_BTNMATRIX_CTRL_CHECKED | 7,
LV_KEYBOARD_CTRL_BTN_FLAGS | 6, LV_KB_BTN(3), LV_KB_BTN(3), LV_KB_BTN(3), LV_KB_BTN(3), LV_KB_BTN(3), LV_KB_BTN(3), LV_KB_BTN(3), LV_KB_BTN(3), LV_KB_BTN(3), LV_BTNMATRIX_CTRL_CHECKED | 7,
LV_BTNMATRIX_CTRL_CHECKED | LV_KB_BTN(1), LV_BTNMATRIX_CTRL_CHECKED | LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_BTNMATRIX_CTRL_CHECKED | LV_KB_BTN(1), LV_BTNMATRIX_CTRL_CHECKED | LV_KB_BTN(1), LV_BTNMATRIX_CTRL_CHECKED | LV_KB_BTN(1),
LV_KEYBOARD_CTRL_BTN_FLAGS | 2, LV_BTNMATRIX_CTRL_CHECKED | 2, 6, LV_BTNMATRIX_CTRL_CHECKED | 2, LV_KEYBOARD_CTRL_BTN_FLAGS | 2
};
static const char * const default_kb_map_spec[] = {"1", "2", "3", "4", "5", "6", "7", "8", "9", "0", LV_SYMBOL_BACKSPACE, "\n",
"abc", "+", "-", "/", "*", "=", "%", "!", "?", "#", "<", ">", "\n",
"\\", "@", "$", "(", ")", "{", "}", "[", "]", ";", "\"", "'", "\n",
LV_SYMBOL_KEYBOARD, LV_SYMBOL_LEFT, " ", LV_SYMBOL_RIGHT, LV_SYMBOL_OK, ""
};
static const lv_btnmatrix_ctrl_t default_kb_ctrl_spec_map[] = {
LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_BTNMATRIX_CTRL_CHECKED | 2,
LV_KEYBOARD_CTRL_BTN_FLAGS | 2, LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1),
LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1), LV_KB_BTN(1),
LV_KEYBOARD_CTRL_BTN_FLAGS | 2, LV_BTNMATRIX_CTRL_CHECKED | 2, 6, LV_BTNMATRIX_CTRL_CHECKED | 2, LV_KEYBOARD_CTRL_BTN_FLAGS | 2
};
static const char * const default_kb_map_num[] = {"1", "2", "3", LV_SYMBOL_KEYBOARD, "\n",
"4", "5", "6", LV_SYMBOL_OK, "\n",
"7", "8", "9", LV_SYMBOL_BACKSPACE, "\n",
"+/-", "0", ".", LV_SYMBOL_LEFT, LV_SYMBOL_RIGHT, ""
};
static const lv_btnmatrix_ctrl_t default_kb_ctrl_num_map[] = {
1, 1, 1, LV_KEYBOARD_CTRL_BTN_FLAGS | 2,
1, 1, 1, LV_KEYBOARD_CTRL_BTN_FLAGS | 2,
1, 1, 1, 2,
1, 1, 1, 1, 1
};
static const char * * kb_map[9] = {
(const char * *)default_kb_map_lc,
(const char * *)default_kb_map_uc,
(const char * *)default_kb_map_spec,
(const char * *)default_kb_map_num,
(const char * *)default_kb_map_lc,
(const char * *)default_kb_map_lc,
(const char * *)default_kb_map_lc,
(const char * *)default_kb_map_lc,
(const char * *)NULL,
};
static const lv_btnmatrix_ctrl_t * kb_ctrl[9] = {
default_kb_ctrl_lc_map,
default_kb_ctrl_uc_map,
default_kb_ctrl_spec_map,
default_kb_ctrl_num_map,
default_kb_ctrl_lc_map,
default_kb_ctrl_lc_map,
default_kb_ctrl_lc_map,
default_kb_ctrl_lc_map,
NULL,
};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Create a keyboard objects
* @param par pointer to an object, it will be the parent of the new keyboard
* @return pointer to the created keyboard
*/
lv_obj_t * lv_keyboard_create(lv_obj_t * parent)
{
LV_LOG_INFO("begin");
lv_obj_t * obj = lv_obj_class_create_obj(&lv_keyboard_class, parent);
lv_obj_class_init_obj(obj);
return obj;
}
/*=====================
* 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_keyboard_set_textarea(lv_obj_t * obj, lv_obj_t * ta)
{
if(ta) {
LV_ASSERT_OBJ(ta, &lv_textarea_class);
}
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_keyboard_t * keyboard = (lv_keyboard_t *)obj;
/*Hide the cursor of the old Text area if cursor management is enabled*/
if(keyboard->ta) {
lv_obj_clear_state(obj, LV_STATE_FOCUSED);
}
keyboard->ta = ta;
/*Show the cursor of the new Text area if cursor management is enabled*/
if(keyboard->ta) {
lv_obj_add_flag(obj, LV_STATE_FOCUSED);
}
}
/**
* Set a new a mode (text or number map)
* @param kb pointer to a Keyboard object
* @param mode the mode from 'lv_keyboard_mode_t'
*/
void lv_keyboard_set_mode(lv_obj_t * obj, lv_keyboard_mode_t mode)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_keyboard_t * keyboard = (lv_keyboard_t *)obj;
if(keyboard->mode == mode) return;
keyboard->mode = mode;
lv_keyboard_update_map(obj);
}
/**
* Show the button title in a popover when pressed.
* @param kb pointer to a Keyboard object
* @param en whether "popovers" mode is enabled
*/
void lv_keyboard_set_popovers(lv_obj_t * obj, bool en)
{
lv_keyboard_t * keyboard = (lv_keyboard_t *)obj;
if (keyboard->popovers == en) {
return;
}
keyboard->popovers = en;
lv_keyboard_update_ctrl_map(obj);
}
/**
* Set a new map for the keyboard
* @param kb pointer to a Keyboard object
* @param mode keyboard map to alter 'lv_keyboard_mode_t'
* @param map pointer to a string array to describe the map.
* See 'lv_btnmatrix_set_map()' for more info.
*/
void lv_keyboard_set_map(lv_obj_t * obj, lv_keyboard_mode_t mode, const char * map[],
const lv_btnmatrix_ctrl_t ctrl_map[])
{
kb_map[mode] = map;
kb_ctrl[mode] = ctrl_map;
lv_keyboard_update_map(obj);
}
/*=====================
* 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_keyboard_get_textarea(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_keyboard_t * keyboard = (lv_keyboard_t *)obj;
return keyboard->ta;
}
/**
* Set a new a mode (text or number map)
* @param kb pointer to a Keyboard object
* @return the current mode from 'lv_keyboard_mode_t'
*/
lv_keyboard_mode_t lv_keyboard_get_mode(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_keyboard_t * keyboard = (lv_keyboard_t *)obj;
return keyboard->mode;
}
/**
* Tell whether "popovers" mode is enabled or not.
* @param kb pointer to a Keyboard object
* @return true: "popovers" mode is enabled; false: disabled
*/
bool lv_btnmatrix_get_popovers(const lv_obj_t * obj)
{
lv_keyboard_t * keyboard = (lv_keyboard_t *)obj;
return keyboard->popovers;
}
/*=====================
* Other functions
*====================*/
/**
* Default keyboard event to add characters to the Text area and change the map.
* If a custom `event_cb` is added to the keyboard this function can be called from it to handle the
* button clicks
* @param kb pointer to a keyboard
* @param event the triggering event
*/
void lv_keyboard_def_event_cb(lv_event_t * e)
{
lv_obj_t * obj = lv_event_get_target(e);
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_keyboard_t * keyboard = (lv_keyboard_t *)obj;
uint16_t btn_id = lv_btnmatrix_get_selected_btn(obj);
if(btn_id == LV_BTNMATRIX_BTN_NONE) return;
const char * txt = lv_btnmatrix_get_btn_text(obj, lv_btnmatrix_get_selected_btn(obj));
if(txt == NULL) return;
if(strcmp(txt, "abc") == 0) {
keyboard->mode = LV_KEYBOARD_MODE_TEXT_LOWER;
lv_btnmatrix_set_map(obj, kb_map[LV_KEYBOARD_MODE_TEXT_LOWER]);
lv_btnmatrix_set_ctrl_map(obj, kb_ctrl[LV_KEYBOARD_MODE_TEXT_LOWER]);
return;
}
else if(strcmp(txt, "ABC") == 0) {
keyboard->mode = LV_KEYBOARD_MODE_TEXT_UPPER;
lv_btnmatrix_set_map(obj, kb_map[LV_KEYBOARD_MODE_TEXT_UPPER]);
lv_btnmatrix_set_ctrl_map(obj, kb_ctrl[LV_KEYBOARD_MODE_TEXT_UPPER]);
return;
}
else if(strcmp(txt, "1#") == 0) {
keyboard->mode = LV_KEYBOARD_MODE_SPECIAL;
lv_btnmatrix_set_map(obj, kb_map[LV_KEYBOARD_MODE_SPECIAL]);
lv_btnmatrix_set_ctrl_map(obj, kb_ctrl[LV_KEYBOARD_MODE_SPECIAL]);
return;
}
else if(strcmp(txt, LV_SYMBOL_CLOSE) == 0 || strcmp(txt, LV_SYMBOL_KEYBOARD) == 0) {
lv_res_t res = lv_event_send(obj, LV_EVENT_CANCEL, NULL);
if(res != LV_RES_OK) return;
if(keyboard->ta) {
res = lv_event_send(keyboard->ta, LV_EVENT_CANCEL, NULL);
if(res != LV_RES_OK) return;
}
return;
}
else if(strcmp(txt, LV_SYMBOL_OK) == 0) {
lv_res_t res = lv_event_send(obj, LV_EVENT_READY, NULL);
if(res != LV_RES_OK) return;
if(keyboard->ta) {
res = lv_event_send(keyboard->ta, LV_EVENT_READY, NULL);
if(res != LV_RES_OK) return;
}
return;
}
/*Add the characters to the text area if set*/
if(keyboard->ta == NULL) return;
if(strcmp(txt, "Enter") == 0 || strcmp(txt, LV_SYMBOL_NEW_LINE) == 0) {
lv_textarea_add_char(keyboard->ta, '\n');
if(lv_textarea_get_one_line(keyboard->ta)) {
lv_res_t res = lv_event_send(keyboard->ta, LV_EVENT_READY, NULL);
if(res != LV_RES_OK) return;
}
}
else if(strcmp(txt, LV_SYMBOL_LEFT) == 0) {
lv_textarea_cursor_left(keyboard->ta);
}
else if(strcmp(txt, LV_SYMBOL_RIGHT) == 0) {
lv_textarea_cursor_right(keyboard->ta);
}
else if(strcmp(txt, LV_SYMBOL_BACKSPACE) == 0) {
lv_textarea_del_char(keyboard->ta);
}
else if(strcmp(txt, "+/-") == 0) {
uint16_t cur = lv_textarea_get_cursor_pos(keyboard->ta);
const char * ta_txt = lv_textarea_get_text(keyboard->ta);
if(ta_txt[0] == '-') {
lv_textarea_set_cursor_pos(keyboard->ta, 1);
lv_textarea_del_char(keyboard->ta);
lv_textarea_add_char(keyboard->ta, '+');
lv_textarea_set_cursor_pos(keyboard->ta, cur);
}
else if(ta_txt[0] == '+') {
lv_textarea_set_cursor_pos(keyboard->ta, 1);
lv_textarea_del_char(keyboard->ta);
lv_textarea_add_char(keyboard->ta, '-');
lv_textarea_set_cursor_pos(keyboard->ta, cur);
}
else {
lv_textarea_set_cursor_pos(keyboard->ta, 0);
lv_textarea_add_char(keyboard->ta, '-');
lv_textarea_set_cursor_pos(keyboard->ta, cur + 1);
}
}
else {
lv_textarea_add_text(keyboard->ta, txt);
}
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_keyboard_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
lv_obj_clear_flag(obj, LV_OBJ_FLAG_CLICK_FOCUSABLE);
lv_keyboard_t * keyboard = (lv_keyboard_t *)obj;
keyboard->ta = NULL;
keyboard->mode = LV_KEYBOARD_MODE_TEXT_LOWER;
keyboard->popovers = 0;
lv_obj_align(obj, LV_ALIGN_BOTTOM_MID, 0, 0);
lv_obj_add_event_cb(obj, lv_keyboard_def_event_cb, LV_EVENT_VALUE_CHANGED, NULL);
lv_obj_set_style_base_dir(obj, LV_BASE_DIR_LTR, 0);
lv_keyboard_update_map(obj);
}
/**
* Update the key and control map for the current mode
* @param obj pointer to a keyboard object
*/
static void lv_keyboard_update_map(lv_obj_t * obj)
{
lv_keyboard_t * keyboard = (lv_keyboard_t *)obj;
lv_btnmatrix_set_map(obj, kb_map[keyboard->mode]);
lv_keyboard_update_ctrl_map(obj);
}
/**
* Update the control map for the current mode
* @param obj pointer to a keyboard object
*/
static void lv_keyboard_update_ctrl_map(lv_obj_t * obj)
{
lv_keyboard_t * keyboard = (lv_keyboard_t *)obj;
if (keyboard->popovers) {
/*Apply the current control map (already includes LV_BTNMATRIX_CTRL_POPOVER flags)*/
lv_btnmatrix_set_ctrl_map(obj, kb_ctrl[keyboard->mode]);
} else {
/*Make a copy of the current control map*/
lv_btnmatrix_t * btnm = (lv_btnmatrix_t *)obj;
lv_btnmatrix_ctrl_t * ctrl_map = lv_mem_alloc(btnm->btn_cnt * sizeof(lv_btnmatrix_ctrl_t));
lv_memcpy(ctrl_map, kb_ctrl[keyboard->mode], sizeof(lv_btnmatrix_ctrl_t) * btnm->btn_cnt);
/*Remove all LV_BTNMATRIX_CTRL_POPOVER flags*/
for(uint16_t i = 0; i < btnm->btn_cnt; i++) {
ctrl_map[i] &= (~LV_BTNMATRIX_CTRL_POPOVER);
}
/*Apply new control map and clean up*/
lv_btnmatrix_set_ctrl_map(obj, ctrl_map);
lv_mem_free(ctrl_map);
}
}
#endif /*LV_USE_KEYBOARD*/

View File

@@ -0,0 +1,165 @@
/**
* @file lv_keyboard.h
*
*/
#ifndef LV_KEYBOARD_H
#define LV_KEYBOARD_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../widgets/lv_btnmatrix.h"
#if LV_USE_KEYBOARD
/*Testing of dependencies*/
#if LV_USE_BTNMATRIX == 0
#error "lv_kb: lv_btnm is required. Enable it in lv_conf.h (LV_USE_BTNMATRIX 1) "
#endif
#if LV_USE_TEXTAREA == 0
#error "lv_kb: lv_ta is required. Enable it in lv_conf.h (LV_USE_TEXTAREA 1) "
#endif
/*********************
* DEFINES
*********************/
#define LV_KEYBOARD_CTRL_BTN_FLAGS (LV_BTNMATRIX_CTRL_NO_REPEAT | LV_BTNMATRIX_CTRL_CLICK_TRIG | LV_BTNMATRIX_CTRL_CHECKED)
/**********************
* TYPEDEFS
**********************/
/** Current keyboard mode.*/
enum {
LV_KEYBOARD_MODE_TEXT_LOWER,
LV_KEYBOARD_MODE_TEXT_UPPER,
LV_KEYBOARD_MODE_SPECIAL,
LV_KEYBOARD_MODE_NUMBER,
LV_KEYBOARD_MODE_USER_1,
LV_KEYBOARD_MODE_USER_2,
LV_KEYBOARD_MODE_USER_3,
LV_KEYBOARD_MODE_USER_4,
};
typedef uint8_t lv_keyboard_mode_t;
/*Data of keyboard*/
typedef struct {
lv_btnmatrix_t btnm;
lv_obj_t * ta; /*Pointer to the assigned text area*/
lv_keyboard_mode_t mode; /*Key map type*/
uint8_t popovers : 1; /*Show button titles in popovers on press*/
} lv_keyboard_t;
extern const lv_obj_class_t lv_keyboard_class;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a keyboard objects
* @param par pointer to an object, it will be the parent of the new keyboard
* @return pointer to the created keyboard
*/
lv_obj_t * lv_keyboard_create(lv_obj_t * parent);
/*=====================
* 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_keyboard_set_textarea(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_keyboard_mode_t'
*/
void lv_keyboard_set_mode(lv_obj_t * kb, lv_keyboard_mode_t mode);
/**
* Show the button title in a popover when pressed.
* @param kb pointer to a Keyboard object
* @param en whether "popovers" mode is enabled
*/
void lv_keyboard_set_popovers(lv_obj_t * kb, bool en);
/**
* Set a new map for the keyboard
* @param kb pointer to a Keyboard object
* @param mode keyboard map to alter 'lv_keyboard_mode_t'
* @param map pointer to a string array to describe the map.
* See 'lv_btnmatrix_set_map()' for more info.
*/
void lv_keyboard_set_map(lv_obj_t * kb, lv_keyboard_mode_t mode, const char * map[],
const lv_btnmatrix_ctrl_t ctrl_map[]);
/*=====================
* 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_keyboard_get_textarea(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_keyboard_mode_t'
*/
lv_keyboard_mode_t lv_keyboard_get_mode(const lv_obj_t * kb);
/**
* Tell whether "popovers" mode is enabled or not.
* @param kb pointer to a Keyboard object
* @return true: "popovers" mode is enabled; false: disabled
*/
bool lv_btnmatrix_get_popovers(const lv_obj_t * obj);
/**
* Get the current map of a keyboard
* @param kb pointer to a keyboard object
* @return the current map
*/
static inline const char ** lv_keyboard_get_map_array(const lv_obj_t * kb)
{
return lv_btnmatrix_get_map(kb);
}
/*=====================
* Other functions
*====================*/
/**
* Default keyboard event to add characters to the Text area and change the map.
* If a custom `event_cb` is added to the keyboard this function can be called from it to handle the
* button clicks
* @param kb pointer to a keyboard
* @param event the triggering event
*/
void lv_keyboard_def_event_cb(lv_event_t * e);
/**********************
* MACROS
**********************/
#endif /*LV_USE_KEYBOARD*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_KEYBOARD_H*/

View File

@@ -0,0 +1,217 @@
/**
* @file lv_led.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_led.h"
#if LV_USE_LED
#include "../../../misc/lv_assert.h"
/*********************
* DEFINES
*********************/
#define MY_CLASS &lv_led_class
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_led_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_led_event(const lv_obj_class_t * class_p, lv_event_t * e);
/**********************
* STATIC VARIABLES
**********************/
const lv_obj_class_t lv_led_class = {
.base_class = &lv_obj_class,
.constructor_cb = lv_led_constructor,
.width_def = LV_DPI_DEF / 5,
.height_def = LV_DPI_DEF / 5,
.event_cb = lv_led_event,
.instance_size = sizeof(lv_led_t),
};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Create a led objects
* @param par pointer to an object, it will be the parent of the new led
* @return pointer to the created led
*/
lv_obj_t * lv_led_create(lv_obj_t * parent)
{
LV_LOG_INFO("begin");
lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
lv_obj_class_init_obj(obj);
return obj;
}
/*=====================
* Setter functions
*====================*/
/**
* Set the color of the LED
* @param led pointer to a LED object
* @param color the color of the the LED
*/
void lv_led_set_color(lv_obj_t * obj, lv_color_t color)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_led_t * led = (lv_led_t *)obj;
led->color = color;
lv_obj_invalidate(obj);
}
/**
* Set the brightness of a LED object
* @param led pointer to a LED object
* @param bright LV_LED_BRIGHT_MIN (max. dark) ... LV_LED_BRIGHT_MAX (max. light)
*/
void lv_led_set_brightness(lv_obj_t * obj, uint8_t bright)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_led_t * led = (lv_led_t *)obj;
if(led->bright == bright) return;
led->bright = LV_CLAMP(LV_LED_BRIGHT_MIN, bright, LV_LED_BRIGHT_MAX);
/*Invalidate the object there fore it will be redrawn*/
lv_obj_invalidate(obj);
}
/**
* Light on a LED
* @param led pointer to a LED object
*/
void lv_led_on(lv_obj_t * led)
{
lv_led_set_brightness(led, LV_LED_BRIGHT_MAX);
}
/**
* Light off a LED
* @param led pointer to a LED object
*/
void lv_led_off(lv_obj_t * led)
{
lv_led_set_brightness(led, LV_LED_BRIGHT_MIN);
}
/**
* Toggle the state of a LED
* @param led pointer to a LED object
*/
void lv_led_toggle(lv_obj_t * obj)
{
uint8_t bright = lv_led_get_brightness(obj);
if(bright > (LV_LED_BRIGHT_MIN + LV_LED_BRIGHT_MAX) >> 1)
lv_led_off(obj);
else
lv_led_on(obj);
}
/*=====================
* 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_brightness(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_led_t * led = (lv_led_t *)obj;
return led->bright;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_led_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
lv_led_t * led = (lv_led_t *)obj;
led->color = lv_theme_get_color_primary(obj);
led->bright = LV_LED_BRIGHT_MAX;
}
static void lv_led_event(const lv_obj_class_t * class_p, lv_event_t * e)
{
LV_UNUSED(class_p);
lv_res_t res;
/* Call the ancestor's event handler */
lv_event_code_t code = lv_event_get_code(e);
if(code != LV_EVENT_DRAW_MAIN && code != LV_EVENT_DRAW_MAIN_END) {
res = lv_obj_event_base(MY_CLASS, e);
if(res != LV_RES_OK) return;
}
lv_obj_t * obj = lv_event_get_target(e);
if(code == LV_EVENT_DRAW_MAIN) {
/*Make darker colors in a temporary style according to the brightness*/
lv_led_t * led = (lv_led_t *)obj;
lv_draw_rect_dsc_t rect_dsc;
lv_draw_rect_dsc_init(&rect_dsc);
lv_obj_init_draw_rect_dsc(obj, LV_PART_MAIN, &rect_dsc);
/*Use the original colors brightness to modify color->led*/
rect_dsc.bg_color = lv_color_mix(led->color, lv_color_black(), lv_color_brightness(rect_dsc.bg_color));
rect_dsc.bg_grad_color = lv_color_mix(led->color, lv_color_black(), lv_color_brightness(rect_dsc.bg_grad_color));
rect_dsc.shadow_color = lv_color_mix(led->color, lv_color_black(), lv_color_brightness(rect_dsc.shadow_color));
rect_dsc.border_color = lv_color_mix(led->color, lv_color_black(), lv_color_brightness(rect_dsc.border_color));
rect_dsc.outline_color = lv_color_mix(led->color, lv_color_black(), lv_color_brightness(rect_dsc.outline_color));
/*Mix. the color with black proportionally with brightness*/
rect_dsc.bg_color = lv_color_mix(rect_dsc.bg_color, lv_color_black(), led->bright);
rect_dsc.bg_grad_color = lv_color_mix(rect_dsc.bg_grad_color, lv_color_black(), led->bright);
rect_dsc.border_color = lv_color_mix(rect_dsc.border_color, lv_color_black(), led->bright);
rect_dsc.shadow_color = lv_color_mix(rect_dsc.shadow_color, lv_color_black(), led->bright);
rect_dsc.outline_color = lv_color_mix(rect_dsc.outline_color, lv_color_black(), led->bright);
/*Set the current shadow width according to brightness proportionally between LV_LED_BRIGHT_OFF
* and LV_LED_BRIGHT_ON*/
rect_dsc.shadow_width = ((led->bright - LV_LED_BRIGHT_MIN) * rect_dsc.shadow_width) /
(LV_LED_BRIGHT_MAX - LV_LED_BRIGHT_MIN);
rect_dsc.shadow_spread = ((led->bright - LV_LED_BRIGHT_MIN) * rect_dsc.shadow_spread) /
(LV_LED_BRIGHT_MAX - LV_LED_BRIGHT_MIN);
const lv_area_t * clip_area = lv_event_get_param(e);
lv_obj_draw_part_dsc_t part_draw_dsc;
lv_obj_draw_dsc_init(&part_draw_dsc, clip_area);
part_draw_dsc.draw_area = &obj->coords;
part_draw_dsc.class_p = MY_CLASS;
part_draw_dsc.type = LV_LED_DRAW_PART_RECTANGLE;
part_draw_dsc.rect_dsc = &rect_dsc;
part_draw_dsc.part = LV_PART_MAIN;
lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &part_draw_dsc);
lv_draw_rect(&obj->coords, clip_area, &rect_dsc);
lv_event_send(obj, LV_EVENT_DRAW_PART_END, &part_draw_dsc);
}
}
#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
*********************/
#include "../../../lvgl.h"
#if LV_USE_LED
/*********************
* DEFINES
*********************/
/** Brightness when the LED if OFF */
#ifndef LV_LED_BRIGHT_MIN
# define LV_LED_BRIGHT_MIN 80
#endif
/** Brightness when the LED if ON */
#ifndef LV_LED_BRIGHT_MAX
# define LV_LED_BRIGHT_MAX 255
#endif
/**********************
* TYPEDEFS
**********************/
/*Data of led*/
typedef struct {
lv_obj_t obj;
lv_color_t color;
uint8_t bright; /**< Current brightness of the LED (0..255)*/
} lv_led_t;
extern const lv_obj_class_t lv_led_class;
/**
* `type` field in `lv_obj_draw_part_dsc_t` if `class_p = lv_led_class`
* Used in `LV_EVENT_DRAW_PART_BEGIN` and `LV_EVENT_DRAW_PART_END`
*/
typedef enum {
LV_LED_DRAW_PART_RECTANGLE, /**< The main rectangle*/
} lv_led_draw_part_type_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a led objects
* @param par pointer to an object, it will be the parent of the new led
* @return pointer to the created led
*/
lv_obj_t * lv_led_create(lv_obj_t * parent);
/**
* Set the color of the LED
* @param led pointer to a LED object
* @param color the color of the the LED
*/
void lv_led_set_color(lv_obj_t * led, lv_color_t color);
/**
* Set the brightness of a LED object
* @param led pointer to a LED object
* @param bright LV_LED_BRIGHT_MIN (max. dark) ... LV_LED_BRIGHT_MAX (max. light)
*/
void lv_led_set_brightness(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);
/**
* 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_brightness(const lv_obj_t * obj);
/**********************
* MACROS
**********************/
#endif /*LV_USE_LED*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_LED_H*/

View File

@@ -0,0 +1,118 @@
/**
* @file lv_list.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_list.h"
#include "../../../core/lv_disp.h"
#include "../../../widgets/lv_label.h"
#include "../../../widgets/lv_img.h"
#include "../../../widgets/lv_btn.h"
#if LV_USE_LIST
/*********************
* DEFINES
*********************/
#define MV_CLASS &lv_list
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
const lv_obj_class_t lv_list_class = {
.base_class = &lv_obj_class,
.width_def = (LV_DPI_DEF * 3) / 2,
.height_def = LV_DPI_DEF * 2
};
const lv_obj_class_t lv_list_btn_class = {
.base_class = &lv_btn_class,
};
const lv_obj_class_t lv_list_text_class = {
.base_class = &lv_label_class,
};
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_obj_t * lv_list_create(lv_obj_t * parent)
{
LV_LOG_INFO("begin");
lv_obj_t * obj = lv_obj_class_create_obj(&lv_list_class, parent);
lv_obj_class_init_obj(obj);
lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_COLUMN);
return obj;
}
lv_obj_t * lv_list_add_text(lv_obj_t * list, const char * txt)
{
LV_LOG_INFO("begin");
lv_obj_t * obj = lv_obj_class_create_obj(&lv_list_text_class, list);
lv_obj_class_init_obj(obj);
lv_label_set_text(obj, txt);
lv_label_set_long_mode(obj, LV_LABEL_LONG_SCROLL_CIRCULAR);
lv_obj_set_width(obj, LV_PCT(100));
return obj;
}
lv_obj_t * lv_list_add_btn(lv_obj_t * list, const char * icon, const char * txt)
{
LV_LOG_INFO("begin");
lv_obj_t * obj = lv_obj_class_create_obj(&lv_list_btn_class, list);
lv_obj_class_init_obj(obj);
lv_obj_set_size(obj, LV_PCT(100), LV_SIZE_CONTENT);
lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_ROW);
if(icon) {
lv_obj_t * img = lv_img_create(obj);
lv_img_set_src(img, icon);
}
if(txt) {
lv_obj_t * label = lv_label_create(obj);
lv_label_set_text(label, txt);
lv_label_set_long_mode(label, LV_LABEL_LONG_SCROLL_CIRCULAR);
lv_obj_set_flex_grow(label, 1);
}
return obj;
}
const char * lv_list_get_btn_text(lv_obj_t * list, lv_obj_t * btn)
{
LV_UNUSED(list);
uint32_t i;
for(i = 0; i < lv_obj_get_child_cnt(btn); i++) {
lv_obj_t * child = lv_obj_get_child(btn, i);
if(lv_obj_check_type(child, &lv_label_class)) {
return lv_label_get_text(child);
}
}
return "";
}
/**********************
* STATIC FUNCTIONS
**********************/
#endif /*LV_USE_LIST*/

View File

@@ -0,0 +1,54 @@
/**
* @file lv_win.h
*
*/
#ifndef LV_LIST_H
#define LV_LIST_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../core/lv_obj.h"
#include "../../layouts/flex/lv_flex.h"
#if LV_USE_LIST
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
extern const lv_obj_class_t lv_list_class;
extern const lv_obj_class_t lv_list_text_class;
extern const lv_obj_class_t lv_list_btn_class;
/**********************
* GLOBAL PROTOTYPES
**********************/
lv_obj_t * lv_list_create(lv_obj_t * parent);
lv_obj_t * lv_list_add_text(lv_obj_t * list, const char * txt);
lv_obj_t * lv_list_add_btn(lv_obj_t * list, const char * icon, const char * txt);
const char * lv_list_get_btn_text(lv_obj_t * list, lv_obj_t * btn);
/**********************
* MACROS
**********************/
#endif /*LV_USE_LIST*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_LIST_H*/

View File

@@ -0,0 +1,55 @@
/**
* @file lv_widgets.h
*
*/
#ifndef LV_WIDGETS_H
#define LV_WIDGETS_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "animimg/lv_animimg.h"
#include "calendar/lv_calendar.h"
#include "calendar/lv_calendar_header_arrow.h"
#include "calendar/lv_calendar_header_dropdown.h"
#include "chart/lv_chart.h"
#include "keyboard/lv_keyboard.h"
#include "list/lv_list.h"
#include "msgbox/lv_msgbox.h"
#include "meter/lv_meter.h"
#include "spinbox/lv_spinbox.h"
#include "spinner/lv_spinner.h"
#include "tabview/lv_tabview.h"
#include "tileview/lv_tileview.h"
#include "win/lv_win.h"
#include "colorwheel/lv_colorwheel.h"
#include "led/lv_led.h"
#include "imgbtn/lv_imgbtn.h"
#include "span/lv_span.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_WIDGETS_H*/

View File

@@ -0,0 +1,709 @@
/**
* @file lv_meter.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_meter.h"
#if LV_USE_METER != 0
#include "../../../misc/lv_assert.h"
/*********************
* DEFINES
*********************/
#define MY_CLASS &lv_meter_class
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_meter_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_meter_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_meter_event(const lv_obj_class_t * class_p, lv_event_t * e);
static void draw_arcs(lv_obj_t * obj, const lv_area_t * clip_area, const lv_area_t * scale_area);
static void draw_ticks_and_labels(lv_obj_t * obj, const lv_area_t * clip_area, const lv_area_t * scale_area);
static void draw_needles(lv_obj_t * obj, const lv_area_t * clip_area, const lv_area_t * scale_area);
static void inv_arc(lv_obj_t * obj, lv_meter_indicator_t * indic, int32_t old_value, int32_t new_value);
static void inv_line(lv_obj_t * obj, lv_meter_indicator_t * indic, int32_t value);
/**********************
* STATIC VARIABLES
**********************/
const lv_obj_class_t lv_meter_class = {
.constructor_cb = lv_meter_constructor,
.destructor_cb = lv_meter_destructor,
.event_cb = lv_meter_event,
.instance_size = sizeof(lv_meter_t),
.base_class = &lv_obj_class
};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_obj_t * lv_meter_create(lv_obj_t * parent)
{
LV_LOG_INFO("begin");
lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
lv_obj_class_init_obj(obj);
return obj;
}
/*=====================
* Add scale
*====================*/
lv_meter_scale_t * lv_meter_add_scale(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_meter_t * meter = (lv_meter_t *)obj;
lv_meter_scale_t * scale = _lv_ll_ins_head(&meter->scale_ll);
LV_ASSERT_MALLOC(scale);
lv_memset_00(scale, sizeof(lv_meter_scale_t));
scale->angle_range = 270;
scale->rotation = 90 + (360 - scale->angle_range) / 2;
scale->min = 0;
scale->max = 100;
scale->tick_cnt = 6;
scale->tick_length = 8;
scale->tick_width = 2;
scale->label_gap = 2;
return scale;
}
void lv_meter_set_scale_ticks(lv_obj_t * obj, lv_meter_scale_t * scale, uint16_t cnt, uint16_t width, uint16_t len,
lv_color_t color)
{
scale->tick_cnt = cnt;
scale->tick_width = width;
scale->tick_length = len;
scale->tick_color = color;
lv_obj_invalidate(obj);
}
void lv_meter_set_scale_major_ticks(lv_obj_t * obj, lv_meter_scale_t * scale, uint16_t nth, uint16_t width,
uint16_t len, lv_color_t color, int16_t label_gap)
{
scale->tick_major_nth = nth;
scale->tick_major_width = width;
scale->tick_major_length = len;
scale->tick_major_color = color;
scale->label_gap = label_gap;
lv_obj_invalidate(obj);
}
void lv_meter_set_scale_range(lv_obj_t * obj, lv_meter_scale_t * scale, int32_t min, int32_t max, uint32_t angle_range,
uint32_t rotation)
{
scale->min = min;
scale->max = max;
scale->angle_range = angle_range;
scale->rotation = rotation;
lv_obj_invalidate(obj);
}
/*=====================
* Add indicator
*====================*/
lv_meter_indicator_t * lv_meter_add_needle_line(lv_obj_t * obj, lv_meter_scale_t * scale, uint16_t width,
lv_color_t color, int16_t r_mod)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_meter_t * meter = (lv_meter_t *)obj;
lv_meter_indicator_t * indic = _lv_ll_ins_head(&meter->indicator_ll);
LV_ASSERT_MALLOC(indic);
lv_memset_00(indic, sizeof(lv_meter_indicator_t));
indic->scale = scale;
indic->opa = LV_OPA_COVER;
indic->type = LV_METER_INDICATOR_TYPE_NEEDLE_LINE;
indic->type_data.needle_line.width = width;
indic->type_data.needle_line.color = color;
indic->type_data.needle_line.r_mod = r_mod;
lv_obj_invalidate(obj);
return indic;
}
lv_meter_indicator_t * lv_meter_add_needle_img(lv_obj_t * obj, lv_meter_scale_t * scale, const void * src,
lv_coord_t pivot_x, lv_coord_t pivot_y)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_meter_t * meter = (lv_meter_t *)obj;
lv_meter_indicator_t * indic = _lv_ll_ins_head(&meter->indicator_ll);
LV_ASSERT_MALLOC(indic);
lv_memset_00(indic, sizeof(lv_meter_indicator_t));
indic->scale = scale;
indic->opa = LV_OPA_COVER;
indic->type = LV_METER_INDICATOR_TYPE_NEEDLE_IMG;
indic->type_data.needle_img.src = src;
indic->type_data.needle_img.pivot.x = pivot_x;
indic->type_data.needle_img.pivot.y = pivot_y;
lv_obj_invalidate(obj);
return indic;
}
lv_meter_indicator_t * lv_meter_add_arc(lv_obj_t * obj, lv_meter_scale_t * scale, uint16_t width, lv_color_t color,
int16_t r_mod)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_meter_t * meter = (lv_meter_t *)obj;
lv_meter_indicator_t * indic = _lv_ll_ins_head(&meter->indicator_ll);
LV_ASSERT_MALLOC(indic);
lv_memset_00(indic, sizeof(lv_meter_indicator_t));
indic->scale = scale;
indic->opa = LV_OPA_COVER;
indic->type = LV_METER_INDICATOR_TYPE_ARC;
indic->type_data.arc.width = width;
indic->type_data.arc.color = color;
indic->type_data.arc.r_mod = r_mod;
lv_obj_invalidate(obj);
return indic;
}
lv_meter_indicator_t * lv_meter_add_scale_lines(lv_obj_t * obj, lv_meter_scale_t * scale, lv_color_t color_start,
lv_color_t color_end, bool local, int16_t width_mod)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_meter_t * meter = (lv_meter_t *)obj;
lv_meter_indicator_t * indic = _lv_ll_ins_head(&meter->indicator_ll);
LV_ASSERT_MALLOC(indic);
lv_memset_00(indic, sizeof(lv_meter_indicator_t));
indic->scale = scale;
indic->opa = LV_OPA_COVER;
indic->type = LV_METER_INDICATOR_TYPE_SCALE_LINES;
indic->type_data.scale_lines.color_start = color_start;
indic->type_data.scale_lines.color_end = color_end;
indic->type_data.scale_lines.local_grad = local;
indic->type_data.scale_lines.width_mod = width_mod;
lv_obj_invalidate(obj);
return indic;
}
/*=====================
* Set indicator value
*====================*/
void lv_meter_set_indicator_value(lv_obj_t * obj, lv_meter_indicator_t * indic, int32_t value)
{
int32_t old_start = indic->start_value;
int32_t old_end = indic->end_value;
indic->start_value = value;
indic->end_value = value;
if(indic->type == LV_METER_INDICATOR_TYPE_ARC) {
inv_arc(obj, indic, old_start, value);
inv_arc(obj, indic, old_end, value);
}
else if(indic->type == LV_METER_INDICATOR_TYPE_NEEDLE_IMG || indic->type == LV_METER_INDICATOR_TYPE_NEEDLE_LINE) {
inv_line(obj, indic, old_start);
inv_line(obj, indic, old_end);
inv_line(obj, indic, value);
}
else {
lv_obj_invalidate(obj);
}
}
void lv_meter_set_indicator_start_value(lv_obj_t * obj, lv_meter_indicator_t * indic, int32_t value)
{
int32_t old_value = indic->start_value;
indic->start_value = value;
if(indic->type == LV_METER_INDICATOR_TYPE_ARC) {
inv_arc(obj, indic, old_value, value);
}
else if(indic->type == LV_METER_INDICATOR_TYPE_NEEDLE_IMG || indic->type == LV_METER_INDICATOR_TYPE_NEEDLE_LINE) {
inv_line(obj, indic, old_value);
inv_line(obj, indic, value);
}
else {
lv_obj_invalidate(obj);
}
}
void lv_meter_set_indicator_end_value(lv_obj_t * obj, lv_meter_indicator_t * indic, int32_t value)
{
int32_t old_value = indic->end_value;
indic->end_value = value;
if(indic->type == LV_METER_INDICATOR_TYPE_ARC) {
inv_arc(obj, indic, old_value, value);
}
else if(indic->type == LV_METER_INDICATOR_TYPE_NEEDLE_IMG || indic->type == LV_METER_INDICATOR_TYPE_NEEDLE_LINE) {
inv_line(obj, indic, old_value);
inv_line(obj, indic, value);
}
else {
lv_obj_invalidate(obj);
}
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_meter_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
LV_TRACE_OBJ_CREATE("begin");
lv_meter_t * meter = (lv_meter_t *)obj;
_lv_ll_init(&meter->scale_ll, sizeof(lv_meter_scale_t));
_lv_ll_init(&meter->indicator_ll, sizeof(lv_meter_indicator_t));
LV_TRACE_OBJ_CREATE("finished");
}
static void lv_meter_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_meter_t * meter = (lv_meter_t *)obj;
_lv_ll_clear(&meter->indicator_ll);
_lv_ll_clear(&meter->scale_ll);
}
static void lv_meter_event(const lv_obj_class_t * class_p, lv_event_t * e)
{
LV_UNUSED(class_p);
lv_res_t res = lv_obj_event_base(MY_CLASS, e);
if(res != LV_RES_OK) return;
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * obj = lv_event_get_target(e);
if(code == LV_EVENT_DRAW_MAIN) {
const lv_area_t * clip_area = lv_event_get_param(e);
lv_area_t scale_area;
lv_obj_get_content_coords(obj, &scale_area);
draw_arcs(obj, clip_area, &scale_area);
draw_ticks_and_labels(obj, clip_area, &scale_area);
draw_needles(obj, clip_area, &scale_area);
lv_coord_t r_edge = lv_area_get_width(&scale_area) / 2;
lv_point_t scale_center;
scale_center.x = scale_area.x1 + r_edge;
scale_center.y = scale_area.y1 + r_edge;
lv_draw_rect_dsc_t mid_dsc;
lv_draw_rect_dsc_init(&mid_dsc);
lv_obj_init_draw_rect_dsc(obj, LV_PART_INDICATOR, &mid_dsc);
lv_coord_t w = lv_obj_get_style_width(obj, LV_PART_INDICATOR) / 2;
lv_coord_t h = lv_obj_get_style_height(obj, LV_PART_INDICATOR) / 2;
lv_area_t nm_cord;
nm_cord.x1 = scale_center.x - w;
nm_cord.y1 = scale_center.y - h;
nm_cord.x2 = scale_center.x + w;
nm_cord.y2 = scale_center.y + h;
lv_draw_rect(&nm_cord, clip_area, &mid_dsc);
}
}
static void draw_arcs(lv_obj_t * obj, const lv_area_t * clip_area, const lv_area_t * scale_area)
{
lv_meter_t * meter = (lv_meter_t *)obj;
lv_draw_arc_dsc_t arc_dsc;
lv_draw_arc_dsc_init(&arc_dsc);
arc_dsc.rounded = lv_obj_get_style_arc_rounded(obj, LV_PART_ITEMS);
lv_coord_t r_out = lv_area_get_width(scale_area) / 2 ;
lv_point_t scale_center;
scale_center.x = scale_area->x1 + r_out;
scale_center.y = scale_area->y1 + r_out;
lv_opa_t opa_main = lv_obj_get_style_opa(obj, LV_PART_MAIN);
lv_meter_indicator_t * indic;
lv_obj_draw_part_dsc_t part_draw_dsc;
lv_obj_draw_dsc_init(&part_draw_dsc, clip_area);
part_draw_dsc.arc_dsc = &arc_dsc;
part_draw_dsc.part = LV_PART_INDICATOR;
part_draw_dsc.class_p = MY_CLASS;
part_draw_dsc.type = LV_METER_DRAW_PART_ARC;
_LV_LL_READ_BACK(&meter->indicator_ll, indic) {
if(indic->type != LV_METER_INDICATOR_TYPE_ARC) continue;
arc_dsc.color = indic->type_data.arc.color;
arc_dsc.width = indic->type_data.arc.width;
arc_dsc.opa = indic->opa > LV_OPA_MAX ? opa_main : (opa_main * indic->opa) >> 8;
lv_meter_scale_t * scale = indic->scale;
int32_t start_angle = lv_map(indic->start_value, scale->min, scale->max, scale->rotation,
scale->rotation + scale->angle_range);
int32_t end_angle = lv_map(indic->end_value, scale->min, scale->max, scale->rotation,
scale->rotation + scale->angle_range);
part_draw_dsc.radius = r_out + indic->type_data.arc.r_mod;
part_draw_dsc.sub_part_ptr = indic;
part_draw_dsc.p1 = &scale_center;
lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &part_draw_dsc);
lv_draw_arc(scale_center.x, scale_center.y, part_draw_dsc.radius, start_angle, end_angle, clip_area, &arc_dsc);
lv_event_send(obj, LV_EVENT_DRAW_PART_END, &part_draw_dsc);
}
}
static void draw_ticks_and_labels(lv_obj_t * obj, const lv_area_t * clip_area, const lv_area_t * scale_area)
{
lv_meter_t * meter = (lv_meter_t *)obj;
lv_point_t p_center;
lv_coord_t r_edge = LV_MIN(lv_area_get_width(scale_area) / 2, lv_area_get_height(scale_area) / 2);
p_center.x = scale_area->x1 + r_edge;
p_center.y = scale_area->y1 + r_edge;
uint8_t i;
lv_draw_line_dsc_t line_dsc;
lv_draw_line_dsc_init(&line_dsc);
lv_obj_init_draw_line_dsc(obj, LV_PART_TICKS, &line_dsc);
line_dsc.raw_end = 1;
lv_draw_label_dsc_t label_dsc;
lv_draw_label_dsc_init(&label_dsc);
lv_obj_init_draw_label_dsc(obj, LV_PART_TICKS, &label_dsc);
lv_meter_scale_t * scale;
lv_draw_mask_radius_param_t inner_minor_mask;
lv_draw_mask_radius_param_t inner_major_mask;
lv_draw_mask_radius_param_t outer_mask;
lv_obj_draw_part_dsc_t part_draw_dsc;
lv_obj_draw_dsc_init(&part_draw_dsc, clip_area);
part_draw_dsc.class_p = MY_CLASS;
part_draw_dsc.part = LV_PART_TICKS;
part_draw_dsc.type = LV_METER_DRAW_PART_TICK;
part_draw_dsc.line_dsc = &line_dsc;
_LV_LL_READ_BACK(&meter->scale_ll, scale) {
part_draw_dsc.sub_part_ptr = scale;
lv_coord_t r_out = r_edge + scale->r_mod;
lv_coord_t r_in_minor = r_out - scale->tick_length;
lv_coord_t r_in_major = r_out - scale->tick_major_length;
lv_area_t area_inner_minor;
area_inner_minor.x1 = p_center.x - r_in_minor;
area_inner_minor.y1 = p_center.y - r_in_minor;
area_inner_minor.x2 = p_center.x + r_in_minor;
area_inner_minor.y2 = p_center.y + r_in_minor;
lv_draw_mask_radius_init(&inner_minor_mask, &area_inner_minor, LV_RADIUS_CIRCLE, true);
lv_area_t area_inner_major;
area_inner_major.x1 = p_center.x - r_in_major;
area_inner_major.y1 = p_center.y - r_in_major;
area_inner_major.x2 = p_center.x + r_in_major - 1;
area_inner_major.y2 = p_center.y + r_in_major - 1;
lv_draw_mask_radius_init(&inner_major_mask, &area_inner_major, LV_RADIUS_CIRCLE, true);
lv_area_t area_outer;
area_outer.x1 = p_center.x - r_out;
area_outer.y1 = p_center.y - r_out;
area_outer.x2 = p_center.x + r_out - 1;
area_outer.y2 = p_center.y + r_out - 1;
lv_draw_mask_radius_init(&outer_mask, &area_outer, LV_RADIUS_CIRCLE, false);
int16_t outer_mask_id = lv_draw_mask_add(&outer_mask, NULL);
int16_t inner_act_mask_id = LV_MASK_ID_INV; /*Will be added later*/
uint32_t minor_cnt = scale->tick_major_nth ? scale->tick_major_nth - 1 : 0xFFFF;
for(i = 0; i < scale->tick_cnt; i++) {
minor_cnt++;
bool major = false;
if(minor_cnt == scale->tick_major_nth) {
minor_cnt = 0;
major = true;
}
int32_t value_of_line = lv_map(i, 0, scale->tick_cnt - 1, scale->min, scale->max);
part_draw_dsc.value = value_of_line;
lv_color_t line_color = major ? scale->tick_major_color : scale->tick_color;
lv_color_t line_color_ori = line_color;
lv_coord_t line_width_ori = major ? scale->tick_major_width : scale->tick_width;
lv_coord_t line_width = line_width_ori;
lv_meter_indicator_t * indic;
_LV_LL_READ_BACK(&meter->indicator_ll, indic) {
if(indic->type != LV_METER_INDICATOR_TYPE_SCALE_LINES) continue;
if(value_of_line >= indic->start_value && value_of_line <= indic->end_value) {
line_width += indic->type_data.scale_lines.width_mod;
if(indic->type_data.scale_lines.color_start.full == indic->type_data.scale_lines.color_end.full) {
line_color = indic->type_data.scale_lines.color_start;
}
else {
lv_opa_t ratio;
if(indic->type_data.scale_lines.local_grad) {
ratio = lv_map(value_of_line, indic->start_value, indic->end_value, LV_OPA_TRANSP, LV_OPA_COVER);
}
else {
ratio = lv_map(value_of_line, scale->min, scale->max, LV_OPA_TRANSP, LV_OPA_COVER);
}
line_color = lv_color_mix(indic->type_data.scale_lines.color_end, indic->type_data.scale_lines.color_start, ratio);
}
}
}
/*`* 256` for extra precision*/
int32_t angle_upscale = ((i * scale->angle_range) << 8) / (scale->tick_cnt - 1);
int32_t angle_low = (angle_upscale >> 8);
int32_t angle_high = angle_low + 1;
int32_t angle_rem = angle_upscale & 0xFF;
/*Interpolate sine and cos*/
int32_t sin_low = lv_trigo_sin(angle_low + scale->rotation);
int32_t sin_high = lv_trigo_sin(angle_high + scale->rotation);
int32_t sin_mid = (sin_low * (256 - angle_rem) + sin_high * angle_rem) >> 8;
int32_t cos_low = lv_trigo_cos(angle_low + scale->rotation);
int32_t cos_high = lv_trigo_cos(angle_high + scale->rotation);
int32_t cos_mid = (cos_low * (256 - angle_rem) + cos_high * angle_rem) >> 8;
line_dsc.color = line_color;
line_dsc.width = line_width;
/*Use the interpolated angle to get the outer x and y coordinates.
*Draw a little bit longer lines to be sure the mask will clip them correctly*/
lv_point_t p_outer;
p_outer.x = (int32_t)(((int32_t)cos_mid * (r_out + line_width) + 127) >> (LV_TRIGO_SHIFT)) + p_center.x;
p_outer.y = (int32_t)(((int32_t)sin_mid * (r_out + line_width) + 127) >> (LV_TRIGO_SHIFT)) + p_center.y;
part_draw_dsc.p1 = &p_outer;
part_draw_dsc.p1 = &p_center;
part_draw_dsc.id = i;
part_draw_dsc.label_dsc = &label_dsc;
/*Draw the text*/
if(major) {
lv_draw_mask_remove_id(outer_mask_id);
uint32_t r_text = r_in_major - scale->label_gap;
lv_point_t p;
p.x = (int32_t)((int32_t)((int32_t)cos_mid * r_text + 127) >> LV_TRIGO_SHIFT) + p_center.x;
p.y = (int32_t)((int32_t)((int32_t)sin_mid * r_text + 127) >> LV_TRIGO_SHIFT) + p_center.y;
lv_draw_label_dsc_t label_dsc_tmp;
lv_memcpy(&label_dsc_tmp, &label_dsc, sizeof(label_dsc_tmp));
part_draw_dsc.label_dsc = &label_dsc_tmp;
char buf[16];
lv_snprintf(buf, sizeof(buf), "%" LV_PRId32, value_of_line);
part_draw_dsc.text = buf;
lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &part_draw_dsc);
lv_point_t label_size;
lv_txt_get_size(&label_size, part_draw_dsc.text, label_dsc.font, label_dsc.letter_space, label_dsc.line_space,
LV_COORD_MAX, LV_TEXT_FLAG_NONE);
lv_area_t label_cord;
label_cord.x1 = p.x - label_size.x / 2;
label_cord.y1 = p.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, clip_area, &label_dsc, part_draw_dsc.text, NULL);
outer_mask_id = lv_draw_mask_add(&outer_mask, NULL);
}
else {
part_draw_dsc.label_dsc = NULL;
part_draw_dsc.text = NULL;
lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &part_draw_dsc);
}
inner_act_mask_id = lv_draw_mask_add(major ? &inner_major_mask : &inner_minor_mask, NULL);
lv_draw_line(&p_outer, &p_center, clip_area, &line_dsc);
lv_draw_mask_remove_id(inner_act_mask_id);
lv_event_send(obj, LV_EVENT_DRAW_MAIN_END, &part_draw_dsc);
line_dsc.color = line_color_ori;
line_dsc.width = line_width_ori;
}
lv_draw_mask_free_param(&inner_minor_mask);
lv_draw_mask_free_param(&inner_major_mask);
lv_draw_mask_free_param(&outer_mask);
lv_draw_mask_remove_id(outer_mask_id);
}
}
static void draw_needles(lv_obj_t * obj, const lv_area_t * clip_area, const lv_area_t * scale_area)
{
lv_meter_t * meter = (lv_meter_t *)obj;
lv_coord_t r_edge = lv_area_get_width(scale_area) / 2;
lv_point_t scale_center;
scale_center.x = scale_area->x1 + r_edge;
scale_center.y = scale_area->y1 + r_edge;
lv_draw_line_dsc_t line_dsc;
lv_draw_line_dsc_init(&line_dsc);
lv_obj_init_draw_line_dsc(obj, LV_PART_ITEMS, &line_dsc);
lv_draw_img_dsc_t img_dsc;
lv_draw_img_dsc_init(&img_dsc);
lv_obj_init_draw_img_dsc(obj, LV_PART_ITEMS, &img_dsc);
lv_opa_t opa_main = lv_obj_get_style_opa(obj, LV_PART_MAIN);
lv_obj_draw_part_dsc_t part_draw_dsc;
lv_obj_draw_dsc_init(&part_draw_dsc, clip_area);
part_draw_dsc.class_p = MY_CLASS;
part_draw_dsc.p1 = &scale_center;
lv_meter_indicator_t * indic;
_LV_LL_READ_BACK(&meter->indicator_ll, indic) {
lv_meter_scale_t * scale = indic->scale;
part_draw_dsc.sub_part_ptr = indic;
if(indic->type == LV_METER_INDICATOR_TYPE_NEEDLE_LINE) {
int32_t angle = lv_map(indic->end_value, scale->min, scale->max, scale->rotation, scale->rotation + scale->angle_range);
lv_coord_t r_out = r_edge + scale->r_mod + indic->type_data.needle_line.r_mod;
lv_point_t p_end;
p_end.y = (lv_trigo_sin(angle) * (r_out)) / LV_TRIGO_SIN_MAX + scale_center.y;
p_end.x = (lv_trigo_cos(angle) * (r_out)) / LV_TRIGO_SIN_MAX + scale_center.x;
line_dsc.color = indic->type_data.needle_line.color;
line_dsc.width = indic->type_data.needle_line.width;
line_dsc.opa = indic->opa > LV_OPA_MAX ? opa_main : (opa_main * indic->opa) >> 8;
part_draw_dsc.id = LV_METER_DRAW_PART_NEEDLE_LINE;
part_draw_dsc.line_dsc = &line_dsc;
part_draw_dsc.p2 = &p_end;
lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &part_draw_dsc);
lv_draw_line(&scale_center, &p_end, clip_area, &line_dsc);
lv_event_send(obj, LV_EVENT_DRAW_PART_END, &part_draw_dsc);
}
else if(indic->type == LV_METER_INDICATOR_TYPE_NEEDLE_IMG) {
if(indic->type_data.needle_img.src == NULL) continue;
int32_t angle = lv_map(indic->end_value, scale->min, scale->max, scale->rotation, scale->rotation + scale->angle_range);
lv_img_header_t info;
lv_img_decoder_get_info(indic->type_data.needle_img.src, &info);
lv_area_t a;
a.x1 = scale_center.x - indic->type_data.needle_img.pivot.x;
a.y1 = scale_center.y - indic->type_data.needle_img.pivot.y;
a.x2 = a.x1 + info.w - 1;
a.y2 = a.y1 + info.h - 1;
img_dsc.opa = indic->opa > LV_OPA_MAX ? opa_main : (opa_main * indic->opa) >> 8;
img_dsc.pivot.x = indic->type_data.needle_img.pivot.x;
img_dsc.pivot.y = indic->type_data.needle_img.pivot.y;
angle = angle * 10;
if(angle > 3600) angle -= 3600;
img_dsc.angle = angle;
part_draw_dsc.img_dsc = &img_dsc;
lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &part_draw_dsc);
lv_draw_img(&a, clip_area, indic->type_data.needle_img.src, &img_dsc);
lv_event_send(obj, LV_EVENT_DRAW_PART_END, &part_draw_dsc);
}
}
}
static void inv_arc(lv_obj_t * obj, lv_meter_indicator_t * indic, int32_t old_value, int32_t new_value)
{
bool rounded = lv_obj_get_style_arc_rounded(obj, LV_PART_ITEMS);
lv_area_t scale_area;
lv_obj_get_content_coords(obj, &scale_area);
lv_coord_t r_out = lv_area_get_width(&scale_area) / 2;
lv_point_t scale_center;
scale_center.x = scale_area.x1 + r_out;
scale_center.y = scale_area.y1 + r_out;
r_out += indic->type_data.arc.r_mod;
lv_meter_scale_t * scale = indic->scale;
int32_t start_angle = lv_map(old_value, scale->min, scale->max, scale->rotation, scale->angle_range + scale->rotation);
int32_t end_angle = lv_map(new_value, scale->min, scale->max, scale->rotation, scale->angle_range + scale->rotation);
lv_area_t a;
lv_draw_arc_get_area(scale_center.x, scale_center.y, r_out, LV_MIN(start_angle, end_angle), LV_MAX(start_angle,
end_angle), indic->type_data.arc.width, rounded, &a);
lv_obj_invalidate_area(obj, &a);
}
static void inv_line(lv_obj_t * obj, lv_meter_indicator_t * indic, int32_t value)
{
lv_area_t scale_area;
lv_obj_get_content_coords(obj, &scale_area);
lv_coord_t r_out = lv_area_get_width(&scale_area) / 2;
lv_point_t scale_center;
scale_center.x = scale_area.x1 + r_out;
scale_center.y = scale_area.y1 + r_out;
lv_meter_scale_t * scale = indic->scale;
if(indic->type == LV_METER_INDICATOR_TYPE_NEEDLE_LINE) {
int32_t angle = lv_map(value, scale->min, scale->max, scale->rotation, scale->rotation + scale->angle_range);
r_out += scale->r_mod + indic->type_data.needle_line.r_mod;
lv_point_t p_end;
p_end.y = (lv_trigo_sin(angle) * (r_out)) / LV_TRIGO_SIN_MAX + scale_center.y;
p_end.x = (lv_trigo_cos(angle) * (r_out)) / LV_TRIGO_SIN_MAX + scale_center.x;
lv_area_t a;
a.x1 = LV_MIN(scale_center.x, p_end.x) - indic->type_data.needle_line.width - 2;
a.y1 = LV_MIN(scale_center.y, p_end.y) - indic->type_data.needle_line.width - 2;
a.x2 = LV_MAX(scale_center.x, p_end.x) + indic->type_data.needle_line.width + 2;
a.y2 = LV_MAX(scale_center.y, p_end.y) + indic->type_data.needle_line.width + 2;
lv_obj_invalidate_area(obj, &a);
}
else if(indic->type == LV_METER_INDICATOR_TYPE_NEEDLE_IMG) {
int32_t angle = lv_map(value, scale->min, scale->max, scale->rotation, scale->rotation + scale->angle_range);
lv_img_header_t info;
lv_img_decoder_get_info(indic->type_data.needle_img.src, &info);
angle = angle * 10;
if(angle > 3600) angle -= 3600;
scale_center.x -= indic->type_data.needle_img.pivot.x;
scale_center.y -= indic->type_data.needle_img.pivot.y;
lv_area_t a;
_lv_img_buf_get_transformed_area(&a, info.w, info.h, angle, LV_IMG_ZOOM_NONE, &indic->type_data.needle_img.pivot);
a.x1 += scale_center.x - 2;
a.y1 += scale_center.y - 2;
a.x2 += scale_center.x + 2;
a.y2 += scale_center.y + 2;
lv_obj_invalidate_area(obj, &a);
}
}
#endif

View File

@@ -0,0 +1,267 @@
/**
* @file lv_meter.h
*
*/
#ifndef LV_METER_H
#define LV_METER_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../lvgl.h"
#if LV_USE_METER != 0
/*Testing of dependencies*/
#if LV_DRAW_COMPLEX == 0
#error "lv_meter: Complex drawing is required. Enable it in lv_conf.h (LV_DRAW_COMPLEX 1)"
#endif
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_color_t tick_color;
uint16_t tick_cnt;
uint16_t tick_length;
uint16_t tick_width;
lv_color_t tick_major_color;
uint16_t tick_major_nth;
uint16_t tick_major_length;
uint16_t tick_major_width;
int16_t label_gap;
int16_t label_color;
int32_t min;
int32_t max;
int16_t r_mod;
uint16_t angle_range;
int16_t rotation;
} lv_meter_scale_t;
enum {
LV_METER_INDICATOR_TYPE_NEEDLE_IMG,
LV_METER_INDICATOR_TYPE_NEEDLE_LINE,
LV_METER_INDICATOR_TYPE_SCALE_LINES,
LV_METER_INDICATOR_TYPE_ARC,
};
typedef uint8_t lv_meter_indicator_type_t;
typedef struct {
lv_meter_scale_t * scale;
lv_meter_indicator_type_t type;
lv_opa_t opa;
int32_t start_value;
int32_t end_value;
union {
struct {
const void * src;
lv_point_t pivot;
} needle_img;
struct {
uint16_t width;
int16_t r_mod;
lv_color_t color;
} needle_line;
struct {
uint16_t width;
const void * src;
lv_color_t color;
int16_t r_mod;
} arc;
struct {
int16_t width_mod;
lv_color_t color_start;
lv_color_t color_end;
uint8_t local_grad : 1;
} scale_lines;
} type_data;
} lv_meter_indicator_t;
/*Data of line meter*/
typedef struct {
lv_obj_t obj;
lv_ll_t scale_ll;
lv_ll_t indicator_ll;
} lv_meter_t;
extern const lv_obj_class_t lv_meter_class;
/**
* `type` field in `lv_obj_draw_part_dsc_t` if `class_p = lv_meter_class`
* Used in `LV_EVENT_DRAW_PART_BEGIN` and `LV_EVENT_DRAW_PART_END`
*/
typedef enum {
LV_METER_DRAW_PART_ARC, /**< The arc indicator*/
LV_METER_DRAW_PART_NEEDLE_LINE, /**< The needle lines*/
LV_METER_DRAW_PART_NEEDLE_IMG, /**< The needle images*/
LV_METER_DRAW_PART_TICK, /**< The tick lines and labels*/
} lv_meter_draw_part_type_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a meter objects
* @param parent pointer to an object, it will be the parent of the new bar.
* @return pointer to the created meter
*/
lv_obj_t * lv_meter_create(lv_obj_t * parent);
/*=====================
* Add scale
*====================*/
/**
* Add a new scale to the meter.
* @param obj pointer to a meter object
* @return the new scale
* @note Indicators can be attached to scales.
*/
lv_meter_scale_t * lv_meter_add_scale(lv_obj_t * obj);
/**
* Set the properties of the ticks of a scale
* @param obj pointer to a meter object
* @param scale pointer to scale (added to `meter`)
* @param cnt number of tick lines
* @param width width of tick lines
* @param len length of tick lines
* @param color color of tick lines
*/
void lv_meter_set_scale_ticks(lv_obj_t * obj, lv_meter_scale_t * scale, uint16_t cnt, uint16_t width, uint16_t len,
lv_color_t color);
/**
* Make some "normal" ticks major ticks and set their attributes.
* Texts with the current value are also added to the major ticks.
* @param obj pointer to a meter object
* @param scale pointer to scale (added to `meter`)
* @param nth make every Nth normal tick major tick. (start from the first on the left)
* @param width width of the major ticks
* @param len length of the major ticks
* @param color color of the major ticks
* @param label_gap gap between the major ticks and the labels
*/
void lv_meter_set_scale_major_ticks(lv_obj_t * obj, lv_meter_scale_t * scale, uint16_t nth, uint16_t width,
uint16_t len, lv_color_t color, int16_t label_gap);
/**
* Set the value and angular range of a scale.
* @param obj pointer to a meter object
* @param scale pointer to scale (added to `meter`)
* @param min the minimum value
* @param max the maximal value
* @param angle_range the angular range of the scale
* @param rotation the angular offset from the 3 o'clock position (clock-wise)
*/
void lv_meter_set_scale_range(lv_obj_t * obj, lv_meter_scale_t * scale, int32_t min, int32_t max, uint32_t angle_range,
uint32_t rotation);
/*=====================
* Add indicator
*====================*/
/**
* Add a needle line indicator the scale
* @param obj pointer to a meter object
* @param scale pointer to scale (added to `meter`)
* @param width width of the line
* @param color color of the line
* @param r_mod the radius modifier (added to the scale's radius) to get the lines length
* @return the new indicator
*/
lv_meter_indicator_t * lv_meter_add_needle_line(lv_obj_t * obj, lv_meter_scale_t * scale, uint16_t width,
lv_color_t color, int16_t r_mod);
/**
* Add a needle image indicator the scale
* @param obj pointer to a meter object
* @param scale pointer to scale (added to `meter`)
* @param src the image source of the indicator. path or pointer to ::lv_img_dsc_t
* @param pivot_x the X pivot point of the needle
* @param pivot_y the Y pivot point of the needle
* @return the new indicator
* @note the needle image should point to the right, like -O----->
*/
lv_meter_indicator_t * lv_meter_add_needle_img(lv_obj_t * obj, lv_meter_scale_t * scale, const void * src,
lv_coord_t pivot_x, lv_coord_t pivot_y);
/**
* Add an arc indicator the scale
* @param obj pointer to a meter object
* @param scale pointer to scale (added to `meter`)
* @param width width of the arc
* @param color color of the arc
* @param r_mod the radius modifier (added to the scale's radius) to get the outer radius of the arc
* @return the new indicator
*/
lv_meter_indicator_t * lv_meter_add_arc(lv_obj_t * obj, lv_meter_scale_t * scale, uint16_t width, lv_color_t color,
int16_t r_mod);
/**
* Add a scale line indicator the scale. It will modify the ticks.
* @param obj pointer to a meter object
* @param scale pointer to scale (added to `meter`)
* @param color_start the start color
* @param color_end the end color
* @param local tell how to map start and end color. true: the indicator's start and end_value; false: the scale's min max value
* @param width_mod add this the affected tick's width
* @return the new indicator
*/
lv_meter_indicator_t * lv_meter_add_scale_lines(lv_obj_t * obj, lv_meter_scale_t * scale, lv_color_t color_start,
lv_color_t color_end, bool local, int16_t width_mod);
/*=====================
* Set indicator value
*====================*/
/**
* Set the value of the indicator. It will set start and and value to the same value
* @param obj pointer to a meter object
* @param indic pointer to an indicator
* @param value the new value
*/
void lv_meter_set_indicator_value(lv_obj_t * obj, lv_meter_indicator_t * indic, int32_t value);
/**
* Set the start value of the indicator.
* @param obj pointer to a meter object
* @param indic pointer to an indicator
* @param value the new value
*/
void lv_meter_set_indicator_start_value(lv_obj_t * obj, lv_meter_indicator_t * indic, int32_t value);
/**
* Set the start value of the indicator.
* @param obj pointer to a meter object
* @param indic pointer to an indicator
* @param value the new value
*/
void lv_meter_set_indicator_end_value(lv_obj_t * obj, lv_meter_indicator_t * indic, int32_t value);
/**********************
* MACROS
**********************/
#endif /*LV_USE_METER*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_METER_H*/

View File

@@ -0,0 +1,209 @@
/**
* @file lv_msgbox.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_msgbox.h"
#if LV_USE_MSGBOX
#include "../../../misc/lv_assert.h"
/*********************
* DEFINES
*********************/
#define LV_MSGBOX_FLAG_AUTO_PARENT LV_OBJ_FLAG_WIDGET_1 /*Mark that the parent was automatically created*/
#define MY_CLASS &lv_msgbox_class
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void msgbox_close_click_event_cb(lv_event_t * e);
/**********************
* STATIC VARIABLES
**********************/
const lv_obj_class_t lv_msgbox_class = {
.base_class = &lv_obj_class,
.width_def = LV_DPI_DEF * 2,
.height_def = LV_SIZE_CONTENT,
.instance_size = sizeof(lv_msgbox_t)
};
const lv_obj_class_t lv_msgbox_content_class = {
.base_class = &lv_obj_class,
.width_def = LV_PCT(100),
.height_def = LV_SIZE_CONTENT,
.instance_size = sizeof(lv_obj_t)
};
const lv_obj_class_t lv_msgbox_backdrop_class = {
.base_class = &lv_obj_class,
.width_def = LV_PCT(100),
.height_def = LV_PCT(100),
.instance_size = sizeof(lv_obj_t)
};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_obj_t * lv_msgbox_create(lv_obj_t * parent, const char * title, const char * txt, const char * btn_txts[],
bool add_close_btn)
{
LV_LOG_INFO("begin");
bool auto_parent = false;
if(parent == NULL) {
auto_parent = true;
parent = lv_obj_class_create_obj(&lv_msgbox_backdrop_class, lv_layer_top());
LV_ASSERT_MALLOC(parent);
lv_obj_class_init_obj(parent);
lv_obj_clear_flag(parent, LV_OBJ_FLAG_IGNORE_LAYOUT);
lv_obj_set_size(parent, LV_PCT(100), LV_PCT(100));
}
lv_obj_t * obj = lv_obj_class_create_obj(&lv_msgbox_class, parent);
LV_ASSERT_MALLOC(obj);
lv_obj_class_init_obj(obj);
if(obj == NULL) return NULL;
lv_msgbox_t * mbox = (lv_msgbox_t *)obj;
if(auto_parent) lv_obj_add_flag(obj, LV_MSGBOX_FLAG_AUTO_PARENT);
lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_ROW_WRAP);
bool has_title = title && strlen(title) > 0;
/*When a close button is required, we need the empty label as spacer to push the button to the right*/
if (add_close_btn || has_title) {
mbox->title = lv_label_create(obj);
lv_label_set_text(mbox->title, has_title ? title : "");
lv_label_set_long_mode(mbox->title, LV_LABEL_LONG_SCROLL_CIRCULAR);
if(add_close_btn) lv_obj_set_flex_grow(mbox->title, 1);
else lv_obj_set_width(mbox->title, LV_PCT(100));
}
if(add_close_btn) {
mbox->close_btn = lv_btn_create(obj);
lv_obj_set_ext_click_area(mbox->close_btn, LV_DPX(10));
lv_obj_add_event_cb(mbox->close_btn, msgbox_close_click_event_cb, LV_EVENT_CLICKED, NULL);
lv_obj_t * label = lv_label_create(mbox->close_btn);
lv_label_set_text(label, LV_SYMBOL_CLOSE);
const lv_font_t * font = lv_obj_get_style_text_font(mbox->close_btn, LV_PART_MAIN);
lv_coord_t close_btn_size = lv_font_get_line_height(font) + LV_DPX(10);
lv_obj_set_size(mbox->close_btn, close_btn_size, close_btn_size);
lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
}
mbox->content = lv_obj_class_create_obj(&lv_msgbox_content_class, obj);
bool has_txt = txt && strlen(txt) > 0;
if (has_txt) {
mbox->text = lv_label_create(mbox->content);
lv_label_set_text(mbox->text, txt);
lv_label_set_long_mode(mbox->text, LV_LABEL_LONG_WRAP);
lv_obj_set_width(mbox->text, lv_pct(100));
}
if(btn_txts) {
mbox->btns = lv_btnmatrix_create(obj);
lv_btnmatrix_set_map(mbox->btns, btn_txts);
lv_btnmatrix_set_btn_ctrl_all(mbox->btns, LV_BTNMATRIX_CTRL_CLICK_TRIG | LV_BTNMATRIX_CTRL_NO_REPEAT);
uint32_t btn_cnt = 0;
while(btn_txts[btn_cnt] && btn_txts[btn_cnt][0] != '\0') {
btn_cnt++;
}
const lv_font_t * font = lv_obj_get_style_text_font(mbox->btns, LV_PART_ITEMS);
lv_coord_t btn_h = lv_font_get_line_height(font) + LV_DPI_DEF / 10;
lv_obj_set_size(mbox->btns, btn_cnt * (2 * LV_DPI_DEF / 3), btn_h);
lv_obj_set_style_max_width(mbox->btns, lv_pct(100), 0);
lv_obj_add_flag(mbox->btns, LV_OBJ_FLAG_EVENT_BUBBLE); /*To see the event directly on the message box*/
}
return obj;
}
lv_obj_t * lv_msgbox_get_title(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_msgbox_t * mbox = (lv_msgbox_t *)obj;
return mbox->title;
}
lv_obj_t * lv_msgbox_get_close_btn(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_msgbox_t * mbox = (lv_msgbox_t *)obj;
return mbox->close_btn;
}
lv_obj_t * lv_msgbox_get_text(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_msgbox_t * mbox = (lv_msgbox_t *)obj;
return mbox->text;
}
lv_obj_t * lv_msgbox_get_content(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_msgbox_t * mbox = (lv_msgbox_t *)obj;
return mbox->content;
}
lv_obj_t * lv_msgbox_get_btns(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_msgbox_t * mbox = (lv_msgbox_t *)obj;
return mbox->btns;
}
uint16_t lv_msgbox_get_active_btn(lv_obj_t * mbox)
{
lv_obj_t * btnm = lv_msgbox_get_btns(mbox);
return lv_btnmatrix_get_selected_btn(btnm);
}
const char * lv_msgbox_get_active_btn_text(lv_obj_t * mbox)
{
lv_obj_t * btnm = lv_msgbox_get_btns(mbox);
return lv_btnmatrix_get_btn_text(btnm, lv_btnmatrix_get_selected_btn(btnm));
}
void lv_msgbox_close(lv_obj_t * mbox)
{
if(lv_obj_has_flag(mbox, LV_MSGBOX_FLAG_AUTO_PARENT)) lv_obj_del(lv_obj_get_parent(mbox));
else lv_obj_del(mbox);
}
void lv_msgbox_close_async(lv_obj_t * dialog)
{
if(lv_obj_has_flag(dialog, LV_MSGBOX_FLAG_AUTO_PARENT)) lv_obj_del_async(lv_obj_get_parent(dialog));
else lv_obj_del_async(dialog);
}
/**********************
* STATIC FUNCTIONS
**********************/
static void msgbox_close_click_event_cb(lv_event_t * e)
{
lv_obj_t * btn = lv_event_get_target(e);
lv_obj_t * mbox = lv_obj_get_parent(btn);
lv_msgbox_close(mbox);
}
#endif /*LV_USE_MSGBOX*/

View File

@@ -0,0 +1,99 @@
/**
* @file lv_mbox.h
*
*/
#ifndef LV_MSGBOX_H
#define LV_MSGBOX_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../lvgl.h"
#if LV_USE_MSGBOX
/*Testing of dependencies*/
#if LV_USE_BTNMATRIX == 0
#error "lv_mbox: lv_btnm is required. Enable it in lv_conf.h (LV_USE_BTNMATRIX 1) "
#endif
#if LV_USE_LABEL == 0
#error "lv_mbox: lv_label is required. Enable it in lv_conf.h (LV_USE_LABEL 1) "
#endif
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_obj_t obj;
lv_obj_t * title;
lv_obj_t * close_btn;
lv_obj_t * content;
lv_obj_t * text;
lv_obj_t * btns;
} lv_msgbox_t;
extern const lv_obj_class_t lv_msgbox_class;
extern const lv_obj_class_t lv_msgbox_content_class;
extern const lv_obj_class_t lv_msgbox_backdrop_class;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a message box object
* @param parent pointer to parent or NULL to create a full screen modal message box
* @param title the title of the message box
* @param txt the text of the message box
* @param btn_txts the buttons as an array of texts terminated by an "" element. E.g. {"btn1", "btn2", ""}
* @param add_close_btn true: add a close button
* @return pointer to the message box object
*/
lv_obj_t * lv_msgbox_create(lv_obj_t * parent, const char * title, const char * txt, const char * btn_txts[],
bool add_close_btn);
lv_obj_t * lv_msgbox_get_title(lv_obj_t * obj);
lv_obj_t * lv_msgbox_get_close_btn(lv_obj_t * obj);
lv_obj_t * lv_msgbox_get_text(lv_obj_t * obj);
lv_obj_t * lv_msgbox_get_content(lv_obj_t * obj);
lv_obj_t * lv_msgbox_get_btns(lv_obj_t * obj);
/**
* Get the index of the selected button
* @param mbox message box object
* @return index of the button (LV_BTNMATRIX_BTN_NONE: if unset)
*/
uint16_t lv_msgbox_get_active_btn(lv_obj_t * mbox);
const char * lv_msgbox_get_active_btn_text(lv_obj_t * mbox);
void lv_msgbox_close(lv_obj_t * mbox);
void lv_msgbox_close_async(lv_obj_t * mbox);
/**********************
* MACROS
**********************/
#endif /*LV_USE_MSGBOX*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_MSGBOX_H*/

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,227 @@
/**
* @file lv_span.h
*
*/
#ifndef LV_SPAN_H
#define LV_SPAN_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../lvgl.h"
#if LV_USE_SPAN != 0
/*********************
* DEFINES
*********************/
#ifndef LV_SPAN_SNIPPET_STACK_SIZE
#define LV_SPAN_SNIPPET_STACK_SIZE 64
#endif
/**********************
* TYPEDEFS
**********************/
enum {
LV_SPAN_OVERFLOW_CLIP,
LV_SPAN_OVERFLOW_ELLIPSIS,
};
typedef uint8_t lv_span_overflow_t;
enum {
LV_SPAN_MODE_FIXED, /**< fixed the obj size*/
LV_SPAN_MODE_EXPAND, /**< Expand the object size to the text size*/
LV_SPAN_MODE_BREAK, /**< Keep width, break the too long lines and expand height*/
};
typedef uint8_t lv_span_mode_t;
typedef struct {
char * txt; /* a pointer to display text */
lv_obj_t * spangroup; /* a pointer to spangroup */
lv_style_t style; /* display text style */
uint8_t static_flag : 1;/* the text is static flag */
} lv_span_t;
/** Data of label*/
typedef struct {
lv_obj_t obj;
lv_coord_t indent; /* first line indent */
lv_coord_t cache_w; /* the cache automatically calculates the width */
lv_coord_t cache_h; /* similar cache_w */
lv_ll_t child_ll;
uint8_t mode : 2; /* details see lv_span_mode_t */
uint8_t overflow : 1; /* details see lv_span_overflow_t */
uint8_t refresh : 1; /* the spangroup need refresh cache_w and cache_h */
} lv_spangroup_t;
extern const lv_obj_class_t lv_spangroup_class;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a spangroup objects
* @param par pointer to an object, it will be the parent of the new spangroup
* @return pointer to the created spangroup
*/
lv_obj_t * lv_spangroup_create(lv_obj_t * par);
/**
* Create a span string descriptor and add to spangroup.
* @param obj pointer to a spangroup object.
* @return pointer to the created span.
*/
lv_span_t * lv_spangroup_new_span(lv_obj_t * obj);
/**
* Remove the span from the spangroup and free memory.
* @param obj pointer to a spangroup object.
* @param span pointer to a span.
*/
void lv_spangroup_del_span(lv_obj_t * obj, lv_span_t * span);
/*=====================
* Setter functions
*====================*/
/**
* Set a new text for a span. Memory will be allocated to store the text by the span.
* @param span pointer to a span.
* @param text pointer to a text.
*/
void lv_span_set_text(lv_span_t * span, const char * text);
/**
* Set a static text. It will not be saved by the span so the 'text' variable
* has to be 'alive' while the span exist.
* @param span pointer to a span.
* @param text pointer to a text.
*/
void lv_span_set_text_static(lv_span_t * span, const char * text);
/**
* Set the align of the spangroup.
* @param obj pointer to a spangroup object.
* @param align see lv_text_align_t for details.
*/
void lv_spangroup_set_align(lv_obj_t * obj, lv_text_align_t align);
/**
* Set the overflow of the spangroup.
* @param obj pointer to a spangroup object.
* @param overflow see lv_span_overflow_t for details.
*/
void lv_spangroup_set_overflow(lv_obj_t * obj, lv_span_overflow_t overflow);
/**
* Set the indent of the spangroup.
* @param obj pointer to a spangroup object.
* @param indent The first line indentation
*/
void lv_spangroup_set_indent(lv_obj_t * obj, lv_coord_t indent);
/**
* Set the mode of the spangroup.
* @param obj pointer to a spangroup object.
* @param mode see lv_span_mode_t for details.
*/
void lv_spangroup_set_mode(lv_obj_t * obj, lv_span_mode_t mode);
/*=====================
* Getter functions
*====================*/
/**
* Get a spangroup child by its index.
*
* @param obj The spangroup object
* @param id the index of the child.
* 0: the oldest (firstly created) child
* 1: the second oldest
* child count-1: the youngest
* -1: the youngest
* -2: the second youngest
* @return The child span at index `id`, or NULL if the ID does not exist
*/
lv_span_t * lv_spangroup_get_child(const lv_obj_t * obj, int32_t id);
/**
*
* @param obj The spangroup object to get the child count of.
* @return The span count of the spangroup.
*/
uint32_t lv_spangroup_get_child_cnt(const lv_obj_t * obj);
/**
* get the align of the spangroup.
* @param obj pointer to a spangroup object.
* @return the align value.
*/
lv_text_align_t lv_spangroup_get_align(lv_obj_t * obj);
/**
* get the overflow of the spangroup.
* @param obj pointer to a spangroup object.
* @return the overflow value.
*/
lv_span_overflow_t lv_spangroup_get_overflow(lv_obj_t * obj);
/**
* get the indent of the spangroup.
* @param obj pointer to a spangroup object.
* @return the indent value.
*/
lv_coord_t lv_spangroup_get_indent(lv_obj_t * obj);
/**
* get the mode of the spangroup.
* @param obj pointer to a spangroup object.
*/
lv_span_mode_t lv_spangroup_get_mode(lv_obj_t * obj);
/**
* get max line height of all span in the spangroup.
* @param obj pointer to a spangroup object.
*/
lv_coord_t lv_spangroup_get_max_line_h(lv_obj_t * obj);
/**
* get the width when all span of spangroup on a line. include spangroup pad.
* @param obj pointer to a spangroup object.
*/
lv_coord_t lv_spangroup_get_expand_width(lv_obj_t * obj);
/**
* get the height with width fixed. the height include spangroup pad.
* @param obj pointer to a spangroup object.
*/
lv_coord_t lv_spangroup_get_expand_height(lv_obj_t * obj, lv_coord_t width);
/*=====================
* Other functions
*====================*/
/**
* update the mode of the spangroup.
* @param obj pointer to a spangroup object.
*/
void lv_spangroup_refr_mode(lv_obj_t * obj);
/**********************
* MACROS
**********************/
#endif /*LV_USE_SPAN*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_SPAN_H*/

View File

@@ -0,0 +1,517 @@
/**
* @file lv_spinbox.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_spinbox.h"
#if LV_USE_SPINBOX
#include "../../../misc/lv_assert.h"
/*********************
* DEFINES
*********************/
#define MY_CLASS &lv_spinbox_class
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_spinbox_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_spinbox_event(const lv_obj_class_t * class_p, lv_event_t * e);
static void lv_spinbox_updatevalue(lv_obj_t * obj);
/**********************
* STATIC VARIABLES
**********************/
const lv_obj_class_t lv_spinbox_class = {
.constructor_cb = lv_spinbox_constructor,
.event_cb = lv_spinbox_event,
.instance_size = sizeof(lv_spinbox_t),
.editable = LV_OBJ_CLASS_EDITABLE_TRUE,
.base_class = &lv_textarea_class
};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_obj_t * lv_spinbox_create(lv_obj_t * parent)
{
LV_LOG_INFO("begin");
lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
lv_obj_class_init_obj(obj);
return obj;
}
/*=====================
* Setter functions
*====================*/
/**
* Set spinbox value
* @param spinbox pointer to spinbox
* @param i value to be set
*/
void lv_spinbox_set_value(lv_obj_t * obj, int32_t i)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
if(i > spinbox->range_max) i = spinbox->range_max;
if(i < spinbox->range_min) i = spinbox->range_min;
spinbox->value = i;
lv_spinbox_updatevalue(obj);
}
/**
* Set spinbox rollover function
* @param spinbox pointer to spinbox
* @param b true or false to enable or disable (default)
*/
void lv_spinbox_set_rollover(lv_obj_t * obj, bool b)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
spinbox->rollover = b;
}
/**
* 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 * obj, uint8_t digit_count, uint8_t separator_position)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
if(digit_count > LV_SPINBOX_MAX_DIGIT_COUNT) digit_count = LV_SPINBOX_MAX_DIGIT_COUNT;
if(separator_position >= digit_count) separator_position = 0;
if(separator_position > LV_SPINBOX_MAX_DIGIT_COUNT) separator_position = LV_SPINBOX_MAX_DIGIT_COUNT;
if(digit_count < LV_SPINBOX_MAX_DIGIT_COUNT) {
int64_t max_val = lv_pow(10, digit_count);
if(spinbox->range_max > max_val - 1) spinbox->range_max = max_val - 1;
if(spinbox->range_min < - max_val + 1) spinbox->range_min = - max_val + 1;
}
spinbox->digit_count = digit_count;
spinbox->dec_point_pos = separator_position;
lv_spinbox_updatevalue(obj);
}
/**
* Set spinbox step
* @param spinbox pointer to spinbox
* @param step steps on increment/decrement
*/
void lv_spinbox_set_step(lv_obj_t * obj, uint32_t step)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
spinbox->step = step;
lv_spinbox_updatevalue(obj);
}
/**
* 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 * obj, int32_t range_min, int32_t range_max)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
spinbox->range_max = range_max;
spinbox->range_min = range_min;
if(spinbox->value > spinbox->range_max) spinbox->value = spinbox->range_max;
if(spinbox->value < spinbox->range_min) spinbox->value = spinbox->range_min;
lv_spinbox_updatevalue(obj);
}
/**
* Set cursor position to a specific digit for edition
* @param spinbox pointer to spinbox
* @param pos selected position in spinbox
*/
void lv_spinbox_set_pos(lv_obj_t * obj, uint8_t pos)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
int32_t step_limit;
step_limit = LV_MAX(spinbox->range_max, (spinbox->range_min < 0 ? (-spinbox->range_min) : spinbox->range_min));
int32_t new_step = spinbox->step * lv_pow(10, pos);
if(pos <= 0) spinbox->step = 1;
else if(new_step <= step_limit) spinbox->step = new_step;
lv_spinbox_updatevalue(obj);
}
/**
* Set direction of digit step when clicking an encoder button while in editing mode
* @param spinbox pointer to spinbox
* @param direction the direction (LV_DIR_RIGHT or LV_DIR_LEFT)
*/
void lv_spinbox_set_digit_step_direction(lv_obj_t *obj, lv_dir_t direction)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
spinbox->digit_step_dir = direction;
lv_spinbox_updatevalue(obj);
}
/*=====================
* 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 * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
return spinbox->value;
}
/**
* Get the spinbox step value (user has to convert to float according to its digit format)
* @param spinbox pointer to spinbox
* @return value integer step value of the spinbox
*/
int32_t lv_spinbox_get_step(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
return spinbox->step;
}
/*=====================
* Other functions
*====================*/
/**
* Select next lower digit for edition
* @param spinbox pointer to spinbox
*/
void lv_spinbox_step_next(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
int32_t new_step = spinbox->step / 10;
if((new_step) > 0)
spinbox->step = new_step;
else
spinbox->step = 1;
lv_spinbox_updatevalue(obj);
}
/**
* Select next higher digit for edition
* @param spinbox pointer to spinbox
*/
void lv_spinbox_step_prev(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
int32_t step_limit;
step_limit = LV_MAX(spinbox->range_max, (spinbox->range_min < 0 ? (-spinbox->range_min) : spinbox->range_min));
int32_t new_step = spinbox->step * 10;
if(new_step <= step_limit) spinbox->step = new_step;
lv_spinbox_updatevalue(obj);
}
/**
* Get spinbox rollover function status
* @param spinbox pointer to spinbox
*/
bool lv_spinbox_get_rollover(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
return spinbox->rollover;
}
/**
* Increment spinbox value by one step
* @param spinbox pointer to spinbox
*/
void lv_spinbox_increment(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
if(spinbox->value + spinbox->step <= spinbox->range_max) {
/*Special mode when zero crossing*/
if((spinbox->value + spinbox->step) > 0 && spinbox->value < 0) spinbox->value = -spinbox->value;
spinbox->value += spinbox->step;
}
else {
// Rollover?
if((spinbox->rollover) && (spinbox->value == spinbox->range_max))
spinbox->value = spinbox->range_min;
else
spinbox->value = spinbox->range_max;
}
lv_spinbox_updatevalue(obj);
}
/**
* Decrement spinbox value by one step
* @param spinbox pointer to spinbox
*/
void lv_spinbox_decrement(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
if(spinbox->value - spinbox->step >= spinbox->range_min) {
/*Special mode when zero crossing*/
if((spinbox->value - spinbox->step) < 0 && spinbox->value > 0) spinbox->value = -spinbox->value;
spinbox->value -= spinbox->step;
}
else {
/*Rollover?*/
if((spinbox->rollover) && (spinbox->value == spinbox->range_min))
spinbox->value = spinbox->range_max;
else
spinbox->value = spinbox->range_min;
}
lv_spinbox_updatevalue(obj);
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_spinbox_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
LV_LOG_TRACE("begin");
lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
/*Initialize the allocated 'ext'*/
spinbox->value = 0;
spinbox->dec_point_pos = 0;
spinbox->digit_count = 5;
spinbox->step = 1;
spinbox->range_max = 99999;
spinbox->range_min = -99999;
spinbox->rollover = false;
spinbox->digit_step_dir = LV_DIR_RIGHT;
lv_textarea_set_one_line(obj, true);
lv_textarea_set_cursor_click_pos(obj, true);
lv_obj_set_width(obj, LV_DPI_DEF);
lv_spinbox_updatevalue(obj);
LV_LOG_TRACE("Spinbox constructor finished");
}
static void lv_spinbox_event(const lv_obj_class_t * class_p, lv_event_t * e)
{
LV_UNUSED(class_p);
/*Call the ancestor's event handler*/
lv_res_t res = LV_RES_OK;
res = lv_obj_event_base(MY_CLASS, e);
if(res != LV_RES_OK) return;
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * obj = lv_event_get_target(e);
lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
if(code == LV_EVENT_RELEASED) {
/*If released with an ENCODER then move to the next digit*/
lv_indev_t * indev = lv_indev_get_act();
if(lv_indev_get_type(indev) == LV_INDEV_TYPE_ENCODER) {
if(lv_group_get_editing(lv_obj_get_group(obj))) {
if (spinbox->digit_count > 1) {
if (spinbox->digit_step_dir == LV_DIR_RIGHT) {
if(spinbox->step > 1) {
lv_spinbox_step_next(obj);
}
else {
/*Restart from the MSB*/
spinbox->step = lv_pow(10, spinbox->digit_count - 2);
lv_spinbox_step_prev(obj);
}
}
else {
if(spinbox->step < lv_pow(10, spinbox->digit_count - 1)) {
lv_spinbox_step_prev(obj);
}
else {
/*Restart from the LSB*/
spinbox->step = 10;
lv_spinbox_step_next(obj);
}
}
}
}
}
/*The cursor has been positioned to a digit.
* Set `step` accordingly*/
else {
const char * txt = lv_textarea_get_text(obj);
size_t txt_len = strlen(txt);
if(txt[spinbox->ta.cursor.pos] == '.') {
lv_textarea_cursor_left(obj);
}
else if(spinbox->ta.cursor.pos == (uint32_t)txt_len) {
lv_textarea_set_cursor_pos(obj, txt_len - 1);
}
else if(spinbox->ta.cursor.pos == 0 && spinbox->range_min < 0) {
lv_textarea_set_cursor_pos(obj, 1);
}
size_t len = spinbox->digit_count - 1;
uint16_t cp = spinbox->ta.cursor.pos;
if(spinbox->ta.cursor.pos > spinbox->dec_point_pos && spinbox->dec_point_pos != 0) cp--;
uint32_t pos = len - cp;
if(spinbox->range_min < 0) pos++;
spinbox->step = 1;
uint16_t i;
for(i = 0; i < pos; i++) spinbox->step *= 10;
}
}
else if(code == LV_EVENT_KEY) {
lv_indev_type_t indev_type = lv_indev_get_type(lv_indev_get_act());
uint32_t c = *((uint32_t *)lv_event_get_param(e)); /*uint32_t because can be UTF-8*/
if(c == LV_KEY_RIGHT) {
if(indev_type == LV_INDEV_TYPE_ENCODER)
lv_spinbox_increment(obj);
else
lv_spinbox_step_next(obj);
}
else if(c == LV_KEY_LEFT) {
if(indev_type == LV_INDEV_TYPE_ENCODER)
lv_spinbox_decrement(obj);
else
lv_spinbox_step_prev(obj);
}
else if(c == LV_KEY_UP) {
lv_spinbox_increment(obj);
}
else if(c == LV_KEY_DOWN) {
lv_spinbox_decrement(obj);
}
else {
lv_textarea_add_char(obj, c);
}
}
}
static void lv_spinbox_updatevalue(lv_obj_t * obj)
{
lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
char buf[LV_SPINBOX_MAX_DIGIT_COUNT + 8];
lv_memset_00(buf, sizeof(buf));
char * buf_p = buf;
uint8_t cur_shift_left = 0;
if(spinbox->range_min < 0) { // hide sign if there are only positive values
/*Add the sign*/
(*buf_p) = spinbox->value >= 0 ? '+' : '-';
buf_p++;
}
else {
/*Cursor need shift to left*/
cur_shift_left++;
}
int32_t i;
char digits[LV_SPINBOX_MAX_DIGIT_COUNT + 4];
/*Convert the numbers to string (the sign is already handled so always covert positive number)*/
lv_snprintf(digits, sizeof(digits), "%" LV_PRId32, LV_ABS(spinbox->value));
/*Add leading zeros*/
int lz_cnt = spinbox->digit_count - (int)strlen(digits);
if(lz_cnt > 0) {
for(i = (uint16_t)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 = (spinbox->dec_point_pos == 0) ? spinbox->digit_count : spinbox->dec_point_pos;
/*Add the decimal part*/
for(i = 0; i < intDigits && digits[i] != '\0'; i++) {
(*buf_p) = digits[i];
buf_p++;
}
if(spinbox->dec_point_pos != 0) {
/*Insert the decimal point*/
(*buf_p) = '.';
buf_p++;
for(/*Leave i*/; i < spinbox->digit_count && digits[i] != '\0'; i++) {
(*buf_p) = digits[i];
buf_p++;
}
}
/*Refresh the text*/
lv_textarea_set_text(obj, (char *)buf);
/*Set the cursor position*/
int32_t step = spinbox->step;
uint8_t cur_pos = (uint8_t)spinbox->digit_count;
while(step >= 10) {
step /= 10;
cur_pos--;
}
if(cur_pos > intDigits) cur_pos++; /*Skip the decimal point*/
cur_pos -= cur_shift_left;
lv_textarea_set_cursor_pos(obj, cur_pos);
}
#endif /*LV_USE_SPINBOX*/

View File

@@ -0,0 +1,178 @@
/**
* @file lv_spinbox.h
*
*/
#ifndef LV_SPINBOX_H
#define LV_SPINBOX_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../lvgl.h"
#if LV_USE_SPINBOX
/*Testing of dependencies*/
#if LV_USE_TEXTAREA == 0
#error "lv_spinbox: lv_ta is required. Enable it in lv_conf.h (LV_USE_TEXTAREA 1) "
#endif
/*********************
* DEFINES
*********************/
#define LV_SPINBOX_MAX_DIGIT_COUNT 10
/**********************
* TYPEDEFS
**********************/
/*Data of spinbox*/
typedef struct {
lv_textarea_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 rollover : 1; // Set to true for rollover functionality
uint16_t digit_step_dir : 2; // the direction the digit will step on encoder button press when editing
} lv_spinbox_t;
extern const lv_obj_class_t lv_spinbox_class;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a spinbox objects
* @param par pointer to an object, it will be the parent of the new spinbox
* @return pointer to the created spinbox
*/
lv_obj_t * lv_spinbox_create(lv_obj_t * parent);
/*=====================
* Setter functions
*====================*/
/**
* Set spinbox value
* @param spinbox pointer to spinbox
* @param i value to be set
*/
void lv_spinbox_set_value(lv_obj_t * obj, int32_t i);
/**
* Set spinbox rollover function
* @param spinbox pointer to spinbox
* @param b true or false to enable or disable (default)
*/
void lv_spinbox_set_rollover(lv_obj_t * obj, bool b);
/**
* 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 * obj, uint8_t digit_count, uint8_t separator_position);
/**
* Set spinbox step
* @param spinbox pointer to spinbox
* @param step steps on increment/decrement. Can be 1, 10, 100, 1000, etc the digit that will change.
*/
void lv_spinbox_set_step(lv_obj_t * obj, 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 * obj, int32_t range_min, int32_t range_max);
/**
* Set cursor position to a specific digit for edition
* @param spinbox pointer to spinbox
* @param pos selected position in spinbox
*/
void lv_spinbox_set_pos(lv_obj_t * obj, uint8_t pos);
/**
* Set direction of digit step when clicking an encoder button while in editing mode
* @param spinbox pointer to spinbox
* @param direction the direction (LV_DIR_RIGHT or LV_DIR_LEFT)
*/
void lv_spinbox_set_digit_step_direction(lv_obj_t * obj, lv_dir_t direction);
/*=====================
* Getter functions
*====================*/
/**
* Get spinbox rollover function status
* @param spinbox pointer to spinbox
*/
bool lv_spinbox_get_rollover(lv_obj_t *obj);
/**
* 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 * obj);
/**
* Get the spinbox step value (user has to convert to float according to its digit format)
* @param spinbox pointer to spinbox
* @return value integer step value of the spinbox
*/
int32_t lv_spinbox_get_step(lv_obj_t * obj);
/*=====================
* 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 * obj);
/**
* Select next higher digit for edition by multiplying the step by 10
* @param spinbox pointer to spinbox
*/
void lv_spinbox_step_prev(lv_obj_t * obj);
/**
* Increment spinbox value by one step
* @param spinbox pointer to spinbox
*/
void lv_spinbox_increment(lv_obj_t * obj);
/**
* Decrement spinbox value by one step
* @param spinbox pointer to spinbox
*/
void lv_spinbox_decrement(lv_obj_t * obj);
/**********************
* MACROS
**********************/
#endif /*LV_USE_SPINBOX*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_USE_SPINBOX*/

View File

@@ -0,0 +1,104 @@
/**
* @file lv_spinner.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_spinner.h"
#if LV_USE_SPINNER
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_spinner_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void arc_anim_start_angle(void * obj, int32_t v);
static void arc_anim_end_angle(void * obj, int32_t v);
/**********************
* STATIC VARIABLES
**********************/
const lv_obj_class_t lv_spinner_class = {
.base_class = &lv_arc_class,
.constructor_cb = lv_spinner_constructor
};
static uint32_t time_param;
static uint32_t arc_length_param;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
* Create a spinner object
* @param par pointer to an object, it will be the parent of the new spinner
* @return pointer to the created spinner
*/
lv_obj_t * lv_spinner_create(lv_obj_t * parent, uint32_t time, uint32_t arc_length)
{
time_param = time;
arc_length_param = arc_length;
lv_obj_t * obj = lv_obj_class_create_obj(&lv_spinner_class, parent);
lv_obj_class_init_obj(obj);
return obj;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_spinner_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_TRACE_OBJ_CREATE("begin");
LV_UNUSED(class_p);
lv_obj_clear_flag(obj, LV_OBJ_FLAG_CLICKABLE);
lv_anim_t a;
lv_anim_init(&a);
lv_anim_set_var(&a, obj);
lv_anim_set_exec_cb(&a, arc_anim_end_angle);
lv_anim_set_repeat_count(&a, LV_ANIM_REPEAT_INFINITE);
lv_anim_set_time(&a, time_param);
lv_anim_set_values(&a, arc_length_param, 360 + arc_length_param);
lv_anim_start(&a);
lv_anim_set_path_cb(&a, lv_anim_path_ease_in_out);
lv_anim_set_values(&a, 0, 360);
lv_anim_set_exec_cb(&a, arc_anim_start_angle);
lv_anim_start(&a);
lv_arc_set_bg_angles(obj, 0, 360);
lv_arc_set_rotation(obj, 270);
}
static void arc_anim_start_angle(void * obj, int32_t v)
{
lv_arc_set_start_angle(obj, (uint16_t) v);
}
static void arc_anim_end_angle(void * obj, int32_t v)
{
lv_arc_set_end_angle(obj, (uint16_t) v);
}
#endif /*LV_USE_SPINNER*/

View File

@@ -0,0 +1,50 @@
/**
* @file lv_spinner.h
*
*/
#ifndef LV_SPINNER_H
#define LV_SPINNER_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../lvgl.h"
#if LV_USE_SPINNER
/*Testing of dependencies*/
#if LV_USE_ARC == 0
#error "lv_spinner: lv_arc is required. Enable it in lv_conf.h (LV_USE_ARC 1) "
#endif
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
extern const lv_obj_class_t lv_spinner_class;
/**********************
* GLOBAL PROTOTYPES
**********************/
lv_obj_t * lv_spinner_create(lv_obj_t * parent, uint32_t time, uint32_t arc_length);
/**********************
* MACROS
**********************/
#endif /*LV_USE_SPINNER*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_SPINNER_H*/

View File

@@ -0,0 +1,313 @@
/**
* @file lv_tabview.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_tabview.h"
#if LV_USE_TABVIEW
#include "../../../misc/lv_assert.h"
/*********************
* DEFINES
*********************/
#define MY_CLASS &lv_tabview_class
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_tabview_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_tabview_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_tabview_event(const lv_obj_class_t * class_p, lv_event_t * e);
static void btns_value_changed_event_cb(lv_event_t * e);
static void cont_scroll_end_event_cb(lv_event_t * e);
/**********************
* STATIC VARIABLES
**********************/
const lv_obj_class_t lv_tabview_class = {
.constructor_cb = lv_tabview_constructor,
.destructor_cb = lv_tabview_destructor,
.event_cb = lv_tabview_event,
.width_def = LV_PCT(100),
.height_def = LV_PCT(100),
.base_class = &lv_obj_class,
.instance_size = sizeof(lv_tabview_t)
};
static lv_dir_t tabpos_create;
static lv_coord_t tabsize_create;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_obj_t * lv_tabview_create(lv_obj_t * parent, lv_dir_t tab_pos, lv_coord_t tab_size)
{
LV_LOG_INFO("begin");
tabpos_create = tab_pos;
tabsize_create = tab_size;
lv_obj_t * obj = lv_obj_class_create_obj(&lv_tabview_class, parent);
lv_obj_class_init_obj(obj);
return obj;
}
lv_obj_t * lv_tabview_add_tab(lv_obj_t * obj, const char * name)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_tabview_t * tabview = (lv_tabview_t *)obj;
lv_obj_t * cont = lv_tabview_get_content(obj);
lv_obj_t * page = lv_obj_create(cont);
lv_obj_set_size(page, LV_PCT(100), LV_PCT(100));
lv_obj_clear_flag(page, LV_OBJ_FLAG_CLICK_FOCUSABLE);
uint32_t tab_id = lv_obj_get_child_cnt(cont);
lv_obj_t * btns = lv_tabview_get_tab_btns(obj);
char ** old_map = tabview->map;
char ** new_map;
/*top or bottom dir*/
if(tabview->tab_pos & LV_DIR_VER) {
new_map = lv_mem_alloc((tab_id + 1) * sizeof(const char *));
lv_memcpy_small(new_map, old_map, sizeof(const char *) * (tab_id - 1));
new_map[tab_id - 1] = lv_mem_alloc(strlen(name) + 1);
strcpy((char *)new_map[tab_id - 1], name);
new_map[tab_id] = "";
}
/*left or right dir*/
else {
new_map = lv_mem_alloc((tab_id * 2) * sizeof(const char *));
lv_memcpy_small(new_map, old_map, sizeof(const char *) * (tab_id - 1) * 2);
if(tabview->tab_cnt == 0) {
new_map[0] = lv_mem_alloc(strlen(name) + 1);
strcpy((char *)new_map[0], name);
new_map[1] = "";
}
else {
new_map[tab_id * 2 - 3] = "\n";
new_map[tab_id * 2 - 2] = lv_mem_alloc(strlen(name) + 1);
new_map[tab_id * 2 - 1] = "";
strcpy((char *)new_map[(tab_id * 2) - 2], name);
}
}
tabview->map = new_map;
lv_btnmatrix_set_map(btns, (const char **)new_map);
lv_mem_free(old_map);
lv_btnmatrix_set_btn_ctrl_all(btns, LV_BTNMATRIX_CTRL_CHECKABLE | LV_BTNMATRIX_CTRL_CLICK_TRIG |
LV_BTNMATRIX_CTRL_NO_REPEAT);
tabview->tab_cnt++;
if(tabview->tab_cnt == 1) {
lv_tabview_set_act(obj, 0, LV_ANIM_OFF);
}
lv_btnmatrix_set_btn_ctrl(btns, tabview->tab_cur, LV_BTNMATRIX_CTRL_CHECKED);
return page;
}
void lv_tabview_set_act(lv_obj_t * obj, uint32_t id, lv_anim_enable_t anim_en)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_tabview_t * tabview = (lv_tabview_t *)obj;
if(id >= tabview->tab_cnt) {
id = tabview->tab_cnt - 1;
}
/*To be sure lv_obj_get_content_width will return valid value*/
lv_obj_update_layout(obj);
lv_obj_t * cont = lv_tabview_get_content(obj);
if(cont == NULL) return;
lv_coord_t gap = lv_obj_get_style_pad_column(cont, LV_PART_MAIN);
lv_coord_t w = lv_obj_get_content_width(cont);
if(lv_obj_get_style_base_dir(obj, LV_PART_MAIN) != LV_BASE_DIR_RTL) {
lv_obj_scroll_to_x(cont, id * (gap + w), anim_en);
}
else {
int32_t id_rtl = -(int32_t)id;
lv_obj_scroll_to_x(cont, (gap + w) * id_rtl, anim_en);
}
lv_obj_t * btns = lv_tabview_get_tab_btns(obj);
lv_btnmatrix_set_btn_ctrl(btns, id, LV_BTNMATRIX_CTRL_CHECKED);
tabview->tab_cur = id;
}
uint16_t lv_tabview_get_tab_act(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_tabview_t * tabview = (lv_tabview_t *)obj;
return tabview->tab_cur;
}
lv_obj_t * lv_tabview_get_content(lv_obj_t * tv)
{
return lv_obj_get_child(tv, 1);
}
lv_obj_t * lv_tabview_get_tab_btns(lv_obj_t * tv)
{
return lv_obj_get_child(tv, 0);
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_tabview_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
lv_tabview_t * tabview = (lv_tabview_t *)obj;
tabview->tab_pos = tabpos_create;
switch(tabview->tab_pos) {
case LV_DIR_TOP:
lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_COLUMN);
break;
case LV_DIR_BOTTOM:
lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_COLUMN_REVERSE);
break;
case LV_DIR_LEFT:
lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_ROW);
break;
case LV_DIR_RIGHT:
lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_ROW_REVERSE);
break;
}
lv_obj_set_size(obj, LV_PCT(100), LV_PCT(100));
lv_obj_t * btnm;
lv_obj_t * cont;
btnm = lv_btnmatrix_create(obj);
cont = lv_obj_create(obj);
lv_btnmatrix_set_one_checked(btnm, true);
tabview->map = lv_mem_alloc(sizeof(const char *));
tabview->map[0] = "";
lv_btnmatrix_set_map(btnm, (const char **)tabview->map);
lv_obj_add_event_cb(btnm, btns_value_changed_event_cb, LV_EVENT_VALUE_CHANGED, NULL);
lv_obj_add_flag(btnm, LV_OBJ_FLAG_EVENT_BUBBLE);
lv_obj_add_event_cb(cont, cont_scroll_end_event_cb, LV_EVENT_ALL, NULL);
lv_obj_set_scrollbar_mode(cont, LV_SCROLLBAR_MODE_OFF);
switch(tabview->tab_pos) {
case LV_DIR_TOP:
case LV_DIR_BOTTOM:
lv_obj_set_size(btnm, LV_PCT(100), tabsize_create);
lv_obj_set_width(cont, LV_PCT(100));
lv_obj_set_flex_grow(cont, 1);
break;
case LV_DIR_LEFT:
case LV_DIR_RIGHT:
lv_obj_set_size(btnm, tabsize_create, LV_PCT(100));
lv_obj_set_height(cont, LV_PCT(100));
lv_obj_set_flex_grow(cont, 1);
break;
}
lv_group_t * g = lv_group_get_default();
if(g) lv_group_add_obj(g, btnm);
lv_obj_set_flex_flow(cont, LV_FLEX_FLOW_ROW);
lv_obj_set_scroll_snap_x(cont, LV_SCROLL_SNAP_CENTER);
lv_obj_add_flag(cont, LV_OBJ_FLAG_SCROLL_ONE);
lv_obj_clear_flag(cont, LV_OBJ_FLAG_SCROLL_ON_FOCUS);
}
static void lv_tabview_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
lv_tabview_t * tabview = (lv_tabview_t *)obj;
uint32_t i;
if(tabview->tab_pos & LV_DIR_VER) {
for(i = 0; i < tabview->tab_cnt; i++) {
lv_mem_free(tabview->map[i]);
tabview->map[i] = NULL;
}
}
if(tabview->tab_pos & LV_DIR_HOR) {
for(i = 0; i < tabview->tab_cnt; i++) {
lv_mem_free(tabview->map[i * 2]);
tabview->map[i * 2] = NULL;
}
}
lv_mem_free(tabview->map);
tabview->map = NULL;
}
static void lv_tabview_event(const lv_obj_class_t * class_p, lv_event_t * e)
{
LV_UNUSED(class_p);
lv_res_t res = lv_obj_event_base(&lv_tabview_class, e);
if(res != LV_RES_OK) return;
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * target = lv_event_get_target(e);
if(code == LV_EVENT_SIZE_CHANGED) {
lv_tabview_set_act(target, lv_tabview_get_tab_act(target), LV_ANIM_OFF);
}
}
static void btns_value_changed_event_cb(lv_event_t * e)
{
lv_obj_t * btns = lv_event_get_target(e);
lv_obj_t * tv = lv_obj_get_parent(btns);
uint32_t id = lv_btnmatrix_get_selected_btn(btns);
lv_tabview_set_act(tv, id, LV_ANIM_ON);
}
static void cont_scroll_end_event_cb(lv_event_t * e)
{
lv_obj_t * cont = lv_event_get_target(e);
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * tv = lv_obj_get_parent(cont);
if(code == LV_EVENT_LAYOUT_CHANGED) {
lv_tabview_set_act(tv, lv_tabview_get_tab_act(tv), LV_ANIM_OFF);
}
else if(code == LV_EVENT_SCROLL_END) {
lv_point_t p;
lv_obj_get_scroll_end(cont, &p);
lv_coord_t w = lv_obj_get_content_width(cont);
lv_coord_t t;
if(lv_obj_get_style_base_dir(tv, LV_PART_MAIN) == LV_BASE_DIR_RTL) t = -(p.x - w / 2) / w;
else t = (p.x + w / 2) / w;
if(t < 0) t = 0;
bool new_tab = false;
if(t != lv_tabview_get_tab_act(tv)) new_tab = true;
lv_tabview_set_act(tv, t, LV_ANIM_ON);
if(new_tab) lv_event_send(tv, LV_EVENT_VALUE_CHANGED, NULL);
}
}
#endif /*LV_USE_TABVIEW*/

View File

@@ -0,0 +1,63 @@
/**
* @file lv_templ.h
*
*/
#ifndef LV_TABVIEW_H
#define LV_TABVIEW_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../lvgl.h"
#if LV_USE_TABVIEW
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_obj_t obj;
char ** map;
uint16_t tab_cnt;
uint16_t tab_cur;
lv_dir_t tab_pos;
} lv_tabview_t;
extern const lv_obj_class_t lv_tabview_class;
/**********************
* GLOBAL PROTOTYPES
**********************/
lv_obj_t * lv_tabview_create(lv_obj_t * parent, lv_dir_t tab_pos, lv_coord_t tab_size);
lv_obj_t * lv_tabview_add_tab(lv_obj_t * tv, const char * name);
lv_obj_t * lv_tabview_get_content(lv_obj_t * tv);
lv_obj_t * lv_tabview_get_tab_btns(lv_obj_t * tv);
void lv_tabview_set_act(lv_obj_t * obj, uint32_t id, lv_anim_enable_t anim_en);
uint16_t lv_tabview_get_tab_act(lv_obj_t * tv);
/**********************
* MACROS
**********************/
#endif /*LV_USE_TABVIEW*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_TABVIEW_H*/

View File

@@ -0,0 +1,186 @@
/**
* @file lv_tileview.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_tileview.h"
#if LV_USE_TILEVIEW
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_tileview_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_tileview_tile_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void tileview_event_cb(lv_event_t * e);
/**********************
* STATIC VARIABLES
**********************/
const lv_obj_class_t lv_tileview_class = {.constructor_cb = lv_tileview_constructor,
.base_class = &lv_obj_class,
.instance_size = sizeof(lv_tileview_t)
};
const lv_obj_class_t lv_tileview_tile_class = {.constructor_cb = lv_tileview_tile_constructor,
.base_class = &lv_obj_class,
.instance_size = sizeof(lv_tileview_tile_t)
};
static lv_dir_t create_dir;
static uint32_t create_col_id;
static uint32_t create_row_id;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_obj_t * lv_tileview_create(lv_obj_t * parent)
{
LV_LOG_INFO("begin");
lv_obj_t * obj = lv_obj_class_create_obj(&lv_tileview_class, parent);
lv_obj_class_init_obj(obj);
return obj;
}
/*======================
* Add/remove functions
*=====================*/
lv_obj_t * lv_tileview_add_tile(lv_obj_t * tv, uint8_t col_id, uint8_t row_id, lv_dir_t dir)
{
LV_LOG_INFO("begin");
create_dir = dir;
create_col_id = col_id;
create_row_id = row_id;
lv_obj_t * obj = lv_obj_class_create_obj(&lv_tileview_tile_class, tv);
lv_obj_class_init_obj(obj);
return obj;
}
void lv_obj_set_tile(lv_obj_t * obj, lv_obj_t * tile_obj, lv_anim_enable_t anim_en)
{
lv_coord_t tx = lv_obj_get_x(tile_obj);
lv_coord_t ty = lv_obj_get_y(tile_obj);
lv_tileview_tile_t * tile = (lv_tileview_tile_t *)tile_obj;
lv_tileview_t * tv = (lv_tileview_t *) obj;
tv->tile_act = (lv_obj_t *)tile;
lv_obj_set_scroll_dir(obj, tile->dir);
lv_obj_scroll_to(obj, tx, ty, anim_en);
}
void lv_obj_set_tile_id(lv_obj_t * tv, uint32_t col_id, uint32_t row_id, lv_anim_enable_t anim_en)
{
lv_coord_t w = lv_obj_get_content_width(tv);
lv_coord_t h = lv_obj_get_content_height(tv);
lv_coord_t tx = col_id * w;
lv_coord_t ty = row_id * h;
uint32_t i;
for(i = 0; i < lv_obj_get_child_cnt(tv); i++) {
lv_obj_t * tile_obj = lv_obj_get_child(tv, i);
lv_coord_t x = lv_obj_get_x(tile_obj);
lv_coord_t y = lv_obj_get_y(tile_obj);
if(x == tx && y == ty) {
lv_obj_set_tile(tv, tile_obj, anim_en);
return;
}
}
LV_LOG_WARN("No tile found with at (%d,%d) index", (int)col_id, (int)row_id);
}
lv_obj_t * lv_tileview_get_tile_act(lv_obj_t * obj)
{
lv_tileview_t * tv = (lv_tileview_t *) obj;
return tv->tile_act;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_tileview_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
lv_obj_set_size(obj, LV_PCT(100), LV_PCT(100));
lv_obj_add_event_cb(obj, tileview_event_cb, LV_EVENT_ALL, NULL);
lv_obj_add_flag(obj, LV_OBJ_FLAG_SCROLL_ONE);
lv_obj_set_scroll_snap_x(obj, LV_SCROLL_SNAP_CENTER);
lv_obj_set_scroll_snap_y(obj, LV_SCROLL_SNAP_CENTER);
}
static void lv_tileview_tile_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
lv_obj_t * parent = lv_obj_get_parent(obj);
lv_obj_set_size(obj, LV_PCT(100), LV_PCT(100));
lv_obj_update_layout(obj); /*Be sure the size is correct*/
lv_obj_set_pos(obj, create_col_id * lv_obj_get_content_width(parent),
create_row_id * lv_obj_get_content_height(parent));
lv_tileview_tile_t * tile = (lv_tileview_tile_t *)obj;
tile->dir = create_dir;
if(create_col_id == 0 && create_row_id == 0) {
lv_obj_set_scroll_dir(parent, create_dir);
}
}
static void tileview_event_cb(lv_event_t * e)
{
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * obj = lv_event_get_target(e);
lv_tileview_t * tv = (lv_tileview_t *) obj;
if(code == LV_EVENT_SCROLL_END) {
lv_coord_t w = lv_obj_get_content_width(obj);
lv_coord_t h = lv_obj_get_content_height(obj);
lv_point_t scroll_end;
lv_obj_get_scroll_end(obj, &scroll_end);
lv_coord_t left = scroll_end.x;
lv_coord_t top = scroll_end.y;
lv_coord_t tx = ((left + (w / 2)) / w) * w;
lv_coord_t ty = ((top + (h / 2)) / h) * h;
lv_dir_t dir = LV_DIR_ALL;
uint32_t i;
for(i = 0; i < lv_obj_get_child_cnt(obj); i++) {
lv_obj_t * tile_obj = lv_obj_get_child(obj, i);
lv_coord_t x = lv_obj_get_x(tile_obj);
lv_coord_t y = lv_obj_get_y(tile_obj);
if(x == tx && y == ty) {
lv_tileview_tile_t * tile = (lv_tileview_tile_t *)tile_obj;
tv->tile_act = (lv_obj_t *)tile;
dir = tile->dir;
lv_event_send(obj, LV_EVENT_VALUE_CHANGED, NULL);
break;
}
}
lv_obj_set_scroll_dir(obj, dir);
}
}
#endif /*LV_USE_TILEVIEW*/

View File

@@ -0,0 +1,72 @@
/**
* @file lv_tileview.h
*
*/
#ifndef LV_TILEVIEW_H
#define LV_TILEVIEW_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../core/lv_obj.h"
#if LV_USE_TILEVIEW
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_obj_t obj;
lv_obj_t * tile_act;
} lv_tileview_t;
typedef struct {
lv_obj_t obj;
lv_dir_t dir;
} lv_tileview_tile_t;
extern const lv_obj_class_t lv_tileview_class;
extern const lv_obj_class_t lv_tileview_tile_class;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a tileview objects
* @param par pointer to an object, it will be the parent of the new tileview
* @return pointer to the created tileview
*/
lv_obj_t * lv_tileview_create(lv_obj_t * parent);
lv_obj_t * lv_tileview_add_tile(lv_obj_t * tv, uint8_t row_id, uint8_t col_id, lv_dir_t dir);
void lv_obj_set_tile(lv_obj_t * tv, lv_obj_t * tile_obj, lv_anim_enable_t anim_en);
void lv_obj_set_tile_id(lv_obj_t * tv, uint32_t col_id, uint32_t row_id, lv_anim_enable_t anim_en);
lv_obj_t * lv_tileview_get_tile_act(lv_obj_t * obj);
/*=====================
* Other functions
*====================*/
/**********************
* MACROS
**********************/
#endif /*LV_USE_TILEVIEW*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_TILEVIEW_H*/

View File

@@ -0,0 +1,110 @@
/**
* @file lv_win.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_win.h"
#if LV_USE_WIN
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_win_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
/**********************
* STATIC VARIABLES
**********************/
const lv_obj_class_t lv_win_class = {
.constructor_cb = lv_win_constructor,
.width_def = LV_PCT(100),
.height_def = LV_PCT(100),
.base_class = &lv_obj_class,
.instance_size = sizeof(lv_win_t)
};
static lv_coord_t create_header_height;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_obj_t * lv_win_create(lv_obj_t * parent, lv_coord_t header_height)
{
LV_LOG_INFO("begin");
create_header_height = header_height;
lv_obj_t * obj = lv_obj_class_create_obj(&lv_win_class, parent);
lv_obj_class_init_obj(obj);
return obj;
}
lv_obj_t * lv_win_add_title(lv_obj_t * win, const char * txt)
{
lv_obj_t * header = lv_win_get_header(win);
lv_obj_t * title = lv_label_create(header);
lv_label_set_long_mode(title, LV_LABEL_LONG_DOT);
lv_label_set_text(title, txt);
lv_obj_set_flex_grow(title, 1);
return title;
}
lv_obj_t * lv_win_add_btn(lv_obj_t * win, const void * icon, lv_coord_t btn_w)
{
lv_obj_t * header = lv_win_get_header(win);
lv_obj_t * btn = lv_btn_create(header);
lv_obj_set_size(btn, btn_w, LV_PCT(100));
lv_obj_t * img = lv_img_create(btn);
lv_img_set_src(img, icon);
lv_obj_align(img, LV_ALIGN_CENTER, 0, 0);
return btn;
}
lv_obj_t * lv_win_get_header(lv_obj_t * win)
{
return lv_obj_get_child(win, 0);
}
lv_obj_t * lv_win_get_content(lv_obj_t * win)
{
return lv_obj_get_child(win, 1);
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_win_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
lv_obj_t * parent = lv_obj_get_parent(obj);
lv_obj_set_size(obj, lv_obj_get_width(parent), lv_obj_get_height(parent));
lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_COLUMN);
lv_obj_t * header = lv_obj_create(obj);
lv_obj_set_size(header, LV_PCT(100), create_header_height);
lv_obj_set_flex_flow(header, LV_FLEX_FLOW_ROW);
lv_obj_set_flex_align(header, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
lv_obj_t * cont = lv_obj_create(obj);
lv_obj_set_flex_grow(cont, 1);
lv_obj_set_width(cont, LV_PCT(100));
}
#endif

View File

@@ -0,0 +1,51 @@
/**
* @file lv_win.h
*
*/
#ifndef LV_WIN_H
#define LV_WIN_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../lvgl.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_obj_t obj;
} lv_win_t;
extern const lv_obj_class_t lv_win_class;
/**********************
* GLOBAL PROTOTYPES
**********************/
lv_obj_t * lv_win_create(lv_obj_t * parent, lv_coord_t header_height);
lv_obj_t * lv_win_add_title(lv_obj_t * win, const char * txt);
lv_obj_t * lv_win_add_btn(lv_obj_t * win, const void * icon, lv_coord_t btn_w);
lv_obj_t * lv_win_get_header(lv_obj_t * win);
lv_obj_t * lv_win_get_content(lv_obj_t * win);
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_WIN_H*/