feat: added PicoW_Sensor code template

Credits to @ext-erich.styger that provided the template
This commit is contained in:
SylvanArnold
2025-04-22 11:30:45 +02:00
committed by Sylvan Arnold
parent b2e9eab44e
commit 6cd510e749
985 changed files with 606823 additions and 0 deletions

View File

@@ -0,0 +1,31 @@
# 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 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
- lv_menu: @HX2003

View File

@@ -0,0 +1,596 @@
/**
* @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 max_main_size,
lv_coord_t item_gap, 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
**********************/
uint16_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_PROP_FLAG_NONE);
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) {
LV_ASSERT(grow_value_sum != 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, false);
}
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,152 @@
/**
* @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 uint16_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,782 @@
/**
* @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
**********************/
uint16_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 grid 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;
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->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, false);
}
}
/**
* 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 uint16_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,258 @@
/**
* @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(lv_fs_get_ext(fn), "bmp") == 0) { /*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(lv_fs_get_ext(fn), "bmp") != 0) {
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]) {
lv_fs_close(&b.f);
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;
bool color_depth_error = false;
if(LV_COLOR_DEPTH == 32 && (b.bpp != 32 && b.bpp != 24)) {
LV_LOG_WARN("LV_COLOR_DEPTH == 32 but bpp is %d (should be 32 or 24)", b.bpp);
color_depth_error = true;
}
else if(LV_COLOR_DEPTH == 16 && b.bpp != 16) {
LV_LOG_WARN("LV_COLOR_DEPTH == 16 but bpp is %d (should be 16)", b.bpp);
color_depth_error = true;
}
else if(LV_COLOR_DEPTH == 8 && b.bpp != 8) {
LV_LOG_WARN("LV_COLOR_DEPTH == 8 but bpp is %d (should be 8)", b.bpp);
color_depth_error = true;
}
if(color_depth_error) {
dsc->error_msg = "Color depth mismatch";
lv_fs_close(&b.f);
return LV_RES_INV;
}
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 == 16 && LV_COLOR_16_SWAP == 1
for(unsigned int i = 0; i < len * (b->bpp / 8); i += 2) {
buf[i] = buf[i] ^ buf[i + 1];
buf[i + 1] = buf[i] ^ buf[i + 1];
buf[i] = buf[i] ^ buf[i + 1];
}
#elif 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_bmp.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,875 @@
/**
* @file lv_ffmpeg.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_ffmpeg.h"
#if LV_USE_FFMPEG != 0
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/imgutils.h>
#include <libavutil/samplefmt.h>
#include <libavutil/timestamp.h>
#include <libswscale/swscale.h>
/*********************
* DEFINES
*********************/
#if LV_COLOR_DEPTH == 1 || LV_COLOR_DEPTH == 8
#define AV_PIX_FMT_TRUE_COLOR AV_PIX_FMT_RGB8
#elif LV_COLOR_DEPTH == 16
#if LV_COLOR_16_SWAP == 0
#define AV_PIX_FMT_TRUE_COLOR AV_PIX_FMT_RGB565LE
#else
#define AV_PIX_FMT_TRUE_COLOR AV_PIX_FMT_RGB565BE
#endif
#elif LV_COLOR_DEPTH == 32
#define AV_PIX_FMT_TRUE_COLOR AV_PIX_FMT_BGR0
#else
#error Unsupported LV_COLOR_DEPTH
#endif
#define MY_CLASS &lv_ffmpeg_player_class
#define FRAME_DEF_REFR_PERIOD 33 /*[ms]*/
/**********************
* TYPEDEFS
**********************/
struct ffmpeg_context_s {
AVFormatContext * fmt_ctx;
AVCodecContext * video_dec_ctx;
AVStream * video_stream;
uint8_t * video_src_data[4];
uint8_t * video_dst_data[4];
struct SwsContext * sws_ctx;
AVFrame * frame;
AVPacket pkt;
int video_stream_idx;
int video_src_linesize[4];
int video_dst_linesize[4];
enum AVPixelFormat video_dst_pix_fmt;
bool has_alpha;
};
#pragma pack(1)
struct lv_img_pixel_color_s {
lv_color_t c;
uint8_t alpha;
};
#pragma pack()
/**********************
* 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 void decoder_close(lv_img_decoder_t * dec, lv_img_decoder_dsc_t * dsc);
static struct ffmpeg_context_s * ffmpeg_open_file(const char * path);
static void ffmpeg_close(struct ffmpeg_context_s * ffmpeg_ctx);
static void ffmpeg_close_src_ctx(struct ffmpeg_context_s * ffmpeg_ctx);
static void ffmpeg_close_dst_ctx(struct ffmpeg_context_s * ffmpeg_ctx);
static int ffmpeg_image_allocate(struct ffmpeg_context_s * ffmpeg_ctx);
static int ffmpeg_get_img_header(const char * path, lv_img_header_t * header);
static int ffmpeg_get_frame_refr_period(struct ffmpeg_context_s * ffmpeg_ctx);
static uint8_t * ffmpeg_get_img_data(struct ffmpeg_context_s * ffmpeg_ctx);
static int ffmpeg_update_next_frame(struct ffmpeg_context_s * ffmpeg_ctx);
static int ffmpeg_output_video_frame(struct ffmpeg_context_s * ffmpeg_ctx);
static bool ffmpeg_pix_fmt_has_alpha(enum AVPixelFormat pix_fmt);
static bool ffmpeg_pix_fmt_is_yuv(enum AVPixelFormat pix_fmt);
static void lv_ffmpeg_player_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_ffmpeg_player_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
#if LV_COLOR_DEPTH != 32
static void convert_color_depth(uint8_t * img, uint32_t px_cnt);
#endif
/**********************
* STATIC VARIABLES
**********************/
const lv_obj_class_t lv_ffmpeg_player_class = {
.constructor_cb = lv_ffmpeg_player_constructor,
.destructor_cb = lv_ffmpeg_player_destructor,
.instance_size = sizeof(lv_ffmpeg_player_t),
.base_class = &lv_img_class
};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_ffmpeg_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);
#if LV_FFMPEG_AV_DUMP_FORMAT == 0
av_log_set_level(AV_LOG_QUIET);
#endif
}
int lv_ffmpeg_get_frame_num(const char * path)
{
int ret = -1;
struct ffmpeg_context_s * ffmpeg_ctx = ffmpeg_open_file(path);
if(ffmpeg_ctx) {
ret = ffmpeg_ctx->video_stream->nb_frames;
ffmpeg_close(ffmpeg_ctx);
}
return ret;
}
lv_obj_t * lv_ffmpeg_player_create(lv_obj_t * parent)
{
lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
lv_obj_class_init_obj(obj);
return obj;
}
lv_res_t lv_ffmpeg_player_set_src(lv_obj_t * obj, const char * path)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_res_t res = LV_RES_INV;
lv_ffmpeg_player_t * player = (lv_ffmpeg_player_t *)obj;
if(player->ffmpeg_ctx) {
ffmpeg_close(player->ffmpeg_ctx);
player->ffmpeg_ctx = NULL;
}
lv_timer_pause(player->timer);
player->ffmpeg_ctx = ffmpeg_open_file(path);
if(!player->ffmpeg_ctx) {
LV_LOG_ERROR("ffmpeg file open failed: %s", path);
goto failed;
}
if(ffmpeg_image_allocate(player->ffmpeg_ctx) < 0) {
LV_LOG_ERROR("ffmpeg image allocate failed");
ffmpeg_close(player->ffmpeg_ctx);
goto failed;
}
bool has_alpha = player->ffmpeg_ctx->has_alpha;
int width = player->ffmpeg_ctx->video_dec_ctx->width;
int height = player->ffmpeg_ctx->video_dec_ctx->height;
uint32_t data_size = 0;
if(has_alpha) {
data_size = width * height * LV_IMG_PX_SIZE_ALPHA_BYTE;
}
else {
data_size = width * height * LV_COLOR_SIZE / 8;
}
player->imgdsc.header.always_zero = 0;
player->imgdsc.header.w = width;
player->imgdsc.header.h = height;
player->imgdsc.data_size = data_size;
player->imgdsc.header.cf = has_alpha ? LV_IMG_CF_TRUE_COLOR_ALPHA : LV_IMG_CF_TRUE_COLOR;
player->imgdsc.data = ffmpeg_get_img_data(player->ffmpeg_ctx);
lv_img_set_src(&player->img.obj, &(player->imgdsc));
int period = ffmpeg_get_frame_refr_period(player->ffmpeg_ctx);
if(period > 0) {
LV_LOG_INFO("frame refresh period = %d ms, rate = %d fps",
period, 1000 / period);
lv_timer_set_period(player->timer, period);
}
else {
LV_LOG_WARN("unable to get frame refresh period");
}
res = LV_RES_OK;
failed:
return res;
}
void lv_ffmpeg_player_set_cmd(lv_obj_t * obj, lv_ffmpeg_player_cmd_t cmd)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_ffmpeg_player_t * player = (lv_ffmpeg_player_t *)obj;
if(!player->ffmpeg_ctx) {
LV_LOG_ERROR("ffmpeg_ctx is NULL");
return;
}
lv_timer_t * timer = player->timer;
switch(cmd) {
case LV_FFMPEG_PLAYER_CMD_START:
av_seek_frame(player->ffmpeg_ctx->fmt_ctx,
0, 0, AVSEEK_FLAG_BACKWARD);
lv_timer_resume(timer);
LV_LOG_INFO("ffmpeg player start");
break;
case LV_FFMPEG_PLAYER_CMD_STOP:
av_seek_frame(player->ffmpeg_ctx->fmt_ctx,
0, 0, AVSEEK_FLAG_BACKWARD);
lv_timer_pause(timer);
LV_LOG_INFO("ffmpeg player stop");
break;
case LV_FFMPEG_PLAYER_CMD_PAUSE:
lv_timer_pause(timer);
LV_LOG_INFO("ffmpeg player pause");
break;
case LV_FFMPEG_PLAYER_CMD_RESUME:
lv_timer_resume(timer);
LV_LOG_INFO("ffmpeg player resume");
break;
default:
LV_LOG_ERROR("Error cmd: %d", cmd);
break;
}
}
void lv_ffmpeg_player_set_auto_restart(lv_obj_t * obj, bool en)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_ffmpeg_player_t * player = (lv_ffmpeg_player_t *)obj;
player->auto_restart = en;
}
/**********************
* STATIC FUNCTIONS
**********************/
static lv_res_t decoder_info(lv_img_decoder_t * decoder, const void * src, lv_img_header_t * header)
{
/* Get the source type */
lv_img_src_t src_type = lv_img_src_get_type(src);
if(src_type == LV_IMG_SRC_FILE) {
const char * fn = src;
if(ffmpeg_get_img_header(fn, header) < 0) {
LV_LOG_ERROR("ffmpeg can't get image header");
return LV_RES_INV;
}
return LV_RES_OK;
}
/* If didn't succeeded earlier then it's an error */
return LV_RES_INV;
}
static lv_res_t decoder_open(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc)
{
if(dsc->src_type == LV_IMG_SRC_FILE) {
const char * path = dsc->src;
struct ffmpeg_context_s * ffmpeg_ctx = ffmpeg_open_file(path);
if(ffmpeg_ctx == NULL) {
return LV_RES_INV;
}
if(ffmpeg_image_allocate(ffmpeg_ctx) < 0) {
LV_LOG_ERROR("ffmpeg image allocate failed");
ffmpeg_close(ffmpeg_ctx);
return LV_RES_INV;
}
if(ffmpeg_update_next_frame(ffmpeg_ctx) < 0) {
ffmpeg_close(ffmpeg_ctx);
LV_LOG_ERROR("ffmpeg update frame failed");
return LV_RES_INV;
}
ffmpeg_close_src_ctx(ffmpeg_ctx);
uint8_t * img_data = ffmpeg_get_img_data(ffmpeg_ctx);
#if LV_COLOR_DEPTH != 32
if(ffmpeg_ctx->has_alpha) {
convert_color_depth(img_data, dsc->header.w * dsc->header.h);
}
#endif
dsc->user_data = ffmpeg_ctx;
dsc->img_data = img_data;
/* The image is fully decoded. Return with its pointer */
return LV_RES_OK;
}
/* If not returned earlier then it failed */
return LV_RES_INV;
}
static void decoder_close(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc)
{
struct ffmpeg_context_s * ffmpeg_ctx = dsc->user_data;
ffmpeg_close(ffmpeg_ctx);
}
#if LV_COLOR_DEPTH != 32
static void convert_color_depth(uint8_t * img, uint32_t px_cnt)
{
lv_color32_t * img_src_p = (lv_color32_t *)img;
struct lv_img_pixel_color_s * img_dst_p = (struct lv_img_pixel_color_s *)img;
for(uint32_t i = 0; i < px_cnt; i++) {
lv_color32_t temp = *img_src_p;
img_dst_p->c = lv_color_hex(temp.full);
img_dst_p->alpha = temp.ch.alpha;
img_src_p++;
img_dst_p++;
}
}
#endif
static uint8_t * ffmpeg_get_img_data(struct ffmpeg_context_s * ffmpeg_ctx)
{
uint8_t * img_data = ffmpeg_ctx->video_dst_data[0];
if(img_data == NULL) {
LV_LOG_ERROR("ffmpeg video dst data is NULL");
}
return img_data;
}
static bool ffmpeg_pix_fmt_has_alpha(enum AVPixelFormat pix_fmt)
{
const AVPixFmtDescriptor * desc = av_pix_fmt_desc_get(pix_fmt);
if(desc == NULL) {
return false;
}
if(pix_fmt == AV_PIX_FMT_PAL8) {
return true;
}
return (desc->flags & AV_PIX_FMT_FLAG_ALPHA) ? true : false;
}
static bool ffmpeg_pix_fmt_is_yuv(enum AVPixelFormat pix_fmt)
{
const AVPixFmtDescriptor * desc = av_pix_fmt_desc_get(pix_fmt);
if(desc == NULL) {
return false;
}
return !(desc->flags & AV_PIX_FMT_FLAG_RGB) && desc->nb_components >= 2;
}
static int ffmpeg_output_video_frame(struct ffmpeg_context_s * ffmpeg_ctx)
{
int ret = -1;
int width = ffmpeg_ctx->video_dec_ctx->width;
int height = ffmpeg_ctx->video_dec_ctx->height;
AVFrame * frame = ffmpeg_ctx->frame;
if(frame->width != width
|| frame->height != height
|| frame->format != ffmpeg_ctx->video_dec_ctx->pix_fmt) {
/* To handle this change, one could call av_image_alloc again and
* decode the following frames into another rawvideo file.
*/
LV_LOG_ERROR("Width, height and pixel format have to be "
"constant in a rawvideo file, but the width, height or "
"pixel format of the input video changed:\n"
"old: width = %d, height = %d, format = %s\n"
"new: width = %d, height = %d, format = %s\n",
width,
height,
av_get_pix_fmt_name(ffmpeg_ctx->video_dec_ctx->pix_fmt),
frame->width, frame->height,
av_get_pix_fmt_name(frame->format));
goto failed;
}
LV_LOG_TRACE("video_frame coded_n:%d", frame->coded_picture_number);
/* copy decoded frame to destination buffer:
* this is required since rawvideo expects non aligned data
*/
av_image_copy(ffmpeg_ctx->video_src_data, ffmpeg_ctx->video_src_linesize,
(const uint8_t **)(frame->data), frame->linesize,
ffmpeg_ctx->video_dec_ctx->pix_fmt, width, height);
if(ffmpeg_ctx->sws_ctx == NULL) {
int swsFlags = SWS_BILINEAR;
if(ffmpeg_pix_fmt_is_yuv(ffmpeg_ctx->video_dec_ctx->pix_fmt)) {
/* When the video width and height are not multiples of 8,
* and there is no size change in the conversion,
* a blurry screen will appear on the right side
* This problem was discovered in 2012 and
* continues to exist in version 4.1.3 in 2019
* This problem can be avoided by increasing SWS_ACCURATE_RND
*/
if((width & 0x7) || (height & 0x7)) {
LV_LOG_WARN("The width(%d) and height(%d) the image "
"is not a multiple of 8, "
"the decoding speed may be reduced",
width, height);
swsFlags |= SWS_ACCURATE_RND;
}
}
ffmpeg_ctx->sws_ctx = sws_getContext(
width, height, ffmpeg_ctx->video_dec_ctx->pix_fmt,
width, height, ffmpeg_ctx->video_dst_pix_fmt,
swsFlags,
NULL, NULL, NULL);
}
if(!ffmpeg_ctx->has_alpha) {
int lv_linesize = sizeof(lv_color_t) * width;
int dst_linesize = ffmpeg_ctx->video_dst_linesize[0];
if(dst_linesize != lv_linesize) {
LV_LOG_WARN("ffmpeg linesize = %d, but lvgl image require %d",
dst_linesize,
lv_linesize);
ffmpeg_ctx->video_dst_linesize[0] = lv_linesize;
}
}
ret = sws_scale(
ffmpeg_ctx->sws_ctx,
(const uint8_t * const *)(ffmpeg_ctx->video_src_data),
ffmpeg_ctx->video_src_linesize,
0,
height,
ffmpeg_ctx->video_dst_data,
ffmpeg_ctx->video_dst_linesize);
failed:
return ret;
}
static int ffmpeg_decode_packet(AVCodecContext * dec, const AVPacket * pkt,
struct ffmpeg_context_s * ffmpeg_ctx)
{
int ret = 0;
/* submit the packet to the decoder */
ret = avcodec_send_packet(dec, pkt);
if(ret < 0) {
LV_LOG_ERROR("Error submitting a packet for decoding (%s)",
av_err2str(ret));
return ret;
}
/* get all the available frames from the decoder */
while(ret >= 0) {
ret = avcodec_receive_frame(dec, ffmpeg_ctx->frame);
if(ret < 0) {
/* those two return values are special and mean there is
* no output frame available,
* but there were no errors during decoding
*/
if(ret == AVERROR_EOF || ret == AVERROR(EAGAIN)) {
return 0;
}
LV_LOG_ERROR("Error during decoding (%s)", av_err2str(ret));
return ret;
}
/* write the frame data to output file */
if(dec->codec->type == AVMEDIA_TYPE_VIDEO) {
ret = ffmpeg_output_video_frame(ffmpeg_ctx);
}
av_frame_unref(ffmpeg_ctx->frame);
if(ret < 0) {
LV_LOG_WARN("ffmpeg_decode_packet ended %d", ret);
return ret;
}
}
return 0;
}
static int ffmpeg_open_codec_context(int * stream_idx,
AVCodecContext ** dec_ctx, AVFormatContext * fmt_ctx,
enum AVMediaType type)
{
int ret;
int stream_index;
AVStream * st;
AVCodec * dec = NULL;
AVDictionary * opts = NULL;
ret = av_find_best_stream(fmt_ctx, type, -1, -1, NULL, 0);
if(ret < 0) {
LV_LOG_ERROR("Could not find %s stream in input file",
av_get_media_type_string(type));
return ret;
}
else {
stream_index = ret;
st = fmt_ctx->streams[stream_index];
/* find decoder for the stream */
dec = avcodec_find_decoder(st->codecpar->codec_id);
if(dec == NULL) {
LV_LOG_ERROR("Failed to find %s codec",
av_get_media_type_string(type));
return AVERROR(EINVAL);
}
/* Allocate a codec context for the decoder */
*dec_ctx = avcodec_alloc_context3(dec);
if(*dec_ctx == NULL) {
LV_LOG_ERROR("Failed to allocate the %s codec context",
av_get_media_type_string(type));
return AVERROR(ENOMEM);
}
/* Copy codec parameters from input stream to output codec context */
if((ret = avcodec_parameters_to_context(*dec_ctx, st->codecpar)) < 0) {
LV_LOG_ERROR(
"Failed to copy %s codec parameters to decoder context",
av_get_media_type_string(type));
return ret;
}
/* Init the decoders */
if((ret = avcodec_open2(*dec_ctx, dec, &opts)) < 0) {
LV_LOG_ERROR("Failed to open %s codec",
av_get_media_type_string(type));
return ret;
}
*stream_idx = stream_index;
}
return 0;
}
static int ffmpeg_get_img_header(const char * filepath,
lv_img_header_t * header)
{
int ret = -1;
AVFormatContext * fmt_ctx = NULL;
AVCodecContext * video_dec_ctx = NULL;
int video_stream_idx;
/* open input file, and allocate format context */
if(avformat_open_input(&fmt_ctx, filepath, NULL, NULL) < 0) {
LV_LOG_ERROR("Could not open source file %s", filepath);
goto failed;
}
/* retrieve stream information */
if(avformat_find_stream_info(fmt_ctx, NULL) < 0) {
LV_LOG_ERROR("Could not find stream information");
goto failed;
}
if(ffmpeg_open_codec_context(&video_stream_idx, &video_dec_ctx,
fmt_ctx, AVMEDIA_TYPE_VIDEO)
>= 0) {
bool has_alpha = ffmpeg_pix_fmt_has_alpha(video_dec_ctx->pix_fmt);
/* allocate image where the decoded image will be put */
header->w = video_dec_ctx->width;
header->h = video_dec_ctx->height;
header->always_zero = 0;
header->cf = (has_alpha ? LV_IMG_CF_TRUE_COLOR_ALPHA : LV_IMG_CF_TRUE_COLOR);
ret = 0;
}
failed:
avcodec_free_context(&video_dec_ctx);
avformat_close_input(&fmt_ctx);
return ret;
}
static int ffmpeg_get_frame_refr_period(struct ffmpeg_context_s * ffmpeg_ctx)
{
int avg_frame_rate_num = ffmpeg_ctx->video_stream->avg_frame_rate.num;
if(avg_frame_rate_num > 0) {
int period = 1000 * (int64_t)ffmpeg_ctx->video_stream->avg_frame_rate.den
/ avg_frame_rate_num;
return period;
}
return -1;
}
static int ffmpeg_update_next_frame(struct ffmpeg_context_s * ffmpeg_ctx)
{
int ret = 0;
while(1) {
/* read frames from the file */
if(av_read_frame(ffmpeg_ctx->fmt_ctx, &(ffmpeg_ctx->pkt)) >= 0) {
bool is_image = false;
/* check if the packet belongs to a stream we are interested in,
* otherwise skip it
*/
if(ffmpeg_ctx->pkt.stream_index == ffmpeg_ctx->video_stream_idx) {
ret = ffmpeg_decode_packet(ffmpeg_ctx->video_dec_ctx,
&(ffmpeg_ctx->pkt), ffmpeg_ctx);
is_image = true;
}
av_packet_unref(&(ffmpeg_ctx->pkt));
if(ret < 0) {
LV_LOG_WARN("video frame is empty %d", ret);
break;
}
/* Used to filter data that is not an image */
if(is_image) {
break;
}
}
else {
ret = -1;
break;
}
}
return ret;
}
struct ffmpeg_context_s * ffmpeg_open_file(const char * path)
{
if(path == NULL || strlen(path) == 0) {
LV_LOG_ERROR("file path is empty");
return NULL;
}
struct ffmpeg_context_s * ffmpeg_ctx = calloc(1, sizeof(struct ffmpeg_context_s));
if(ffmpeg_ctx == NULL) {
LV_LOG_ERROR("ffmpeg_ctx malloc failed");
goto failed;
}
/* open input file, and allocate format context */
if(avformat_open_input(&(ffmpeg_ctx->fmt_ctx), path, NULL, NULL) < 0) {
LV_LOG_ERROR("Could not open source file %s", path);
goto failed;
}
/* retrieve stream information */
if(avformat_find_stream_info(ffmpeg_ctx->fmt_ctx, NULL) < 0) {
LV_LOG_ERROR("Could not find stream information");
goto failed;
}
if(ffmpeg_open_codec_context(
&(ffmpeg_ctx->video_stream_idx),
&(ffmpeg_ctx->video_dec_ctx),
ffmpeg_ctx->fmt_ctx, AVMEDIA_TYPE_VIDEO)
>= 0) {
ffmpeg_ctx->video_stream = ffmpeg_ctx->fmt_ctx->streams[ffmpeg_ctx->video_stream_idx];
ffmpeg_ctx->has_alpha = ffmpeg_pix_fmt_has_alpha(ffmpeg_ctx->video_dec_ctx->pix_fmt);
ffmpeg_ctx->video_dst_pix_fmt = (ffmpeg_ctx->has_alpha ? AV_PIX_FMT_BGRA : AV_PIX_FMT_TRUE_COLOR);
}
#if LV_FFMPEG_AV_DUMP_FORMAT != 0
/* dump input information to stderr */
av_dump_format(ffmpeg_ctx->fmt_ctx, 0, path, 0);
#endif
if(ffmpeg_ctx->video_stream == NULL) {
LV_LOG_ERROR("Could not find video stream in the input, aborting");
goto failed;
}
return ffmpeg_ctx;
failed:
ffmpeg_close(ffmpeg_ctx);
return NULL;
}
static int ffmpeg_image_allocate(struct ffmpeg_context_s * ffmpeg_ctx)
{
int ret;
/* allocate image where the decoded image will be put */
ret = av_image_alloc(
ffmpeg_ctx->video_src_data,
ffmpeg_ctx->video_src_linesize,
ffmpeg_ctx->video_dec_ctx->width,
ffmpeg_ctx->video_dec_ctx->height,
ffmpeg_ctx->video_dec_ctx->pix_fmt,
4);
if(ret < 0) {
LV_LOG_ERROR("Could not allocate src raw video buffer");
return ret;
}
LV_LOG_INFO("alloc video_src_bufsize = %d", ret);
ret = av_image_alloc(
ffmpeg_ctx->video_dst_data,
ffmpeg_ctx->video_dst_linesize,
ffmpeg_ctx->video_dec_ctx->width,
ffmpeg_ctx->video_dec_ctx->height,
ffmpeg_ctx->video_dst_pix_fmt,
4);
if(ret < 0) {
LV_LOG_ERROR("Could not allocate dst raw video buffer");
return ret;
}
LV_LOG_INFO("allocate video_dst_bufsize = %d", ret);
ffmpeg_ctx->frame = av_frame_alloc();
if(ffmpeg_ctx->frame == NULL) {
LV_LOG_ERROR("Could not allocate frame");
return -1;
}
/* initialize packet, set data to NULL, let the demuxer fill it */
av_init_packet(&ffmpeg_ctx->pkt);
ffmpeg_ctx->pkt.data = NULL;
ffmpeg_ctx->pkt.size = 0;
return 0;
}
static void ffmpeg_close_src_ctx(struct ffmpeg_context_s * ffmpeg_ctx)
{
avcodec_free_context(&(ffmpeg_ctx->video_dec_ctx));
avformat_close_input(&(ffmpeg_ctx->fmt_ctx));
av_frame_free(&(ffmpeg_ctx->frame));
if(ffmpeg_ctx->video_src_data[0] != NULL) {
av_free(ffmpeg_ctx->video_src_data[0]);
ffmpeg_ctx->video_src_data[0] = NULL;
}
}
static void ffmpeg_close_dst_ctx(struct ffmpeg_context_s * ffmpeg_ctx)
{
if(ffmpeg_ctx->video_dst_data[0] != NULL) {
av_free(ffmpeg_ctx->video_dst_data[0]);
ffmpeg_ctx->video_dst_data[0] = NULL;
}
}
static void ffmpeg_close(struct ffmpeg_context_s * ffmpeg_ctx)
{
if(ffmpeg_ctx == NULL) {
LV_LOG_WARN("ffmpeg_ctx is NULL");
return;
}
sws_freeContext(ffmpeg_ctx->sws_ctx);
ffmpeg_close_src_ctx(ffmpeg_ctx);
ffmpeg_close_dst_ctx(ffmpeg_ctx);
free(ffmpeg_ctx);
LV_LOG_INFO("ffmpeg_ctx closed");
}
static void lv_ffmpeg_player_frame_update_cb(lv_timer_t * timer)
{
lv_obj_t * obj = (lv_obj_t *)timer->user_data;
lv_ffmpeg_player_t * player = (lv_ffmpeg_player_t *)obj;
if(!player->ffmpeg_ctx) {
return;
}
int has_next = ffmpeg_update_next_frame(player->ffmpeg_ctx);
if(has_next < 0) {
lv_ffmpeg_player_set_cmd(obj, player->auto_restart ? LV_FFMPEG_PLAYER_CMD_START : LV_FFMPEG_PLAYER_CMD_STOP);
return;
}
#if LV_COLOR_DEPTH != 32
if(player->ffmpeg_ctx->has_alpha) {
convert_color_depth((uint8_t *)(player->imgdsc.data),
player->imgdsc.header.w * player->imgdsc.header.h);
}
#endif
lv_img_cache_invalidate_src(lv_img_get_src(obj));
lv_obj_invalidate(obj);
}
static void lv_ffmpeg_player_constructor(const lv_obj_class_t * class_p,
lv_obj_t * obj)
{
LV_TRACE_OBJ_CREATE("begin");
lv_ffmpeg_player_t * player = (lv_ffmpeg_player_t *)obj;
player->auto_restart = false;
player->ffmpeg_ctx = NULL;
player->timer = lv_timer_create(lv_ffmpeg_player_frame_update_cb,
FRAME_DEF_REFR_PERIOD, obj);
lv_timer_pause(player->timer);
LV_TRACE_OBJ_CREATE("finished");
}
static void lv_ffmpeg_player_destructor(const lv_obj_class_t * class_p,
lv_obj_t * obj)
{
LV_TRACE_OBJ_CREATE("begin");
lv_ffmpeg_player_t * player = (lv_ffmpeg_player_t *)obj;
if(player->timer) {
lv_timer_del(player->timer);
player->timer = NULL;
}
lv_img_cache_invalidate_src(lv_img_get_src(obj));
ffmpeg_close(player->ffmpeg_ctx);
player->ffmpeg_ctx = NULL;
LV_TRACE_OBJ_CREATE("finished");
}
#endif /*LV_USE_FFMPEG*/

View File

@@ -0,0 +1,104 @@
/**
* @file lv_ffmpeg.h
*
*/
#ifndef LV_FFMPEG_H
#define LV_FFMPEG_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../lvgl.h"
#if LV_USE_FFMPEG != 0
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
struct ffmpeg_context_s;
extern const lv_obj_class_t lv_ffmpeg_player_class;
typedef struct {
lv_img_t img;
lv_timer_t * timer;
lv_img_dsc_t imgdsc;
bool auto_restart;
struct ffmpeg_context_s * ffmpeg_ctx;
} lv_ffmpeg_player_t;
typedef enum {
LV_FFMPEG_PLAYER_CMD_START,
LV_FFMPEG_PLAYER_CMD_STOP,
LV_FFMPEG_PLAYER_CMD_PAUSE,
LV_FFMPEG_PLAYER_CMD_RESUME,
_LV_FFMPEG_PLAYER_CMD_LAST
} lv_ffmpeg_player_cmd_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Register FFMPEG image decoder
*/
void lv_ffmpeg_init(void);
/**
* Get the number of frames contained in the file
* @param path image or video file name
* @return Number of frames, less than 0 means failed
*/
int lv_ffmpeg_get_frame_num(const char * path);
/**
* Create ffmpeg_player object
* @param parent pointer to an object, it will be the parent of the new player
* @return pointer to the created ffmpeg_player
*/
lv_obj_t * lv_ffmpeg_player_create(lv_obj_t * parent);
/**
* Set the path of the file to be played
* @param obj pointer to a ffmpeg_player object
* @param path video file path
* @return LV_RES_OK: no error; LV_RES_INV: can't get the info.
*/
lv_res_t lv_ffmpeg_player_set_src(lv_obj_t * obj, const char * path);
/**
* Set command control video player
* @param obj pointer to a ffmpeg_player object
* @param cmd control commands
*/
void lv_ffmpeg_player_set_cmd(lv_obj_t * obj, lv_ffmpeg_player_cmd_t cmd);
/**
* Set the video to automatically replay
* @param obj pointer to a ffmpeg_player object
* @param en true: enable the auto restart
*/
void lv_ffmpeg_player_set_auto_restart(lv_obj_t * obj, bool en);
/*=====================
* Other functions
*====================*/
/**********************
* MACROS
**********************/
#endif /*LV_USE_FFMPEG*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_FFMPEG_H*/

View File

@@ -0,0 +1,687 @@
/**
* @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
#include FT_IMAGE_H
#include FT_OUTLINE_H
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_ll_t face_ll;
} lv_faces_control_t;
typedef struct name_refer_t {
const char * name; /* point to font name string */
int32_t cnt; /* reference count */
} name_refer_t;
typedef struct {
const void * mem;
const char * name;
size_t mem_size;
#if LV_FREETYPE_CACHE_SIZE < 0
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);
#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 const char * name_refer_save(const char * name);
static void name_refer_del(const char * name);
static const char * name_refer_find(const char * name);
/**********************
* STATIC VARIABLES
**********************/
static FT_Library library;
static lv_ll_t names_ll;
#if LV_FREETYPE_CACHE_SIZE >= 0
static FTC_Manager cache_manager;
static FTC_CMapCache cmap_cache;
static FT_Face current_face = NULL;
#if LV_FREETYPE_SBIT_CACHE
static FTC_SBitCache sbit_cache;
static FTC_SBit sbit;
#else
static FTC_ImageCache image_cache;
static FT_Glyph image_glyph;
#endif
#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;
}
_lv_ll_init(&names_ll, sizeof(name_refer_t));
#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;
}
#if LV_FREETYPE_SBIT_CACHE
error = FTC_SBitCache_New(cache_manager, &sbit_cache);
if(error) {
LV_LOG_ERROR("Failed to open sbit cache");
goto Fail;
}
#else
error = FTC_ImageCache_New(cache_manager, &image_cache);
if(error) {
LV_LOG_ERROR("Failed to open image cache");
goto Fail;
}
#endif
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_font_fmt_ft_dsc_t * dsc = (lv_font_fmt_ft_dsc_t *)face_id;
FT_Error error;
if(dsc->mem) {
error = FT_New_Memory_Face(library, dsc->mem, dsc->mem_size, 0, aface);
}
else {
error = FT_New_Face(library, dsc->name, 0, aface);
}
if(error) {
LV_LOG_ERROR("FT_New_Face error:%d\n", error);
return error;
}
return FT_Err_Ok;
}
static bool get_bold_glyph(const lv_font_t * font, FT_Face face,
FT_UInt glyph_index, lv_font_glyph_dsc_t * dsc_out)
{
if(FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT)) {
return false;
}
lv_font_fmt_ft_dsc_t * dsc = (lv_font_fmt_ft_dsc_t *)(font->dsc);
if(face->glyph->format == FT_GLYPH_FORMAT_OUTLINE) {
if(dsc->style & FT_FONT_STYLE_BOLD) {
int strength = 1 << 6;
FT_Outline_Embolden(&face->glyph->outline, strength);
}
}
if(FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL)) {
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 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);
FTC_FaceID face_id = (FTC_FaceID)dsc;
FT_Size face_size;
struct FTC_ScalerRec_ scaler;
scaler.face_id = face_id;
scaler.width = dsc->height;
scaler.height = dsc->height;
scaler.pixel = 1;
if(FTC_Manager_LookupSize(cache_manager, &scaler, &face_size) != 0) {
return false;
}
FT_Face face = face_size->face;
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);
dsc_out->is_placeholder = glyph_index == 0;
if(dsc->style & FT_FONT_STYLE_ITALIC) {
FT_Matrix italic_matrix;
italic_matrix.xx = 1 << 16;
italic_matrix.xy = 0x5800;
italic_matrix.yx = 0;
italic_matrix.yy = 1 << 16;
FT_Set_Transform(face, &italic_matrix, NULL);
}
if(dsc->style & FT_FONT_STYLE_BOLD) {
current_face = face;
if(!get_bold_glyph(font, face, glyph_index, dsc_out)) {
current_face = NULL;
return false;
}
goto end;
}
FTC_ImageTypeRec desc_type;
desc_type.face_id = face_id;
desc_type.flags = FT_LOAD_RENDER | FT_LOAD_TARGET_NORMAL;
desc_type.height = dsc->height;
desc_type.width = dsc->height;
#if LV_FREETYPE_SBIT_CACHE
FT_Error error = FTC_SBitCache_Lookup(sbit_cache, &desc_type, glyph_index, &sbit, NULL);
if(error) {
LV_LOG_ERROR("SBitCache_Lookup error");
return false;
}
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*/
#else
FT_Error error = FTC_ImageCache_Lookup(image_cache, &desc_type, glyph_index, &image_glyph, NULL);
if(error) {
LV_LOG_ERROR("ImageCache_Lookup error");
return false;
}
if(image_glyph->format != FT_GLYPH_FORMAT_BITMAP) {
LV_LOG_ERROR("Glyph_To_Bitmap error");
return false;
}
FT_BitmapGlyph glyph_bitmap = (FT_BitmapGlyph)image_glyph;
dsc_out->adv_w = (glyph_bitmap->root.advance.x >> 16);
dsc_out->box_h = glyph_bitmap->bitmap.rows; /*Height of the bitmap in [px]*/
dsc_out->box_w = glyph_bitmap->bitmap.width; /*Width of the bitmap in [px]*/
dsc_out->ofs_x = glyph_bitmap->left; /*X offset of the bitmap in [pf]*/
dsc_out->ofs_y = glyph_bitmap->top -
glyph_bitmap->bitmap.rows; /*Y offset of the bitmap measured from the as line*/
dsc_out->bpp = 8; /*Bit per pixel: 1/2/4/8*/
#endif
end:
if((dsc->style & FT_FONT_STYLE_ITALIC) && (unicode_letter_next == '\0')) {
dsc_out->adv_w = dsc_out->box_w + dsc_out->ofs_x;
}
return true;
}
static const uint8_t * get_glyph_bitmap_cb_cache(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);
if(dsc->style & FT_FONT_STYLE_BOLD) {
if(current_face && current_face->glyph->format == FT_GLYPH_FORMAT_BITMAP) {
return (const uint8_t *)(current_face->glyph->bitmap.buffer);
}
return NULL;
}
#if LV_FREETYPE_SBIT_CACHE
return (const uint8_t *)sbit->buffer;
#else
FT_BitmapGlyph glyph_bitmap = (FT_BitmapGlyph)image_glyph;
return (const uint8_t *)glyph_bitmap->bitmap.buffer;
#endif
}
static bool lv_ft_font_init_cache(lv_ft_info_t * info)
{
size_t need_size = sizeof(lv_font_fmt_ft_dsc_t) + sizeof(lv_font_t);
lv_font_fmt_ft_dsc_t * dsc = lv_mem_alloc(need_size);
if(dsc == NULL) return false;
lv_memset_00(dsc, need_size);
dsc->font = (lv_font_t *)(((char *)dsc) + sizeof(lv_font_fmt_ft_dsc_t));
dsc->mem = info->mem;
dsc->mem_size = info->mem_size;
dsc->name = name_refer_save(info->name);
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;
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_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);
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) {
FTC_Manager_RemoveFaceID(cache_manager, (FTC_FaceID)dsc);
name_refer_del(dsc->name);
lv_mem_free(dsc);
}
}
#else/* LV_FREETYPE_CACHE_SIZE */
static FT_Face face_find_in_list(lv_ft_info_t * info)
{
lv_font_fmt_ft_dsc_t * dsc;
FT_Face * pface = _lv_ll_get_head(&face_control.face_ll);
while(pface) {
dsc = (lv_font_fmt_ft_dsc_t *)(*pface)->generic.data;
if(strcmp(dsc->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);
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);
}
dsc_out->is_placeholder = glyph_index == 0;
error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT);
if(error) {
return false;
}
if(face->glyph->format == FT_GLYPH_FORMAT_OUTLINE) {
if(dsc->style & FT_FONT_STYLE_BOLD) {
int strength = 1 << 6;
FT_Outline_Embolden(&face->glyph->outline, strength);
}
if(dsc->style & FT_FONT_STYLE_ITALIC) {
FT_Matrix italic_matrix;
italic_matrix.xx = 1 << 16;
italic_matrix.xy = 0x5800;
italic_matrix.yx = 0;
italic_matrix.yy = 1 << 16;
FT_Outline_Transform(&face->glyph->outline, &italic_matrix);
}
}
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*/
if((dsc->style & FT_FONT_STYLE_ITALIC) && (unicode_letter_next == '\0')) {
dsc_out->adv_w = dsc_out->box_w + dsc_out->ofs_x;
}
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)
{
size_t need_size = sizeof(lv_font_fmt_ft_dsc_t) + sizeof(lv_font_t);
lv_font_fmt_ft_dsc_t * dsc = lv_mem_alloc(need_size);
if(dsc == NULL) return false;
lv_memset_00(dsc, need_size);
dsc->font = (lv_font_t *)(((char *)dsc) + sizeof(lv_font_fmt_ft_dsc_t));
dsc->mem = info->mem;
dsc->mem_size = info->mem_size;
dsc->name = name_refer_save(info->name);
dsc->height = info->weight;
dsc->style = info->style;
FT_Face face = face_find_in_list(info);
if(face == NULL) {
FT_Error error;
if(dsc->mem) {
error = FT_New_Memory_Face(library, dsc->mem, (FT_Long) dsc->mem_size, 0, &face);
}
else {
error = FT_New_Face(library, dsc->name, 0, &face);
}
if(error) {
LV_LOG_WARN("create face error(%d)", error);
goto Fail;
}
/* link face and face info */
face->generic.data = dsc;
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;
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);
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);
name_refer_del(dsc->name);
lv_mem_free(dsc);
}
}
#endif/* LV_FREETYPE_CACHE_SIZE */
/**
* find name string in names list.name string cnt += 1 if find.
* @param name name string
* @return the string pointer of name.
*/
static const char * name_refer_find(const char * name)
{
name_refer_t * refer = _lv_ll_get_head(&names_ll);
while(refer) {
if(strcmp(refer->name, name) == 0) {
refer->cnt += 1;
return refer->name;
}
refer = _lv_ll_get_next(&names_ll, refer);
}
return NULL;
}
/**
* del name string from list.
*/
static void name_refer_del(const char * name)
{
name_refer_t * refer = _lv_ll_get_head(&names_ll);
while(refer) {
if(strcmp(refer->name, name) == 0) {
refer->cnt -= 1;
if(refer->cnt <= 0) {
_lv_ll_remove(&names_ll, refer);
lv_mem_free((void *)refer->name);
lv_mem_free(refer);
}
return;
}
refer = _lv_ll_get_next(&names_ll, refer);
}
LV_LOG_WARN("name_in_names_del error(not find:%p).", name);
}
/**
* save name string to list.
* @param name name string
* @return Saved string pointer
*/
static const char * name_refer_save(const char * name)
{
const char * pos = name_refer_find(name);
if(pos) {
return pos;
}
name_refer_t * refer = _lv_ll_ins_tail(&names_ll);
if(refer) {
uint32_t len = strlen(name) + 1;
refer->name = lv_mem_alloc(len);
if(refer->name) {
lv_memcpy((void *)refer->name, name, len);
refer->cnt = 1;
return refer->name;
}
_lv_ll_remove(&names_ll, refer);
lv_mem_free(refer);
}
LV_LOG_WARN("save_name_to_names error(not memory).");
return "";
}
#endif /*LV_USE_FREETYPE*/

View File

@@ -0,0 +1,83 @@
/**
* @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 */
const void * mem; /* The pointer of the font file */
size_t mem_size; /* The size of the memory */
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,290 @@
/**
* @file lv_fs_fatfs.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../../lvgl.h"
#if LV_USE_FS_FATFS
#include "ff.h"
/*********************
* DEFINES
*********************/
#if LV_FS_FATFS_LETTER == '\0'
#error "LV_FS_FATFS_LETTER must be an upper case ASCII letter"
#endif
/**********************
* 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 LVGL
*--------------------------------------------------*/
/*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_FS_FATFS_LETTER;
fs_drv.cache_size = LV_FS_FATFS_CACHE_SIZE;
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 btw Bytes To Write
* @param bw 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((FIL *)file_p) + pos);
break;
case LV_FS_SEEK_END:
f_lseek(file_p, f_size((FIL *)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((FIL *)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;
}
#else /*LV_USE_FS_FATFS == 0*/
#if defined(LV_FS_FATFS_LETTER) && LV_FS_FATFS_LETTER != '\0'
#warning "LV_USE_FS_FATFS is not enabled but LV_FS_FATFS_LETTER is set"
#endif
#endif /*LV_USE_FS_POSIX*/

View File

@@ -0,0 +1,319 @@
/**
* @file lv_fs_posix.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../../lvgl.h"
#if LV_USE_FS_POSIX
#include <fcntl.h>
#include <stdio.h>
#ifndef WIN32
#include <dirent.h>
#include <unistd.h>
#else
#include <windows.h>
#endif
/*********************
* DEFINES
*********************/
#if LV_FS_POSIX_LETTER == '\0'
#error "LV_FS_POSIX_LETTER must be an upper case ASCII letter"
#endif
/**********************
* 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 LVGL
*--------------------------------------------------*/
/*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_FS_POSIX_LETTER;
fs_drv.cache_size = LV_FS_POSIX_CACHE_SIZE;
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;
/*Make the path relative to the current directory (the projects root folder)*/
char buf[256];
lv_snprintf(buf, sizeof(buf), LV_FS_POSIX_PATH "%s", path);
int f = open(buf, flags);
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 btw Bytes To Write
* @param bw 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);
off_t offset = lseek((lv_uintptr_t)file_p, pos, whence);
return offset < 0 ? LV_FS_RES_FS_ERR : 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);
off_t offset = lseek((lv_uintptr_t)file_p, 0, SEEK_CUR);
*pos_p = offset;
return offset < 0 ? LV_FS_RES_FS_ERR : 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
/*Make the path relative to the current directory (the projects root folder)*/
char buf[256];
lv_snprintf(buf, sizeof(buf), LV_FS_POSIX_PATH "%s", path);
return opendir(buf);
#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];
lv_snprintf(buf, sizeof(buf), LV_FS_POSIX_PATH "%s\\*", path);
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;
}
#else /*LV_USE_FS_POSIX == 0*/
#if defined(LV_FS_POSIX_LETTER) && LV_FS_POSIX_LETTER != '\0'
#warning "LV_USE_FS_POSIX is not enabled but LV_FS_POSIX_LETTER is set"
#endif
#endif /*LV_USE_FS_POSIX*/

View File

@@ -0,0 +1,329 @@
/**
* @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
*********************/
#define MAX_PATH_LEN 256
/**********************
* TYPEDEFS
**********************/
typedef struct {
#ifdef WIN32
HANDLE dir_p;
char next_fn[MAX_PATH_LEN];
#else
DIR * dir_p;
#endif
} dir_handle_t;
/**********************
* 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 LVGL
*--------------------------------------------------*/
/*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_FS_STDIO_LETTER;
fs_drv.cache_size = LV_FS_STDIO_CACHE_SIZE;
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+";
/*Make the path relative to the current directory (the projects root folder)*/
char buf[MAX_PATH_LEN];
lv_snprintf(buf, sizeof(buf), LV_FS_STDIO_PATH "%s", path);
return fopen(buf, flags);
}
/**
* 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 btw Bytes To Write
* @param bw 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;
}
/**
* 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);
dir_handle_t * handle = (dir_handle_t *)lv_mem_alloc(sizeof(dir_handle_t));
#ifndef WIN32
/*Make the path relative to the current directory (the projects root folder)*/
char buf[MAX_PATH_LEN];
lv_snprintf(buf, sizeof(buf), LV_FS_STDIO_PATH "%s", path);
handle->dir_p = opendir(buf);
if(handle->dir_p == NULL) {
lv_mem_free(handle);
return NULL;
}
return handle;
#else
handle->dir_p = INVALID_HANDLE_VALUE;
WIN32_FIND_DATAA fdata;
/*Make the path relative to the current directory (the projects root folder)*/
char buf[MAX_PATH_LEN];
lv_snprintf(buf, sizeof(buf), LV_FS_STDIO_PATH "%s\\*", path);
strcpy(handle->next_fn, "");
handle->dir_p = FindFirstFileA(buf, &fdata);
do {
if(strcmp(fdata.cFileName, ".") == 0 || strcmp(fdata.cFileName, "..") == 0) {
continue;
}
else {
if(fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
lv_snprintf(handle->next_fn, sizeof(handle->next_fn), "/%s", fdata.cFileName);
}
else {
lv_snprintf(handle->next_fn, sizeof(handle->next_fn), "%s", fdata.cFileName);
}
break;
}
} while(FindNextFileA(handle->dir_p, &fdata));
if(handle->dir_p == INVALID_HANDLE_VALUE) {
lv_mem_free(handle);
return INVALID_HANDLE_VALUE;
}
return handle;
#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);
dir_handle_t * handle = (dir_handle_t *)dir_p;
#ifndef WIN32
struct dirent * entry;
do {
entry = readdir(handle->dir_p);
if(entry) {
if(entry->d_type == DT_DIR) lv_snprintf(fn, MAX_PATH_LEN, "/%s", entry->d_name);
else strcpy(fn, entry->d_name);
}
else {
strcpy(fn, "");
}
} while(strcmp(fn, "/.") == 0 || strcmp(fn, "/..") == 0);
#else
strcpy(fn, handle->next_fn);
strcpy(handle->next_fn, "");
WIN32_FIND_DATAA fdata;
if(FindNextFileA(handle->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) {
lv_snprintf(handle->next_fn, sizeof(handle->next_fn), "/%s", fdata.cFileName);
}
else {
lv_snprintf(handle->next_fn, sizeof(handle->next_fn), "%s", fdata.cFileName);
}
break;
}
} while(FindNextFileA(handle->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);
dir_handle_t * handle = (dir_handle_t *)dir_p;
#ifndef WIN32
closedir(handle->dir_p);
#else
FindClose(handle->dir_p);
#endif
lv_mem_free(handle);
return LV_FS_RES_OK;
}
#else /*LV_USE_FS_STDIO == 0*/
#if defined(LV_FS_STDIO_LETTER) && LV_FS_STDIO_LETTER != '\0'
#warning "LV_USE_FS_STDIO is not enabled but LV_FS_STDIO_LETTER is set"
#endif
#endif /*LV_USE_FS_POSIX*/

View File

@@ -0,0 +1,466 @@
/**
* @file lv_fs_win32.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../../lvgl.h"
#if LV_USE_FS_WIN32 != '\0'
#include <windows.h>
#include <stdio.h>
/*********************
* DEFINES
*********************/
#define MAX_PATH_LEN 256
/**********************
* TYPEDEFS
**********************/
typedef struct {
HANDLE dir_p;
char next_fn[MAX_PATH_LEN];
lv_fs_res_t next_error;
} dir_handle_t;
/**********************
* 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 LVGL
*--------------------------------------------------*/
/*Add a simple driver 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_FS_WIN32_LETTER;
fs_drv.cache_size = LV_FS_WIN32_CACHE_SIZE;
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;
}
/*Make the path relative to the current directory (the projects root folder)*/
char buf[MAX_PATH];
lv_snprintf(buf, sizeof(buf), LV_FS_WIN32_PATH "%s", path);
return (void *)CreateFileA(
buf,
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 btw Bytes To Write
* @param bw 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());
}
}
/**
* 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);
dir_handle_t * handle = (dir_handle_t *)lv_mem_alloc(sizeof(dir_handle_t));
handle->dir_p = INVALID_HANDLE_VALUE;
handle->next_error = LV_FS_RES_OK;
WIN32_FIND_DATAA fdata;
/*Make the path relative to the current directory (the projects root folder)*/
char buf[MAX_PATH_LEN];
#ifdef LV_FS_WIN32_PATH
lv_snprintf(buf, sizeof(buf), LV_FS_WIN32_PATH "%s\\*", path);
#else
lv_snprintf(buf, sizeof(buf), "%s\\*", path);
#endif
strcpy(handle->next_fn, "");
handle->dir_p = FindFirstFileA(buf, &fdata);
do {
if(is_dots_name(fdata.cFileName)) {
continue;
}
else {
if(fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
lv_snprintf(handle->next_fn, sizeof(handle->next_fn), "/%s", fdata.cFileName);
}
else {
lv_snprintf(handle->next_fn, sizeof(handle->next_fn), "%s", fdata.cFileName);
}
break;
}
} while(FindNextFileA(handle->dir_p, &fdata));
if(handle->dir_p == INVALID_HANDLE_VALUE) {
lv_mem_free(handle);
handle->next_error = fs_error_from_win32(GetLastError());
return INVALID_HANDLE_VALUE;
}
else {
handle->next_error = LV_FS_RES_OK;
return handle;
}
}
/**
* 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);
dir_handle_t * handle = (dir_handle_t *)dir_p;
strcpy(fn, handle->next_fn);
lv_fs_res_t current_error = handle->next_error;
strcpy(handle->next_fn, "");
WIN32_FIND_DATAA fdata;
while(FindNextFileA(handle->dir_p, &fdata)) {
if(is_dots_name(fdata.cFileName)) {
continue;
}
else {
if(fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
lv_snprintf(handle->next_fn, sizeof(handle->next_fn), "/%s", fdata.cFileName);
}
else {
lv_snprintf(handle->next_fn, sizeof(handle->next_fn), "%s", fdata.cFileName);
}
break;
}
}
if(handle->next_fn[0] == '\0') {
handle->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);
dir_handle_t * handle = (dir_handle_t *)dir_p;
lv_fs_res_t res = FindClose(handle->dir_p)
? LV_FS_RES_OK
: fs_error_from_win32(GetLastError());
lv_mem_free(handle);
return res;
}
#else /*LV_USE_FS_WIN32 == 0*/
#if defined(LV_FS_WIN32_LETTER) && LV_FS_WIN32_LETTER != '\0'
#warning "LV_USE_FS_WIN32 is not enabled but LV_FS_WIN32_LETTER is set"
#endif
#endif

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,659 @@
#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 = NULL;
/* 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];
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;
#elif LV_COLOR_DEPTH == 1
lv_color_t c = lv_color_make(*(bgcolor + 0), *(bgcolor + 1), *(bgcolor + 2));
gif->canvas[i*2 + 0] = c.ch.red > 128 ? 1 : 0;
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,155 @@
/**
* @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,46 @@
/**
* @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"
#include "ffmpeg/lv_ffmpeg.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_LIBS_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,258 @@
/**
* @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 LVGL
*/
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(lv_fs_get_ext(fn), "png") == 0) { /*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);
lv_fs_close(&f);
if(rn != 8) return LV_RES_INV;
/*Save the data in the header*/
header->always_zero = 0;
header->cf = LV_IMG_CF_TRUE_COLOR_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;
const uint32_t data_size = img_dsc->data_size;
const uint8_t magic[] = {0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a};
if(data_size < sizeof(magic)) return LV_RES_INV;
if(memcmp(magic, img_dsc->data, sizeof(magic))) return LV_RES_INV;
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(lv_fs_get_ext(fn), "png") == 0) { /*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) {
if(img_data != NULL) {
lv_mem_free(img_data);
}
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) {
if(img_data != NULL) {
lv_mem_free(img_data);
}
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 LVGL
*/
void lv_png_init(void);
/**********************
* MACROS
**********************/
#endif /*LV_USE_PNG*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_PNG_H*/

View File

@@ -0,0 +1,215 @@
/**
* @file lv_qrcode.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_qrcode.h"
#if LV_USE_QRCODE
#include "qrcodegen.h"
/*********************
* DEFINES
*********************/
#define MY_CLASS &lv_qrcode_class
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_qrcode_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_qrcode_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
/**********************
* STATIC VARIABLES
**********************/
const lv_obj_class_t lv_qrcode_class = {
.constructor_cb = lv_qrcode_constructor,
.destructor_cb = lv_qrcode_destructor,
.base_class = &lv_canvas_class
};
static lv_coord_t size_param;
static lv_color_t dark_color_param;
static lv_color_t light_color_param;
/**********************
* 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)
{
LV_LOG_INFO("begin");
size_param = size;
light_color_param = light_color;
dark_color_param = dark_color;
lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
lv_obj_class_init_obj(obj);
return obj;
}
/**
* 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;
lv_img_dsc_t * imgdsc = lv_canvas_get_img(qrcode);
int32_t qr_version = qrcodegen_getMinFitVersion(qrcodegen_Ecc_MEDIUM, data_len);
if(qr_version <= 0) return LV_RES_INV;
int32_t qr_size = qrcodegen_version2size(qr_version);
if(qr_size <= 0) return LV_RES_INV;
int32_t scale = imgdsc->header.w / qr_size;
if(scale <= 0) return LV_RES_INV;
int32_t remain = imgdsc->header.w % qr_size;
/* The qr version is incremented by four point */
uint32_t version_extend = remain / (scale << 2);
if(version_extend && qr_version < qrcodegen_VERSION_MAX) {
qr_version = qr_version + version_extend > qrcodegen_VERSION_MAX ?
qrcodegen_VERSION_MAX : qr_version + version_extend;
}
uint8_t * qr0 = lv_mem_alloc(qrcodegen_BUFFER_LEN_FOR_VERSION(qr_version));
LV_ASSERT_MALLOC(qr0);
uint8_t * data_tmp = lv_mem_alloc(qrcodegen_BUFFER_LEN_FOR_VERSION(qr_version));
LV_ASSERT_MALLOC(data_tmp);
lv_memcpy(data_tmp, data, data_len);
bool ok = qrcodegen_encodeBinary(data_tmp, data_len,
qr0, qrcodegen_Ecc_MEDIUM,
qr_version, qr_version,
qrcodegen_Mask_AUTO, true);
if(!ok) {
lv_mem_free(qr0);
lv_mem_free(data_tmp);
return LV_RES_INV;
}
lv_coord_t obj_w = imgdsc->header.w;
qr_size = qrcodegen_getSize(qr0);
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++) {
lv_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;
}
void lv_qrcode_delete(lv_obj_t * qrcode)
{
lv_obj_del(qrcode);
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_qrcode_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
uint32_t buf_size = LV_CANVAS_BUF_SIZE_INDEXED_1BIT(size_param, size_param);
uint8_t * buf = lv_mem_alloc(buf_size);
LV_ASSERT_MALLOC(buf);
if(buf == NULL) return;
lv_canvas_set_buffer(obj, buf, size_param, size_param, LV_IMG_CF_INDEXED_1BIT);
lv_canvas_set_palette(obj, 0, dark_color_param);
lv_canvas_set_palette(obj, 1, light_color_param);
}
static void lv_qrcode_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
lv_img_dsc_t * img = lv_canvas_get_img(obj);
lv_img_cache_invalidate_src(img);
lv_mem_free((void *)img->data);
img->data = NULL;
}
#endif /*LV_USE_QRCODE*/

View File

@@ -0,0 +1,69 @@
/**
* @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);
/**
* DEPRECATED: Use normal lv_obj_del instead
* 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,319 @@
/*
* 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);
/*
* Returns the qrcode size of the specified version. Returns -1 on failure
*/
int qrcodegen_version2size(int version);
/*
* Returns the min version of the data that can be stored. Returns -1 on failure
*/
int qrcodegen_getMinFitVersion(enum qrcodegen_Ecc ecl, size_t dataLen);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,284 @@
/**
* @file lv_rlottie.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_rlottie.h"
#if LV_USE_RLOTTIE
#include <rlottie_capi.h>
/*********************
* DEFINES
*********************/
#define MY_CLASS &lv_rlottie_class
#define LV_ARGB32 32
/**********************
* TYPEDEFS
**********************/
#define LV_ARGB32 32
/**********************
* 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;
}
void lv_rlottie_set_play_mode(lv_obj_t * obj, const lv_rlottie_ctrl_t ctrl)
{
lv_rlottie_t * rlottie = (lv_rlottie_t *) obj;
rlottie->play_ctrl = ctrl;
if(rlottie->task && (rlottie->dest_frame != rlottie->current_frame ||
(rlottie->play_ctrl & LV_RLOTTIE_CTRL_PAUSE) == LV_RLOTTIE_CTRL_PLAY)) {
lv_timer_resume(rlottie->task);
}
}
void lv_rlottie_set_current_frame(lv_obj_t * obj, const size_t goto_frame)
{
lv_rlottie_t * rlottie = (lv_rlottie_t *) obj;
rlottie->current_frame = goto_frame < rlottie->total_frames ? goto_frame : rlottie->total_frames - 1;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_rlottie_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
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_ARGB32 / 8;
size_t allocaled_buf_size = (create_width * create_height * LV_ARGB32 / 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->play_ctrl = LV_RLOTTIE_CTRL_FORWARD | LV_RLOTTIE_CTRL_PLAY | LV_RLOTTIE_CTRL_LOOP;
rlottie->dest_frame = rlottie->total_frames; /* invalid destination frame so it's possible to pause on frame 0 */
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_UNUSED(class_p);
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;
rlottie->play_ctrl = LV_RLOTTIE_CTRL_FORWARD;
rlottie->dest_frame = 0;
}
lv_img_cache_invalidate_src(&rlottie->imgdsc);
if(rlottie->allocated_buf) {
lv_mem_free(rlottie->allocated_buf);
rlottie->allocated_buf = NULL;
rlottie->allocated_buffer_size = 0;
}
}
#if LV_COLOR_DEPTH == 16
static void convert_to_rgba5658(uint32_t * pix, const size_t width, const size_t height)
{
/* rlottie draws in ARGB32 format, but LVGL only deal with RGB565 format with (optional 8 bit alpha channel)
so convert in place here the received buffer to LVGL format. */
uint8_t * dest = (uint8_t *)pix;
uint32_t * src = pix;
for(size_t y = 0; y < height; y++) {
/* Convert a 4 bytes per pixel in format ARGB to R5G6B5A8 format
naive way:
r = ((c & 0xFF0000) >> 19)
g = ((c & 0xFF00) >> 10)
b = ((c & 0xFF) >> 3)
rgb565 = (r << 11) | (g << 5) | b
a = c >> 24;
That's 3 mask, 6 bitshift and 2 or operations
A bit better:
r = ((c & 0xF80000) >> 8)
g = ((c & 0xFC00) >> 5)
b = ((c & 0xFF) >> 3)
rgb565 = r | g | b
a = c >> 24;
That's 3 mask, 3 bitshifts and 2 or operations */
for(size_t x = 0; x < width; x++) {
uint32_t in = src[x];
#if LV_COLOR_16_SWAP == 0
uint16_t r = (uint16_t)(((in & 0xF80000) >> 8) | ((in & 0xFC00) >> 5) | ((in & 0xFF) >> 3));
#else
/* We want: rrrr rrrr GGGg gggg bbbb bbbb => gggb bbbb rrrr rGGG */
uint16_t r = (uint16_t)(((in & 0xF80000) >> 16) | ((in & 0xFC00) >> 13) | ((in & 0x1C00) << 3) | ((in & 0xF8) << 5));
#endif
lv_memcpy(dest, &r, sizeof(r));
dest[sizeof(r)] = (uint8_t)(in >> 24);
dest += LV_IMG_PX_SIZE_ALPHA_BYTE;
}
src += width;
}
}
#endif
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->play_ctrl & LV_RLOTTIE_CTRL_PAUSE) == LV_RLOTTIE_CTRL_PAUSE) {
if(rlottie->current_frame == rlottie->dest_frame) {
/* Pause the timer too when it has run once to avoid CPU consumption */
lv_timer_pause(t);
return;
}
rlottie->dest_frame = rlottie->current_frame;
}
else {
if((rlottie->play_ctrl & LV_RLOTTIE_CTRL_BACKWARD) == LV_RLOTTIE_CTRL_BACKWARD) {
if(rlottie->current_frame > 0)
--rlottie->current_frame;
else { /* Looping ? */
if((rlottie->play_ctrl & LV_RLOTTIE_CTRL_LOOP) == LV_RLOTTIE_CTRL_LOOP)
rlottie->current_frame = rlottie->total_frames - 1;
else {
lv_event_send(obj, LV_EVENT_READY, NULL);
lv_timer_pause(t);
return;
}
}
}
else {
if(rlottie->current_frame < rlottie->total_frames)
++rlottie->current_frame;
else { /* Looping ? */
if((rlottie->play_ctrl & LV_RLOTTIE_CTRL_LOOP) == LV_RLOTTIE_CTRL_LOOP)
rlottie->current_frame = 0;
else {
lv_event_send(obj, LV_EVENT_READY, NULL);
lv_timer_pause(t);
return;
}
}
}
}
lottie_animation_render(
rlottie->animation,
rlottie->current_frame,
rlottie->allocated_buf,
rlottie->imgdsc.header.w,
rlottie->imgdsc.header.h,
rlottie->scanline_width
);
#if LV_COLOR_DEPTH == 16
convert_to_rgba5658(rlottie->allocated_buf, rlottie->imgdsc.header.w, rlottie->imgdsc.header.h);
#endif
lv_obj_invalidate(obj);
}
#endif /*LV_USE_RLOTTIE*/

View File

@@ -0,0 +1,75 @@
/**
* @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
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef enum {
LV_RLOTTIE_CTRL_FORWARD = 0,
LV_RLOTTIE_CTRL_BACKWARD = 1,
LV_RLOTTIE_CTRL_PAUSE = 2,
LV_RLOTTIE_CTRL_PLAY = 0, /* Yes, play = 0 is the default mode */
LV_RLOTTIE_CTRL_LOOP = 8,
} lv_rlottie_ctrl_t;
/** definition in lottieanimation_capi.c */
struct Lottie_Animation_S;
typedef struct {
lv_img_t img_ext;
struct Lottie_Animation_S * 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_ctrl_t play_ctrl;
size_t dest_frame;
} 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);
void lv_rlottie_set_play_mode(lv_obj_t * rlottie, const lv_rlottie_ctrl_t ctrl);
void lv_rlottie_set_current_frame(lv_obj_t * rlottie, const size_t goto_frame);
/**********************
* MACROS
**********************/
#endif /*LV_USE_RLOTTIE*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /*LV_RLOTTIE_H*/

View File

@@ -0,0 +1,917 @@
/**
* @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, size_t len);
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) {
const lv_img_dsc_t * img_dsc = src;
uint8_t * raw_sjpeg_data = (uint8_t *)img_dsc->data;
const uint32_t raw_sjpeg_data_size = img_dsc->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, raw_sjpeg_data_size) == 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(lv_fs_get_ext(fn), "sjpg") == 0) {
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(lv_fs_get_ext(fn), "jpg") == 0) {
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;
const uint32_t raw_sjpeg_data_size = ((lv_img_dsc_t *)dsc->src)->data_size;
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, raw_sjpeg_data_size) == 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(lv_fs_get_ext(fn), "sjpg") == 0) {
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(lv_fs_get_ext(fn), "jpg") == 0) {
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 || LV_COLOR_16_SWAP == 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 || LV_COLOR_16_SWAP == 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, size_t len)
{
const uint8_t jpg_signature[] = {0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46};
if(len < sizeof(jpg_signature)) return false;
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,93 @@
/**
* @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_MSG
lv_msg_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_FFMPEG
lv_ffmpeg_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*/
# if LV_FREETYPE_CACHE_SIZE >= 0
lv_freetype_init(LV_FREETYPE_CACHE_FT_FACES, LV_FREETYPE_CACHE_FT_SIZES, LV_FREETYPE_CACHE_SIZE);
# else
lv_freetype_init(0, 0, 0);
# endif
#endif
}
/**********************
* STATIC FUNCTIONS
**********************/

View File

@@ -0,0 +1,48 @@
/**
* @file lv_extra.h
*
*/
#ifndef LV_EXTRA_H
#define LV_EXTRA_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "layouts/lv_layouts.h"
#include "libs/lv_libs.h"
#include "others/lv_others.h"
#include "themes/lv_themes.h"
#include "widgets/lv_widgets.h"
/*********************
* 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 @@
CSRCS += $(shell find -L $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/extra -name \*.c)

View File

@@ -0,0 +1,144 @@
/**
* @file lv_fragment.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_fragment.h"
#if LV_USE_FRAGMENT
/**********************
* STATIC PROTOTYPES
**********************/
static void cb_delete_assertion(lv_event_t * event);
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_fragment_t * lv_fragment_create(const lv_fragment_class_t * cls, void * args)
{
LV_ASSERT_NULL(cls);
LV_ASSERT_NULL(cls->create_obj_cb);
LV_ASSERT(cls->instance_size > 0);
lv_fragment_t * instance = lv_mem_alloc(cls->instance_size);
lv_memset_00(instance, cls->instance_size);
instance->cls = cls;
instance->child_manager = lv_fragment_manager_create(instance);
if(cls->constructor_cb) {
cls->constructor_cb(instance, args);
}
return instance;
}
void lv_fragment_del(lv_fragment_t * fragment)
{
LV_ASSERT_NULL(fragment);
if(fragment->managed) {
lv_fragment_manager_remove(fragment->managed->manager, fragment);
return;
}
if(fragment->obj) {
lv_fragment_del_obj(fragment);
}
/* Objects will leak if this function called before objects deleted */
const lv_fragment_class_t * cls = fragment->cls;
if(cls->destructor_cb) {
cls->destructor_cb(fragment);
}
lv_fragment_manager_del(fragment->child_manager);
lv_mem_free(fragment);
}
lv_fragment_manager_t * lv_fragment_get_manager(lv_fragment_t * fragment)
{
LV_ASSERT_NULL(fragment);
LV_ASSERT_NULL(fragment->managed);
return fragment->managed->manager;
}
lv_obj_t * const * lv_fragment_get_container(lv_fragment_t * fragment)
{
LV_ASSERT_NULL(fragment);
LV_ASSERT_NULL(fragment->managed);
return fragment->managed->container;
}
lv_fragment_t * lv_fragment_get_parent(lv_fragment_t * fragment)
{
LV_ASSERT_NULL(fragment);
LV_ASSERT_NULL(fragment->managed);
return lv_fragment_manager_get_parent_fragment(fragment->managed->manager);
}
lv_obj_t * lv_fragment_create_obj(lv_fragment_t * fragment, lv_obj_t * container)
{
lv_fragment_managed_states_t * states = fragment->managed;
if(states) {
states->destroying_obj = false;
}
const lv_fragment_class_t * cls = fragment->cls;
lv_obj_t * obj = cls->create_obj_cb(fragment, container);
LV_ASSERT_NULL(obj);
fragment->obj = obj;
lv_fragment_manager_create_obj(fragment->child_manager);
if(states) {
states->obj_created = true;
lv_obj_add_event_cb(obj, cb_delete_assertion, LV_EVENT_DELETE, NULL);
}
if(cls->obj_created_cb) {
cls->obj_created_cb(fragment, obj);
}
return obj;
}
void lv_fragment_del_obj(lv_fragment_t * fragment)
{
LV_ASSERT_NULL(fragment);
lv_fragment_manager_del_obj(fragment->child_manager);
lv_fragment_managed_states_t * states = fragment->managed;
if(states) {
if(!states->obj_created) return;
states->destroying_obj = true;
bool cb_removed = lv_obj_remove_event_cb(fragment->obj, cb_delete_assertion);
LV_ASSERT(cb_removed);
}
LV_ASSERT_NULL(fragment->obj);
const lv_fragment_class_t * cls = fragment->cls;
if(cls->obj_will_delete_cb) {
cls->obj_will_delete_cb(fragment, fragment->obj);
}
lv_obj_del(fragment->obj);
if(cls->obj_deleted_cb) {
cls->obj_deleted_cb(fragment, fragment->obj);
}
if(states) {
states->obj_created = false;
}
fragment->obj = NULL;
}
void lv_fragment_recreate_obj(lv_fragment_t * fragment)
{
LV_ASSERT_NULL(fragment);
LV_ASSERT_NULL(fragment->managed);
lv_fragment_del_obj(fragment);
lv_fragment_create_obj(fragment, *fragment->managed->container);
}
/**********************
* STATIC FUNCTIONS
**********************/
static void cb_delete_assertion(lv_event_t * event)
{
LV_UNUSED(event);
LV_ASSERT_MSG(0, "Please delete objects with lv_fragment_destroy_obj");
}
#endif /*LV_USE_FRAGMENT*/

View File

@@ -0,0 +1,339 @@
/**
* Public header for Fragment
* @file lv_fragment.h
*/
#ifndef LV_FRAGMENT_H
#define LV_FRAGMENT_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../lv_conf_internal.h"
#if LV_USE_FRAGMENT
#include "../../../core/lv_obj.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct _lv_fragment_manager_t lv_fragment_manager_t;
typedef struct _lv_fragment_t lv_fragment_t;
typedef struct _lv_fragment_class_t lv_fragment_class_t;
typedef struct _lv_fragment_managed_states_t lv_fragment_managed_states_t;
struct _lv_fragment_t {
/**
* Class of this fragment
*/
const lv_fragment_class_t * cls;
/**
* Managed fragment states. If not null, then this fragment is managed.
*
* @warning Don't modify values inside this struct!
*/
lv_fragment_managed_states_t * managed;
/**
* Child fragment manager
*/
lv_fragment_manager_t * child_manager;
/**
* lv_obj returned by create_obj_cb
*/
lv_obj_t * obj;
};
struct _lv_fragment_class_t {
/**
* Constructor function for fragment class
* @param self Fragment instance
* @param args Arguments assigned by fragment manager
*/
void (*constructor_cb)(lv_fragment_t * self, void * args);
/**
* Destructor function for fragment class
* @param self Fragment instance, will be freed after this call
*/
void (*destructor_cb)(lv_fragment_t * self);
/**
* Fragment attached to manager
* @param self Fragment instance
*/
void (*attached_cb)(lv_fragment_t * self);
/**
* Fragment detached from manager
* @param self Fragment instance
*/
void (*detached_cb)(lv_fragment_t * self);
/**
* Create objects
* @param self Fragment instance
* @param container Container of the objects should be created upon
* @return Created object, NULL if multiple objects has been created
*/
lv_obj_t * (*create_obj_cb)(lv_fragment_t * self, lv_obj_t * container);
/**
*
* @param self Fragment instance
* @param obj lv_obj returned by create_obj_cb
*/
void (*obj_created_cb)(lv_fragment_t * self, lv_obj_t * obj);
/**
* Called before objects in the fragment will be deleted.
*
* @param self Fragment instance
* @param obj object with this fragment
*/
void (*obj_will_delete_cb)(lv_fragment_t * self, lv_obj_t * obj);
/**
* Called when the object created by fragment received `LV_EVENT_DELETE` event
* @param self Fragment instance
* @param obj object with this fragment
*/
void (*obj_deleted_cb)(lv_fragment_t * self, lv_obj_t * obj);
/**
* Handle event
* @param self Fragment instance
* @param which User-defined ID of event
* @param data1 User-defined data
* @param data2 User-defined data
*/
bool (*event_cb)(lv_fragment_t * self, int code, void * userdata);
/**
* *REQUIRED*: Allocation size of fragment
*/
size_t instance_size;
};
/**
* Fragment states
*/
typedef struct _lv_fragment_managed_states_t {
/**
* Class of the fragment
*/
const lv_fragment_class_t * cls;
/**
* Manager the fragment attached to
*/
lv_fragment_manager_t * manager;
/**
* Container object the fragment adding view to
*/
lv_obj_t * const * container;
/**
* Fragment instance
*/
lv_fragment_t * instance;
/**
* true between `create_obj_cb` and `obj_deleted_cb`
*/
bool obj_created;
/**
* true before `lv_fragment_del_obj` is called. Don't touch any object if this is true
*/
bool destroying_obj;
/**
* true if this fragment is in navigation stack that can be popped
*/
bool in_stack;
} lv_fragment_managed_states_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create fragment manager instance
* @param parent Parent fragment if this manager is placed inside another fragment, can be null.
* @return Fragment manager instance
*/
lv_fragment_manager_t * lv_fragment_manager_create(lv_fragment_t * parent);
/**
* Destroy fragment manager instance
* @param manager Fragment manager instance
*/
void lv_fragment_manager_del(lv_fragment_manager_t * manager);
/**
* Create object of all fragments managed by this manager.
* @param manager Fragment manager instance
*/
void lv_fragment_manager_create_obj(lv_fragment_manager_t * manager);
/**
* Delete object created by all fragments managed by this manager. Instance of fragments will not be deleted.
* @param manager Fragment manager instance
*/
void lv_fragment_manager_del_obj(lv_fragment_manager_t * manager);
/**
* Attach fragment to manager, and add to container.
* @param manager Fragment manager instance
* @param fragment Fragment instance
* @param container Pointer to container object for manager to add objects to
*/
void lv_fragment_manager_add(lv_fragment_manager_t * manager, lv_fragment_t * fragment, lv_obj_t * const * container);
/**
* Detach and destroy fragment. If fragment is in navigation stack, remove from it.
* @param manager Fragment manager instance
* @param fragment Fragment instance
*/
void lv_fragment_manager_remove(lv_fragment_manager_t * manager, lv_fragment_t * fragment);
/**
* Attach fragment to manager and add to navigation stack.
* @param manager Fragment manager instance
* @param fragment Fragment instance
* @param container Pointer to container object for manager to add objects to
*/
void lv_fragment_manager_push(lv_fragment_manager_t * manager, lv_fragment_t * fragment, lv_obj_t * const * container);
/**
* Remove the top-most fragment for stack
* @param manager Fragment manager instance
* @return true if there is fragment to pop
*/
bool lv_fragment_manager_pop(lv_fragment_manager_t * manager);
/**
* Replace fragment. Old item in the stack will be removed.
* @param manager Fragment manager instance
* @param fragment Fragment instance
* @param container Pointer to container object for manager to add objects to
*/
void lv_fragment_manager_replace(lv_fragment_manager_t * manager, lv_fragment_t * fragment,
lv_obj_t * const * container);
/**
* Send event to top-most fragment
* @param manager Fragment manager instance
* @param code User-defined ID of event
* @param userdata User-defined data
* @return true if fragment returned true
*/
bool lv_fragment_manager_send_event(lv_fragment_manager_t * manager, int code, void * userdata);
/**
* Get stack size of this fragment manager
* @param manager Fragment manager instance
* @return Stack size of this fragment manager
*/
size_t lv_fragment_manager_get_stack_size(lv_fragment_manager_t * manager);
/**
* Get top most fragment instance
* @param manager Fragment manager instance
* @return Top most fragment instance
*/
lv_fragment_t * lv_fragment_manager_get_top(lv_fragment_manager_t * manager);
/**
* Find first fragment instance in the container
* @param manager Fragment manager instance
* @param container Container which target fragment added to
* @return First fragment instance in the container
*/
lv_fragment_t * lv_fragment_manager_find_by_container(lv_fragment_manager_t * manager, const lv_obj_t * container);
/**
* Get parent fragment
* @param manager Fragment manager instance
* @return Parent fragment instance
*/
lv_fragment_t * lv_fragment_manager_get_parent_fragment(lv_fragment_manager_t * manager);
/**
* Create a fragment instance.
*
* @param cls Fragment class. This fragment must return non null object.
* @param args Arguments assigned by fragment manager
* @return Fragment instance
*/
lv_fragment_t * lv_fragment_create(const lv_fragment_class_t * cls, void * args);
/**
* Destroy a fragment.
* @param fragment Fragment instance.
*/
void lv_fragment_del(lv_fragment_t * fragment);
/**
* Get associated manager of this fragment
* @param fragment Fragment instance
* @return Fragment manager instance
*/
lv_fragment_manager_t * lv_fragment_get_manager(lv_fragment_t * fragment);
/**
* Get container object of this fragment
* @param fragment Fragment instance
* @return Reference to container object
*/
lv_obj_t * const * lv_fragment_get_container(lv_fragment_t * fragment);
/**
* Get parent fragment of this fragment
* @param fragment Fragment instance
* @return Parent fragment
*/
lv_fragment_t * lv_fragment_get_parent(lv_fragment_t * fragment);
/**
* Create object by fragment.
*
* @param fragment Fragment instance.
* @param container Container of the objects should be created upon.
* @return Created object
*/
lv_obj_t * lv_fragment_create_obj(lv_fragment_t * fragment, lv_obj_t * container);
/**
* Delete created object of a fragment
*
* @param fragment Fragment instance.
*/
void lv_fragment_del_obj(lv_fragment_t * fragment);
/**
* Destroy obj in fragment, and recreate them.
* @param fragment Fragment instance
*/
void lv_fragment_recreate_obj(lv_fragment_t * fragment);
/**********************
* MACROS
**********************/
#endif /*LV_USE_FRAGMENT*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_FRAGMENT_H*/

View File

@@ -0,0 +1,281 @@
/**
* @file lv_fragment_manager.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_fragment.h"
#if LV_USE_FRAGMENT
#include "../../../misc/lv_ll.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct _lv_fragment_stack_item_t {
lv_fragment_managed_states_t * states;
} lv_fragment_stack_item_t;
struct _lv_fragment_manager_t {
lv_fragment_t * parent;
/**
* Linked list to store attached fragments
*/
lv_ll_t attached;
/**
* Linked list to store fragments in stack
*/
lv_ll_t stack;
};
/**********************
* STATIC PROTOTYPES
**********************/
static void item_create_obj(lv_fragment_managed_states_t * item);
static void item_del_obj(lv_fragment_managed_states_t * item);
static void item_del_fragment(lv_fragment_managed_states_t * item);
static lv_fragment_managed_states_t * fragment_attach(lv_fragment_manager_t * manager, lv_fragment_t * fragment,
lv_obj_t * const * container);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_fragment_manager_t * lv_fragment_manager_create(lv_fragment_t * parent)
{
lv_fragment_manager_t * instance = lv_mem_alloc(sizeof(lv_fragment_manager_t));
lv_memset_00(instance, sizeof(lv_fragment_manager_t));
instance->parent = parent;
_lv_ll_init(&instance->attached, sizeof(lv_fragment_managed_states_t));
_lv_ll_init(&instance->stack, sizeof(lv_fragment_stack_item_t));
return instance;
}
void lv_fragment_manager_del(lv_fragment_manager_t * manager)
{
LV_ASSERT_NULL(manager);
lv_fragment_managed_states_t * states;
_LV_LL_READ_BACK(&manager->attached, states) {
item_del_obj(states);
item_del_fragment(states);
}
_lv_ll_clear(&manager->attached);
_lv_ll_clear(&manager->stack);
lv_mem_free(manager);
}
void lv_fragment_manager_create_obj(lv_fragment_manager_t * manager)
{
LV_ASSERT_NULL(manager);
lv_fragment_stack_item_t * top = _lv_ll_get_tail(&manager->stack);
lv_fragment_managed_states_t * states = NULL;
_LV_LL_READ(&manager->attached, states) {
if(states->in_stack && top->states != states) {
/*Only create obj for top item in stack*/
continue;
}
item_create_obj(states);
}
}
void lv_fragment_manager_del_obj(lv_fragment_manager_t * manager)
{
LV_ASSERT_NULL(manager);
lv_fragment_managed_states_t * states = NULL;
_LV_LL_READ_BACK(&manager->attached, states) {
item_del_obj(states);
}
}
void lv_fragment_manager_add(lv_fragment_manager_t * manager, lv_fragment_t * fragment, lv_obj_t * const * container)
{
lv_fragment_managed_states_t * states = fragment_attach(manager, fragment, container);
if(!manager->parent || manager->parent->managed->obj_created) {
item_create_obj(states);
}
}
void lv_fragment_manager_remove(lv_fragment_manager_t * manager, lv_fragment_t * fragment)
{
LV_ASSERT_NULL(manager);
LV_ASSERT_NULL(fragment);
LV_ASSERT_NULL(fragment->managed);
LV_ASSERT(fragment->managed->manager == manager);
lv_fragment_managed_states_t * states = fragment->managed;
lv_fragment_managed_states_t * prev = NULL;
bool was_top = false;
if(states->in_stack) {
void * stack_top = _lv_ll_get_tail(&manager->stack);
lv_fragment_stack_item_t * item = NULL;
_LV_LL_READ_BACK(&manager->stack, item) {
if(item->states == states) {
was_top = stack_top == item;
void * stack_prev = _lv_ll_get_prev(&manager->stack, item);
if(!stack_prev) break;
prev = ((lv_fragment_stack_item_t *) stack_prev)->states;
break;
}
}
if(item) {
_lv_ll_remove(&manager->stack, item);
lv_mem_free(item);
}
}
item_del_obj(states);
item_del_fragment(states);
_lv_ll_remove(&manager->attached, states);
lv_mem_free(states);
if(prev && was_top) {
item_create_obj(prev);
}
}
void lv_fragment_manager_push(lv_fragment_manager_t * manager, lv_fragment_t * fragment, lv_obj_t * const * container)
{
lv_fragment_stack_item_t * top = _lv_ll_get_tail(&manager->stack);
if(top != NULL) {
item_del_obj(top->states);
}
lv_fragment_managed_states_t * states = fragment_attach(manager, fragment, container);
states->in_stack = true;
/*Add fragment to the top of the stack*/
lv_fragment_stack_item_t * item = _lv_ll_ins_tail(&manager->stack);
lv_memset_00(item, sizeof(lv_fragment_stack_item_t));
item->states = states;
item_create_obj(states);
}
bool lv_fragment_manager_pop(lv_fragment_manager_t * manager)
{
lv_fragment_t * top = lv_fragment_manager_get_top(manager);
if(top == NULL) return false;
lv_fragment_manager_remove(manager, top);
return true;
}
void lv_fragment_manager_replace(lv_fragment_manager_t * manager, lv_fragment_t * fragment,
lv_obj_t * const * container)
{
lv_fragment_t * top = lv_fragment_manager_find_by_container(manager, *container);
if(top != NULL) {
lv_fragment_manager_remove(manager, top);
}
lv_fragment_manager_add(manager, fragment, container);
}
bool lv_fragment_manager_send_event(lv_fragment_manager_t * manager, int code, void * userdata)
{
LV_ASSERT_NULL(manager);
lv_fragment_managed_states_t * p = NULL;
_LV_LL_READ_BACK(&manager->attached, p) {
if(!p->obj_created || p->destroying_obj) continue;
lv_fragment_t * instance = p->instance;
if(!instance) continue;
if(lv_fragment_manager_send_event(instance->child_manager, code, userdata)) return true;
if(p->cls->event_cb && p->cls->event_cb(instance, code, userdata)) return true;
}
return false;
}
size_t lv_fragment_manager_get_stack_size(lv_fragment_manager_t * manager)
{
LV_ASSERT_NULL(manager);
return _lv_ll_get_len(&manager->stack);
}
lv_fragment_t * lv_fragment_manager_get_top(lv_fragment_manager_t * manager)
{
LV_ASSERT(manager);
lv_fragment_stack_item_t * top = _lv_ll_get_tail(&manager->stack);
if(!top)return NULL;
return top->states->instance;
}
lv_fragment_t * lv_fragment_manager_find_by_container(lv_fragment_manager_t * manager, const lv_obj_t * container)
{
LV_ASSERT(manager);
lv_fragment_managed_states_t * states;
_LV_LL_READ(&manager->attached, states) {
if(*states->container == container) return states->instance;
}
return NULL;
}
lv_fragment_t * lv_fragment_manager_get_parent_fragment(lv_fragment_manager_t * manager)
{
LV_ASSERT_NULL(manager);
return manager->parent;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void item_create_obj(lv_fragment_managed_states_t * item)
{
LV_ASSERT(item->instance);
lv_fragment_create_obj(item->instance, item->container ? *item->container : NULL);
}
static void item_del_obj(lv_fragment_managed_states_t * item)
{
lv_fragment_del_obj(item->instance);
}
/**
* Detach, then destroy fragment
* @param item fragment states
*/
static void item_del_fragment(lv_fragment_managed_states_t * item)
{
lv_fragment_t * instance = item->instance;
if(instance->cls->detached_cb) {
instance->cls->detached_cb(instance);
}
instance->managed = NULL;
lv_fragment_del(instance);
item->instance = NULL;
}
static lv_fragment_managed_states_t * fragment_attach(lv_fragment_manager_t * manager, lv_fragment_t * fragment,
lv_obj_t * const * container)
{
LV_ASSERT(manager);
LV_ASSERT(fragment);
LV_ASSERT(fragment->managed == NULL);
lv_fragment_managed_states_t * states = _lv_ll_ins_tail(&manager->attached);
lv_memset_00(states, sizeof(lv_fragment_managed_states_t));
states->cls = fragment->cls;
states->manager = manager;
states->container = container;
states->instance = fragment;
fragment->managed = states;
if(fragment->cls->attached_cb) {
fragment->cls->attached_cb(fragment);
}
return states;
}
#endif /*LV_USE_FRAGMENT*/

View File

@@ -0,0 +1,375 @@
/**
* @file lv_gridnav.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_gridnav.h"
#if LV_USE_GRIDNAV
#include "../../../misc/lv_assert.h"
#include "../../../misc/lv_math.h"
#include "../../../core/lv_indev.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_gridnav_ctrl_t ctrl;
lv_obj_t * focused_obj;
} lv_gridnav_dsc_t;
typedef enum {
FIND_LEFT,
FIND_RIGHT,
FIND_TOP,
FIND_BOTTOM,
FIND_NEXT_ROW_FIRST_ITEM,
FIND_PREV_ROW_LAST_ITEM,
FIND_FIRST_ROW,
FIND_LAST_ROW,
} find_mode_t;
/**********************
* STATIC PROTOTYPES
**********************/
static void gridnav_event_cb(lv_event_t * e);
static lv_obj_t * find_chid(lv_obj_t * obj, lv_obj_t * start_child, find_mode_t mode);
static lv_obj_t * find_first_focusable(lv_obj_t * obj);
static lv_obj_t * find_last_focusable(lv_obj_t * obj);
static bool obj_is_focuable(lv_obj_t * obj);
static lv_coord_t get_x_center(lv_obj_t * obj);
static lv_coord_t get_y_center(lv_obj_t * obj);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_gridnav_add(lv_obj_t * obj, lv_gridnav_ctrl_t ctrl)
{
lv_gridnav_remove(obj); /*Be sure to not add gridnav twice*/
lv_gridnav_dsc_t * dsc = lv_mem_alloc(sizeof(lv_gridnav_dsc_t));
LV_ASSERT_MALLOC(dsc);
dsc->ctrl = ctrl;
dsc->focused_obj = NULL;
lv_obj_add_event_cb(obj, gridnav_event_cb, LV_EVENT_ALL, dsc);
lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLL_WITH_ARROW);
}
void lv_gridnav_remove(lv_obj_t * obj)
{
lv_gridnav_dsc_t * dsc = lv_obj_get_event_user_data(obj, gridnav_event_cb);
if(dsc == NULL) return; /* no gridnav on this object */
lv_mem_free(dsc);
lv_obj_remove_event_cb(obj, gridnav_event_cb);
}
void lv_gridnav_set_focused(lv_obj_t * cont, lv_obj_t * to_focus, lv_anim_enable_t anim_en)
{
LV_ASSERT_NULL(to_focus);
lv_gridnav_dsc_t * dsc = lv_obj_get_event_user_data(cont, gridnav_event_cb);
if(dsc == NULL) {
LV_LOG_WARN("`cont` is not a gridnav container");
return;
}
if(obj_is_focuable(to_focus) == false) {
LV_LOG_WARN("The object to focus is not focusable");
return;
}
lv_obj_clear_state(dsc->focused_obj, LV_STATE_FOCUSED | LV_STATE_FOCUS_KEY);
lv_obj_add_state(to_focus, LV_STATE_FOCUSED | LV_STATE_FOCUS_KEY);
lv_obj_scroll_to_view(to_focus, anim_en);
dsc->focused_obj = to_focus;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void gridnav_event_cb(lv_event_t * e)
{
lv_obj_t * obj = lv_event_get_current_target(e);
lv_gridnav_dsc_t * dsc = lv_event_get_user_data(e);
lv_event_code_t code = lv_event_get_code(e);
if(code == LV_EVENT_KEY) {
uint32_t child_cnt = lv_obj_get_child_cnt(obj);
if(child_cnt == 0) return;
if(dsc->focused_obj == NULL) dsc->focused_obj = find_first_focusable(obj);
if(dsc->focused_obj == NULL) return;
uint32_t key = lv_event_get_key(e);
lv_obj_t * guess = NULL;
if(key == LV_KEY_RIGHT) {
if((dsc->ctrl & LV_GRIDNAV_CTRL_SCROLL_FIRST) && lv_obj_has_flag(dsc->focused_obj, LV_OBJ_FLAG_SCROLLABLE) &&
lv_obj_get_scroll_right(dsc->focused_obj) > 0) {
lv_coord_t d = lv_obj_get_width(dsc->focused_obj) / 4;
if(d <= 0) d = 1;
lv_obj_scroll_by_bounded(dsc->focused_obj, -d, 0, LV_ANIM_ON);
}
else {
guess = find_chid(obj, dsc->focused_obj, FIND_RIGHT);
if(guess == NULL) {
if(dsc->ctrl & LV_GRIDNAV_CTRL_ROLLOVER) {
guess = find_chid(obj, dsc->focused_obj, FIND_NEXT_ROW_FIRST_ITEM);
if(guess == NULL) guess = find_first_focusable(obj);
}
else {
lv_group_focus_next(lv_obj_get_group(obj));
}
}
}
}
else if(key == LV_KEY_LEFT) {
if((dsc->ctrl & LV_GRIDNAV_CTRL_SCROLL_FIRST) && lv_obj_has_flag(dsc->focused_obj, LV_OBJ_FLAG_SCROLLABLE) &&
lv_obj_get_scroll_left(dsc->focused_obj) > 0) {
lv_coord_t d = lv_obj_get_width(dsc->focused_obj) / 4;
if(d <= 0) d = 1;
lv_obj_scroll_by_bounded(dsc->focused_obj, d, 0, LV_ANIM_ON);
}
else {
guess = find_chid(obj, dsc->focused_obj, FIND_LEFT);
if(guess == NULL) {
if(dsc->ctrl & LV_GRIDNAV_CTRL_ROLLOVER) {
guess = find_chid(obj, dsc->focused_obj, FIND_PREV_ROW_LAST_ITEM);
if(guess == NULL) guess = find_last_focusable(obj);
}
else {
lv_group_focus_prev(lv_obj_get_group(obj));
}
}
}
}
else if(key == LV_KEY_DOWN) {
if((dsc->ctrl & LV_GRIDNAV_CTRL_SCROLL_FIRST) && lv_obj_has_flag(dsc->focused_obj, LV_OBJ_FLAG_SCROLLABLE) &&
lv_obj_get_scroll_bottom(dsc->focused_obj) > 0) {
lv_coord_t d = lv_obj_get_height(dsc->focused_obj) / 4;
if(d <= 0) d = 1;
lv_obj_scroll_by_bounded(dsc->focused_obj, 0, -d, LV_ANIM_ON);
}
else {
guess = find_chid(obj, dsc->focused_obj, FIND_BOTTOM);
if(guess == NULL) {
if(dsc->ctrl & LV_GRIDNAV_CTRL_ROLLOVER) {
guess = find_chid(obj, dsc->focused_obj, FIND_FIRST_ROW);
}
else {
lv_group_focus_next(lv_obj_get_group(obj));
}
}
}
}
else if(key == LV_KEY_UP) {
if((dsc->ctrl & LV_GRIDNAV_CTRL_SCROLL_FIRST) && lv_obj_has_flag(dsc->focused_obj, LV_OBJ_FLAG_SCROLLABLE) &&
lv_obj_get_scroll_top(dsc->focused_obj) > 0) {
lv_coord_t d = lv_obj_get_height(dsc->focused_obj) / 4;
if(d <= 0) d = 1;
lv_obj_scroll_by_bounded(dsc->focused_obj, 0, d, LV_ANIM_ON);
}
else {
guess = find_chid(obj, dsc->focused_obj, FIND_TOP);
if(guess == NULL) {
if(dsc->ctrl & LV_GRIDNAV_CTRL_ROLLOVER) {
guess = find_chid(obj, dsc->focused_obj, FIND_LAST_ROW);
}
else {
lv_group_focus_prev(lv_obj_get_group(obj));
}
}
}
}
else {
if(lv_group_get_focused(lv_obj_get_group(obj)) == obj) {
lv_event_send(dsc->focused_obj, LV_EVENT_KEY, &key);
}
}
if(guess && guess != dsc->focused_obj) {
lv_obj_clear_state(dsc->focused_obj, LV_STATE_FOCUSED | LV_STATE_FOCUS_KEY);
lv_obj_add_state(guess, LV_STATE_FOCUSED | LV_STATE_FOCUS_KEY);
lv_obj_scroll_to_view(guess, LV_ANIM_ON);
dsc->focused_obj = guess;
}
}
else if(code == LV_EVENT_FOCUSED) {
if(dsc->focused_obj == NULL) dsc->focused_obj = find_first_focusable(obj);
if(dsc->focused_obj) {
lv_obj_add_state(dsc->focused_obj, LV_STATE_FOCUSED | LV_STATE_FOCUS_KEY);
lv_obj_scroll_to_view(dsc->focused_obj, LV_ANIM_OFF);
}
}
else if(code == LV_EVENT_DEFOCUSED) {
if(dsc->focused_obj) {
lv_obj_clear_state(dsc->focused_obj, LV_STATE_FOCUSED | LV_STATE_FOCUS_KEY);
}
}
else if(code == LV_EVENT_CHILD_CREATED) {
lv_obj_t * child = lv_event_get_target(e);
if(lv_obj_get_parent(child) == obj) {
if(dsc->focused_obj == NULL) {
dsc->focused_obj = child;
if(lv_obj_has_state(obj, LV_STATE_FOCUSED)) {
lv_obj_add_state(child, LV_STATE_FOCUSED | LV_STATE_FOCUS_KEY);
lv_obj_scroll_to_view(child, LV_ANIM_OFF);
}
}
}
}
else if(code == LV_EVENT_CHILD_DELETED) {
/*This event bubble, so be sure this object's child was deleted.
*As we don't know which object was deleted we can't make the next focused.
*So make the first object focused*/
lv_obj_t * target = lv_event_get_target(e);
if(target == obj) {
dsc->focused_obj = find_first_focusable(obj);
}
}
else if(code == LV_EVENT_DELETE) {
lv_gridnav_remove(obj);
}
else if(code == LV_EVENT_PRESSED || code == LV_EVENT_PRESSING || code == LV_EVENT_PRESS_LOST ||
code == LV_EVENT_LONG_PRESSED || code == LV_EVENT_LONG_PRESSED_REPEAT ||
code == LV_EVENT_CLICKED || code == LV_EVENT_RELEASED) {
if(lv_group_get_focused(lv_obj_get_group(obj)) == obj) {
/*Forward press/release related event too*/
lv_indev_type_t t = lv_indev_get_type(lv_indev_get_act());
if(t == LV_INDEV_TYPE_ENCODER || t == LV_INDEV_TYPE_KEYPAD) {
lv_event_send(dsc->focused_obj, code, lv_indev_get_act());
}
}
}
}
static lv_obj_t * find_chid(lv_obj_t * obj, lv_obj_t * start_child, find_mode_t mode)
{
lv_coord_t x_start = get_x_center(start_child);
lv_coord_t y_start = get_y_center(start_child);
uint32_t child_cnt = lv_obj_get_child_cnt(obj);
lv_obj_t * guess = NULL;
lv_coord_t x_err_guess = LV_COORD_MAX;
lv_coord_t y_err_guess = LV_COORD_MAX;
lv_coord_t h_half = lv_obj_get_height(start_child) / 2;
lv_coord_t h_max = lv_obj_get_height(obj) + lv_obj_get_scroll_top(obj) + lv_obj_get_scroll_bottom(obj);
uint32_t i;
for(i = 0; i < child_cnt; i++) {
lv_obj_t * child = lv_obj_get_child(obj, i);
if(child == start_child) continue;
if(obj_is_focuable(child) == false) continue;
lv_coord_t x_err = 0;
lv_coord_t y_err = 0;
switch(mode) {
case FIND_LEFT:
x_err = get_x_center(child) - x_start;
y_err = get_y_center(child) - y_start;
if(x_err >= 0) continue; /*It's on the right*/
if(LV_ABS(y_err) > h_half) continue; /*Too far*/
break;
case FIND_RIGHT:
x_err = get_x_center(child) - x_start;
y_err = get_y_center(child) - y_start;
if(x_err <= 0) continue; /*It's on the left*/
if(LV_ABS(y_err) > h_half) continue; /*Too far*/
break;
case FIND_TOP:
x_err = get_x_center(child) - x_start;
y_err = get_y_center(child) - y_start;
if(y_err >= 0) continue; /*It's on the bottom*/
break;
case FIND_BOTTOM:
x_err = get_x_center(child) - x_start;
y_err = get_y_center(child) - y_start;
if(y_err <= 0) continue; /*It's on the top*/
break;
case FIND_NEXT_ROW_FIRST_ITEM:
y_err = get_y_center(child) - y_start;
if(y_err <= 0) continue; /*It's on the top*/
x_err = lv_obj_get_x(child);
break;
case FIND_PREV_ROW_LAST_ITEM:
y_err = get_y_center(child) - y_start;
if(y_err >= 0) continue; /*It's on the bottom*/
x_err = obj->coords.x2 - child->coords.x2;
break;
case FIND_FIRST_ROW:
x_err = get_x_center(child) - x_start;
y_err = lv_obj_get_y(child);
break;
case FIND_LAST_ROW:
x_err = get_x_center(child) - x_start;
y_err = h_max - lv_obj_get_y(child);
}
if(guess == NULL ||
(y_err * y_err + x_err * x_err < y_err_guess * y_err_guess + x_err_guess * x_err_guess)) {
guess = child;
x_err_guess = x_err;
y_err_guess = y_err;
}
}
return guess;
}
static lv_obj_t * find_first_focusable(lv_obj_t * obj)
{
uint32_t child_cnt = lv_obj_get_child_cnt(obj);
uint32_t i;
for(i = 0; i < child_cnt; i++) {
lv_obj_t * child = lv_obj_get_child(obj, i);
if(obj_is_focuable(child)) return child;
}
return NULL;
}
static lv_obj_t * find_last_focusable(lv_obj_t * obj)
{
uint32_t child_cnt = lv_obj_get_child_cnt(obj);
int32_t i;
for(i = child_cnt - 1; i >= 0; i--) {
lv_obj_t * child = lv_obj_get_child(obj, i);
if(obj_is_focuable(child)) return child;
}
return NULL;
}
static bool obj_is_focuable(lv_obj_t * obj)
{
if(lv_obj_has_flag(obj, LV_OBJ_FLAG_HIDDEN)) return false;
if(lv_obj_has_flag(obj, LV_OBJ_FLAG_CLICKABLE | LV_OBJ_FLAG_CLICK_FOCUSABLE)) return true;
else return false;
}
static lv_coord_t get_x_center(lv_obj_t * obj)
{
return obj->coords.x1 + lv_area_get_width(&obj->coords) / 2;
}
static lv_coord_t get_y_center(lv_obj_t * obj)
{
return obj->coords.y1 + lv_area_get_height(&obj->coords) / 2;
}
#endif /*LV_USE_GRIDNAV*/

View File

@@ -0,0 +1,123 @@
/**
* @file lv_templ.c
*
*/
/*********************
* INCLUDES
*********************/
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/*This typedef exists purely to keep -Wpedantic happy when the file is empty.*/
/*It can be removed.*/
typedef int _keep_pedantic_happy;
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**********************
* STATIC FUNCTIONS
**********************/
/**
* @file lv_gridnav.h
*
*/
#ifndef LV_GRIDFOCUS_H
#define LV_GRIDFOCUS_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../core/lv_obj.h"
#if LV_USE_GRIDNAV
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef enum {
LV_GRIDNAV_CTRL_NONE = 0x0,
/**
* If there is no next/previous object in a direction,
* the focus goes to the object in the next/previous row (on left/right keys)
* or first/last row (on up/down keys)
*/
LV_GRIDNAV_CTRL_ROLLOVER = 0x1,
/**
* If an arrow is pressed and the focused object can be scrolled in that direction
* then it will be scrolled instead of going to the next/previous object.
* If there is no more room for scrolling the next/previous object will be focused normally */
LV_GRIDNAV_CTRL_SCROLL_FIRST = 0x2,
} lv_gridnav_ctrl_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Add grid navigation feature to an object. It expects the children to be arranged
* into a grid-like layout. Although it's not required to have pixel perfect alignment.
* This feature makes possible to use keys to navigate among the children and focus them.
* The keys other than arrows and press/release related events
* are forwarded to the focused child.
* @param obj pointer to an object on which navigation should be applied.
* @param ctrl control flags from `lv_gridnav_ctrl_t`.
*/
void lv_gridnav_add(lv_obj_t * obj, lv_gridnav_ctrl_t ctrl);
/**
* Remove the grid navigation support from an object
* @param obj pointer to an object
*/
void lv_gridnav_remove(lv_obj_t * obj);
/**
* Manually focus an object on gridnav container
* @param cont pointer to a gridnav container
* @param to_focus pointer to an object to focus
* @param anim_en LV_ANIM_ON/OFF
*/
void lv_gridnav_set_focused(lv_obj_t * cont, lv_obj_t * to_focus, lv_anim_enable_t anim_en);
/**********************
* MACROS
**********************/
#endif /*LV_USE_GRIDNAV*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_GRIDFOCUS_H*/

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,145 @@
/**
* @file lv_ime_pinyin.h
*
*/
#ifndef LV_IME_PINYIN_H
#define LV_IME_PINYIN_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../lvgl.h"
#if LV_USE_IME_PINYIN != 0
/*********************
* DEFINES
*********************/
#define LV_IME_PINYIN_K9_MAX_INPUT 7
/**********************
* TYPEDEFS
**********************/
typedef enum {
LV_IME_PINYIN_MODE_K26,
LV_IME_PINYIN_MODE_K9,
} lv_ime_pinyin_mode_t;
/*Data of pinyin_dict*/
typedef struct {
const char * const py;
const char * const py_mb;
} lv_pinyin_dict_t;
/*Data of 9-key input(k9) mode*/
typedef struct {
char py_str[7];
} ime_pinyin_k9_py_str_t;
/*Data of lv_ime_pinyin*/
typedef struct {
lv_obj_t obj;
lv_obj_t * kb;
lv_obj_t * cand_panel;
lv_pinyin_dict_t * dict;
lv_ll_t k9_legal_py_ll;
char * cand_str; /* Candidate string */
char input_char[16]; /* Input box character */
#if LV_IME_PINYIN_USE_K9_MODE
char k9_input_str[LV_IME_PINYIN_K9_MAX_INPUT]; /* 9-key input(k9) mode input string */
uint16_t k9_py_ll_pos; /* Current pinyin map pages(k9) */
uint16_t k9_legal_py_count; /* Count of legal Pinyin numbers(k9) */
uint16_t k9_input_str_len; /* 9-key input(k9) mode input string max len */
#endif
uint16_t ta_count; /* The number of characters entered in the text box this time */
uint16_t cand_num; /* Number of candidates */
uint16_t py_page; /* Current pinyin map pages(k26) */
uint16_t py_num[26]; /* Number and length of Pinyin */
uint16_t py_pos[26]; /* Pinyin position */
uint8_t mode : 1; /* Set mode, 1: 26-key input(k26), 0: 9-key input(k9). Default: 1. */
} lv_ime_pinyin_t;
/***********************
* GLOBAL VARIABLES
***********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
lv_obj_t * lv_ime_pinyin_create(lv_obj_t * parent);
/*=====================
* Setter functions
*====================*/
/**
* Set the keyboard of Pinyin input method.
* @param obj pointer to a Pinyin input method object
* @param dict pointer to a Pinyin input method keyboard
*/
void lv_ime_pinyin_set_keyboard(lv_obj_t * obj, lv_obj_t * kb);
/**
* Set the dictionary of Pinyin input method.
* @param obj pointer to a Pinyin input method object
* @param dict pointer to a Pinyin input method dictionary
*/
void lv_ime_pinyin_set_dict(lv_obj_t * obj, lv_pinyin_dict_t * dict);
/**
* Set mode, 26-key input(k26) or 9-key input(k9).
* @param obj pointer to a Pinyin input method object
* @param mode the mode from 'lv_ime_pinyin_mode_t'
*/
void lv_ime_pinyin_set_mode(lv_obj_t * obj, lv_ime_pinyin_mode_t mode);
/*=====================
* Getter functions
*====================*/
/**
* Set the dictionary of Pinyin input method.
* @param obj pointer to a Pinyin IME object
* @return pointer to the Pinyin IME keyboard
*/
lv_obj_t * lv_ime_pinyin_get_kb(lv_obj_t * obj);
/**
* Set the dictionary of Pinyin input method.
* @param obj pointer to a Pinyin input method object
* @return pointer to the Pinyin input method candidate panel
*/
lv_obj_t * lv_ime_pinyin_get_cand_panel(lv_obj_t * obj);
/**
* Set the dictionary of Pinyin input method.
* @param obj pointer to a Pinyin input method object
* @return pointer to the Pinyin input method dictionary
*/
lv_pinyin_dict_t * lv_ime_pinyin_get_dict(lv_obj_t * obj);
/*=====================
* Other functions
*====================*/
/**********************
* MACROS
**********************/
#endif /*LV_IME_PINYIN*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_USE_IME_PINYIN*/

View File

@@ -0,0 +1,126 @@
/**
* @file lv_imgfont.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_imgfont.h"
#if LV_USE_IMGFONT
/*********************
* DEFINES
*********************/
#define LV_IMGFONT_PATH_MAX_LEN 64
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_font_t * font;
lv_get_imgfont_path_cb_t path_cb;
char path[LV_IMGFONT_PATH_MAX_LEN];
} imgfont_dsc_t;
/**********************
* STATIC PROTOTYPES
**********************/
static const uint8_t * imgfont_get_glyph_bitmap(const lv_font_t * font, uint32_t unicode);
static bool imgfont_get_glyph_dsc(const lv_font_t * font, lv_font_glyph_dsc_t * dsc_out,
uint32_t unicode, uint32_t unicode_next);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_font_t * lv_imgfont_create(uint16_t height, lv_get_imgfont_path_cb_t path_cb)
{
LV_ASSERT_MSG(LV_IMGFONT_PATH_MAX_LEN > sizeof(lv_img_dsc_t),
"LV_IMGFONT_PATH_MAX_LEN must be greater than sizeof(lv_img_dsc_t)");
size_t size = sizeof(imgfont_dsc_t) + sizeof(lv_font_t);
imgfont_dsc_t * dsc = (imgfont_dsc_t *)lv_mem_alloc(size);
if(dsc == NULL) return NULL;
lv_memset_00(dsc, size);
dsc->font = (lv_font_t *)(((char *)dsc) + sizeof(imgfont_dsc_t));
dsc->path_cb = path_cb;
lv_font_t * font = dsc->font;
font->dsc = dsc;
font->get_glyph_dsc = imgfont_get_glyph_dsc;
font->get_glyph_bitmap = imgfont_get_glyph_bitmap;
font->subpx = LV_FONT_SUBPX_NONE;
font->line_height = height;
font->base_line = 0;
font->underline_position = 0;
font->underline_thickness = 0;
return dsc->font;
}
void lv_imgfont_destroy(lv_font_t * font)
{
if(font == NULL) {
return;
}
imgfont_dsc_t * dsc = (imgfont_dsc_t *)font->dsc;
lv_mem_free(dsc);
}
/**********************
* STATIC FUNCTIONS
**********************/
static const uint8_t * imgfont_get_glyph_bitmap(const lv_font_t * font, uint32_t unicode)
{
LV_UNUSED(unicode);
LV_ASSERT_NULL(font);
imgfont_dsc_t * dsc = (imgfont_dsc_t *)font->dsc;
return (uint8_t *)dsc->path;
}
static bool imgfont_get_glyph_dsc(const lv_font_t * font, lv_font_glyph_dsc_t * dsc_out,
uint32_t unicode, uint32_t unicode_next)
{
LV_ASSERT_NULL(font);
imgfont_dsc_t * dsc = (imgfont_dsc_t *)font->dsc;
LV_ASSERT_NULL(dsc);
if(dsc->path_cb == NULL) return false;
if(!dsc->path_cb(dsc->font, dsc->path, LV_IMGFONT_PATH_MAX_LEN, unicode, unicode_next)) {
return false;
}
lv_img_header_t header;
if(lv_img_decoder_get_info(dsc->path, &header) != LV_RES_OK) {
return false;
}
dsc_out->is_placeholder = 0;
dsc_out->adv_w = header.w;
dsc_out->box_w = header.w;
dsc_out->box_h = header.h;
dsc_out->bpp = LV_IMGFONT_BPP; /* is image identifier */
dsc_out->ofs_x = 0;
dsc_out->ofs_y = 0;
return true;
}
#endif /*LV_USE_IMGFONT*/

View File

@@ -0,0 +1,60 @@
/**
* @file lv_imgfont.h
*
*/
#ifndef LV_IMGFONT_H
#define LV_IMGFONT_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../lvgl.h"
#if LV_USE_IMGFONT
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/* gets the image path name of this character */
typedef bool (*lv_get_imgfont_path_cb_t)(const lv_font_t * font, void * img_src,
uint16_t len, uint32_t unicode, uint32_t unicode_next);
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Creates a image font with info parameter specified.
* @param height font size
* @param path_cb a function to get the image path name of character.
* @return pointer to the new imgfont or NULL if create error.
*/
lv_font_t * lv_imgfont_create(uint16_t height, lv_get_imgfont_path_cb_t path_cb);
/**
* Destroy a image font that has been created.
* @param font pointer to image font handle.
*/
void lv_imgfont_destroy(lv_font_t * font);
/**********************
* MACROS
**********************/
#endif /*LV_USE_IMGFONT*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /* LV_IMGFONT_H */

View File

@@ -0,0 +1,44 @@
/**
* @file lv_others.h
*
*/
#ifndef LV_OTHERS_H
#define LV_OTHERS_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "snapshot/lv_snapshot.h"
#include "monkey/lv_monkey.h"
#include "gridnav/lv_gridnav.h"
#include "fragment/lv_fragment.h"
#include "imgfont/lv_imgfont.h"
#include "msg/lv_msg.h"
#include "ime/lv_ime_pinyin.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
/**********************
* MACROS
**********************/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_OTHERS_H*/

View File

@@ -0,0 +1,187 @@
/**
* @file lv_monkey.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_monkey.h"
#if LV_USE_MONKEY != 0
/*********************
* DEFINES
*********************/
#define MONKEY_PERIOD_RANGE_MIN_DEF 100
#define MONKEY_PERIOD_RANGE_MAX_DEF 1000
/**********************
* TYPEDEFS
**********************/
typedef struct _lv_monkey {
lv_monkey_config_t config;
lv_indev_drv_t indev_drv;
lv_indev_data_t indev_data;
lv_indev_t * indev;
lv_timer_t * timer;
#if LV_USE_USER_DATA
void * user_data;
#endif
} lv_monkey_t;
static const lv_key_t lv_key_map[] = {
LV_KEY_UP,
LV_KEY_DOWN,
LV_KEY_RIGHT,
LV_KEY_LEFT,
LV_KEY_ESC,
LV_KEY_DEL,
LV_KEY_BACKSPACE,
LV_KEY_ENTER,
LV_KEY_NEXT,
LV_KEY_PREV,
LV_KEY_HOME,
LV_KEY_END,
};
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_monkey_read_cb(lv_indev_drv_t * indev_drv, lv_indev_data_t * data);
static int32_t lv_monkey_random(int32_t howsmall, int32_t howbig);
static void lv_monkey_timer_cb(lv_timer_t * timer);
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_monkey_config_init(lv_monkey_config_t * config)
{
lv_memset_00(config, sizeof(lv_monkey_config_t));
config->type = LV_INDEV_TYPE_POINTER;
config->period_range.min = MONKEY_PERIOD_RANGE_MIN_DEF;
config->period_range.max = MONKEY_PERIOD_RANGE_MAX_DEF;
}
lv_monkey_t * lv_monkey_create(const lv_monkey_config_t * config)
{
lv_monkey_t * monkey = lv_mem_alloc(sizeof(lv_monkey_t));
LV_ASSERT_MALLOC(monkey);
lv_memset_00(monkey, sizeof(lv_monkey_t));
monkey->config = *config;
lv_indev_drv_t * drv = &monkey->indev_drv;
lv_indev_drv_init(drv);
drv->type = config->type;
drv->read_cb = lv_monkey_read_cb;
drv->user_data = monkey;
monkey->timer = lv_timer_create(lv_monkey_timer_cb, monkey->config.period_range.min, monkey);
lv_timer_pause(monkey->timer);
monkey->indev = lv_indev_drv_register(drv);
return monkey;
}
lv_indev_t * lv_monkey_get_indev(lv_monkey_t * monkey)
{
LV_ASSERT_NULL(monkey);
return monkey->indev;
}
void lv_monkey_set_enable(lv_monkey_t * monkey, bool en)
{
LV_ASSERT_NULL(monkey);
en ? lv_timer_resume(monkey->timer) : lv_timer_pause(monkey->timer);
}
bool lv_monkey_get_enable(lv_monkey_t * monkey)
{
LV_ASSERT_NULL(monkey);
return !monkey->timer->paused;
}
#if LV_USE_USER_DATA
void lv_monkey_set_user_data(lv_monkey_t * monkey, void * user_data)
{
LV_ASSERT_NULL(monkey);
monkey->user_data = user_data;
}
void * lv_monkey_get_user_data(lv_monkey_t * monkey)
{
LV_ASSERT_NULL(monkey);
return monkey->user_data;
}
#endif
void lv_monkey_del(lv_monkey_t * monkey)
{
LV_ASSERT_NULL(monkey);
lv_timer_del(monkey->timer);
lv_indev_delete(monkey->indev);
lv_mem_free(monkey);
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_monkey_read_cb(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
{
lv_monkey_t * monkey = indev_drv->user_data;
data->btn_id = monkey->indev_data.btn_id;
data->point = monkey->indev_data.point;
data->enc_diff = monkey->indev_data.enc_diff;
data->state = monkey->indev_data.state;
}
static int32_t lv_monkey_random(int32_t howsmall, int32_t howbig)
{
if(howsmall >= howbig) {
return howsmall;
}
int32_t diff = howbig - howsmall;
return (int32_t)lv_rand(0, diff) + howsmall;
}
static void lv_monkey_timer_cb(lv_timer_t * timer)
{
lv_monkey_t * monkey = timer->user_data;
lv_indev_data_t * data = &monkey->indev_data;
switch(monkey->indev_drv.type) {
case LV_INDEV_TYPE_POINTER:
data->point.x = (lv_coord_t)lv_monkey_random(0, LV_HOR_RES - 1);
data->point.y = (lv_coord_t)lv_monkey_random(0, LV_VER_RES - 1);
break;
case LV_INDEV_TYPE_ENCODER:
data->enc_diff = (int16_t)lv_monkey_random(monkey->config.input_range.min, monkey->config.input_range.max);
break;
case LV_INDEV_TYPE_BUTTON:
data->btn_id = (uint32_t)lv_monkey_random(monkey->config.input_range.min, monkey->config.input_range.max);
break;
case LV_INDEV_TYPE_KEYPAD: {
int32_t index = lv_monkey_random(0, sizeof(lv_key_map) / sizeof(lv_key_map[0]) - 1);
data->key = lv_key_map[index];
break;
}
default:
break;
}
data->state = lv_monkey_random(0, 100) < 50 ? LV_INDEV_STATE_RELEASED : LV_INDEV_STATE_PRESSED;
lv_timer_set_period(monkey->timer, lv_monkey_random(monkey->config.period_range.min, monkey->config.period_range.max));
}
#endif /*LV_USE_MONKEY*/

View File

@@ -0,0 +1,118 @@
/**
* @file lv_monkey.h
*
*/
#ifndef LV_MONKEY_H
#define LV_MONKEY_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../lvgl.h"
#if LV_USE_MONKEY != 0
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
struct _lv_monkey;
typedef struct _lv_monkey lv_monkey_t;
typedef struct {
/**< Input device type*/
lv_indev_type_t type;
/**< Monkey execution period*/
struct {
uint32_t min;
uint32_t max;
} period_range;
/**< The range of input value*/
struct {
int32_t min;
int32_t max;
} input_range;
} lv_monkey_config_t;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Initialize a monkey config with default values
* @param config pointer to 'lv_monkey_config_t' variable to initialize
*/
void lv_monkey_config_init(lv_monkey_config_t * config);
/**
* Create monkey for test
* @param config pointer to 'lv_monkey_config_t' variable
* @return pointer to the created monkey
*/
lv_monkey_t * lv_monkey_create(const lv_monkey_config_t * config);
/**
* Get monkey input device
* @param monkey pointer to a monkey
* @return pointer to the input device
*/
lv_indev_t * lv_monkey_get_indev(lv_monkey_t * monkey);
/**
* Enable monkey
* @param monkey pointer to a monkey
* @param en set to true to enable
*/
void lv_monkey_set_enable(lv_monkey_t * monkey, bool en);
/**
* Get whether monkey is enabled
* @param monkey pointer to a monkey
* @return return true if monkey enabled
*/
bool lv_monkey_get_enable(lv_monkey_t * monkey);
#if LV_USE_USER_DATA
/**
* Set the user_data field of the monkey
* @param monkey pointer to a monkey
* @param user_data pointer to the new user_data.
*/
void lv_monkey_set_user_data(lv_monkey_t * monkey, void * user_data);
/**
* Get the user_data field of the monkey
* @param monkey pointer to a monkey
* @return the pointer to the user_data of the monkey
*/
void * lv_monkey_get_user_data(lv_monkey_t * monkey);
#endif/*LV_USE_USER_DATA*/
/**
* Delete monkey
* @param monkey pointer to monkey
*/
void lv_monkey_del(lv_monkey_t * monkey);
/**********************
* MACROS
**********************/
#endif /*LV_USE_MONKEY*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_MONKEY_H*/

View File

@@ -0,0 +1,172 @@
/**
* @file lv_msg.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_msg.h"
#if LV_USE_MSG
#include "../../../misc/lv_assert.h"
#include "../../../misc/lv_ll.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct {
uint32_t msg_id;
lv_msg_subscribe_cb_t callback;
void * user_data;
void * _priv_data; /*Internal: used only store 'obj' in lv_obj_subscribe*/
} sub_dsc_t;
/**********************
* STATIC PROTOTYPES
**********************/
static void notify(lv_msg_t * m);
static void obj_notify_cb(void * s, lv_msg_t * m);
static void obj_delete_event_cb(lv_event_t * e);
/**********************
* STATIC VARIABLES
**********************/
static lv_ll_t subs_ll;
/**********************
* GLOBAL VARIABLES
**********************/
lv_event_code_t LV_EVENT_MSG_RECEIVED;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_msg_init(void)
{
LV_EVENT_MSG_RECEIVED = lv_event_register_id();
_lv_ll_init(&subs_ll, sizeof(sub_dsc_t));
}
void * lv_msg_subsribe(uint32_t msg_id, lv_msg_subscribe_cb_t cb, void * user_data)
{
sub_dsc_t * s = _lv_ll_ins_tail(&subs_ll);
LV_ASSERT_MALLOC(s);
if(s == NULL) return NULL;
lv_memset_00(s, sizeof(*s));
s->msg_id = msg_id;
s->callback = cb;
s->user_data = user_data;
return s;
}
void * lv_msg_subsribe_obj(uint32_t msg_id, lv_obj_t * obj, void * user_data)
{
sub_dsc_t * s = lv_msg_subsribe(msg_id, obj_notify_cb, user_data);
if(s == NULL) return NULL;
s->_priv_data = obj;
/*If not added yet, add a delete event cb which automatically unsubcribes the object*/
sub_dsc_t * s_first = lv_obj_get_event_user_data(obj, obj_delete_event_cb);
if(s_first == NULL) {
lv_obj_add_event_cb(obj, obj_delete_event_cb, LV_EVENT_DELETE, s);
}
return s;
}
void lv_msg_unsubscribe(void * s)
{
LV_ASSERT_NULL(s);
_lv_ll_remove(&subs_ll, s);
lv_mem_free(s);
}
void lv_msg_send(uint32_t msg_id, const void * payload)
{
lv_msg_t m;
lv_memset_00(&m, sizeof(m));
m.id = msg_id;
m.payload = payload;
notify(&m);
}
uint32_t lv_msg_get_id(lv_msg_t * m)
{
return m->id;
}
const void * lv_msg_get_payload(lv_msg_t * m)
{
return m->payload;
}
void * lv_msg_get_user_data(lv_msg_t * m)
{
return m->user_data;
}
lv_msg_t * lv_event_get_msg(lv_event_t * e)
{
if(e->code == LV_EVENT_MSG_RECEIVED) {
return lv_event_get_param(e);
}
else {
LV_LOG_WARN("Not interpreted with this event code");
return NULL;
}
}
/**********************
* STATIC FUNCTIONS
**********************/
static void notify(lv_msg_t * m)
{
sub_dsc_t * s;
_LV_LL_READ(&subs_ll, s) {
if(s->msg_id == m->id && s->callback) {
m->user_data = s->user_data;
m->_priv_data = s->_priv_data;
s->callback(s, m);
}
}
}
static void obj_notify_cb(void * s, lv_msg_t * m)
{
LV_UNUSED(s);
lv_event_send(m->_priv_data, LV_EVENT_MSG_RECEIVED, m);
}
static void obj_delete_event_cb(lv_event_t * e)
{
lv_obj_t * obj = lv_event_get_target(e);
sub_dsc_t * s = _lv_ll_get_head(&subs_ll);
sub_dsc_t * s_next;
while(s) {
/*On unsubscribe the list changes s becomes invalid so get next item while it's surely valid*/
s_next = _lv_ll_get_next(&subs_ll, s);
if(s->_priv_data == obj) {
lv_msg_unsubscribe(s);
}
s = s_next;
}
}
#endif /*LV_USE_MSG*/

View File

@@ -0,0 +1,124 @@
/**
* @file lv_msg.h
*
*/
#ifndef LV_MSG_H
#define LV_MSG_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../core/lv_obj.h"
#if LV_USE_MSG
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct {
uint32_t id; /*Identifier of the message*/
void * user_data; /*Set the the user_data set in `lv_msg_subscribe`*/
void * _priv_data; /*Used internally*/
const void * payload; /*Pointer to the data of the message*/
} lv_msg_t;
typedef void (*lv_msg_subscribe_cb_t)(void * s, lv_msg_t * msg);
typedef void (*lv_msg_request_cb_t)(void * r, uint32_t msg_id);
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Called internally to initialize the message module
*/
void lv_msg_init(void);
/**
* Subscribe to an `msg_id`
* @param msg_id the message ID to listen to
* @param cb callback to call if a message with `msg_id` was sent
* @param user_data arbitrary data which will be available in `cb` too
* @return pointer to a "subscribe object". It can be used the unsubscribe.
*/
void * lv_msg_subsribe(uint32_t msg_id, lv_msg_subscribe_cb_t cb, void * user_data);
/**
* Subscribe an `lv_obj` to a message.
* `LV_EVENT_MSG_RECEIVED` will be triggered if a message with matching ID was sent
* @param msg_id the message ID to listen to
* @param obj pointer to an `lv_obj`
* @param user_data arbitrary data which will be available in `cb` too
* @return pointer to a "subscribe object". It can be used the unsubscribe.
*/
void * lv_msg_subsribe_obj(uint32_t msg_id, lv_obj_t * obj, void * user_data);
/**
* Cancel a previous subscription
* @param s pointer to a "subscibe object".
* Return value of `lv_msg_subsribe` or `lv_msg_subsribe_obj`
*/
void lv_msg_unsubscribe(void * s);
/**
* Send a message with a given ID and payload
* @param msg_id ID of the message to send
* @param data pointer to the data to send
*/
void lv_msg_send(uint32_t msg_id, const void * payload);
/**
* Get the ID of a message object. Typically used in the subscriber callback.
* @param m pointer to a message object
* @return the ID of the message
*/
uint32_t lv_msg_get_id(lv_msg_t * m);
/**
* Get the payload of a message object. Typically used in the subscriber callback.
* @param m pointer to a message object
* @return the payload of the message
*/
const void * lv_msg_get_payload(lv_msg_t * m);
/**
* Get the user data of a message object. Typically used in the subscriber callback.
* @param m pointer to a message object
* @return the user data of the message
*/
void * lv_msg_get_user_data(lv_msg_t * m);
/**
* Get the message object from an event object. Can be used in `LV_EVENT_MSG_RECEIVED` events.
* @param e pointer to an event object
* @return the message object or NULL if called with unrelated event code.
*/
lv_msg_t * lv_event_get_msg(lv_event_t * e);
/**********************
* GLOBAL VARIABLES
**********************/
extern lv_event_code_t LV_EVENT_MSG_RECEIVED;
/**********************
* MACROS
**********************/
#endif /*LV_USE_MSG*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_MSG_H*/

View File

@@ -0,0 +1,213 @@
/**
* @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)
{
LV_ASSERT_NULL(obj);
switch(cf) {
case LV_IMG_CF_TRUE_COLOR:
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_NULL(obj);
LV_ASSERT_NULL(dsc);
LV_ASSERT_NULL(buf);
switch(cf) {
case LV_IMG_CF_TRUE_COLOR:
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;
lv_area_t snapshot_area;
lv_obj_get_coords(obj, &snapshot_area);
lv_area_increase(&snapshot_area, ext_size, ext_size);
lv_memset(buf, 0x00, buff_size);
lv_memset_00(dsc, sizeof(lv_img_dsc_t));
lv_disp_t * obj_disp = lv_obj_get_disp(obj);
lv_disp_drv_t driver;
lv_disp_drv_init(&driver);
/*In lack of a better idea use the resolution of the object's display*/
driver.hor_res = lv_disp_get_hor_res(obj_disp);
driver.ver_res = lv_disp_get_hor_res(obj_disp);
lv_disp_drv_use_generic_set_px_cb(&driver, cf);
lv_disp_t fake_disp;
lv_memset_00(&fake_disp, sizeof(lv_disp_t));
fake_disp.driver = &driver;
lv_draw_ctx_t * draw_ctx = lv_mem_alloc(obj_disp->driver->draw_ctx_size);
LV_ASSERT_MALLOC(draw_ctx);
if(draw_ctx == NULL) return LV_RES_INV;
obj_disp->driver->draw_ctx_init(fake_disp.driver, draw_ctx);
fake_disp.driver->draw_ctx = draw_ctx;
draw_ctx->clip_area = &snapshot_area;
draw_ctx->buf_area = &snapshot_area;
draw_ctx->buf = (void *)buf;
driver.draw_ctx = draw_ctx;
lv_disp_t * refr_ori = _lv_refr_get_disp_refreshing();
_lv_refr_set_disp_refreshing(&fake_disp);
lv_obj_redraw(draw_ctx, obj);
_lv_refr_set_disp_refreshing(refr_ori);
obj_disp->driver->draw_ctx_deinit(fake_disp.driver, draw_ctx);
lv_mem_free(draw_ctx);
dsc->data = buf;
dsc->header.w = w;
dsc->header.h = h;
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 an image descriptor, or NULL if failed.
*/
lv_img_dsc_t * lv_snapshot_take(lv_obj_t * obj, lv_img_cf_t cf)
{
LV_ASSERT_NULL(obj);
uint32_t buff_size = lv_snapshot_buf_size_needed(obj, cf);
void * buf = lv_mem_alloc(buff_size);
LV_ASSERT_MALLOC(buf);
if(buf == NULL) {
return NULL;
}
lv_img_dsc_t * dsc = lv_mem_alloc(sizeof(lv_img_dsc_t));
LV_ASSERT_MALLOC(buf);
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 an 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,428 @@
/**
* @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_SCR lv_palette_lighten(LV_PALETTE_GREY, 4)
#define COLOR_WHITE lv_color_white()
#define COLOR_LIGHT lv_palette_lighten(LV_PALETTE_GREY, 2)
#define COLOR_DARK lv_palette_main(LV_PALETTE_GREY)
#define COLOR_DIM lv_palette_darken(LV_PALETTE_GREY, 2)
#define SCROLLBAR_WIDTH 2
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_style_t scr;
lv_style_t transp;
lv_style_t white;
lv_style_t light;
lv_style_t dark;
lv_style_t dim;
lv_style_t scrollbar;
#if LV_USE_ARC || LV_USE_COLORWHEEL
lv_style_t arc_line;
lv_style_t arc_knob;
#endif
#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(void)
{
style_init_reset(&styles->scrollbar);
lv_style_set_bg_opa(&styles->scrollbar, LV_OPA_COVER);
lv_style_set_bg_color(&styles->scrollbar, COLOR_DARK);
lv_style_set_width(&styles->scrollbar, SCROLLBAR_WIDTH);
style_init_reset(&styles->scr);
lv_style_set_bg_opa(&styles->scr, LV_OPA_COVER);
lv_style_set_bg_color(&styles->scr, COLOR_SCR);
lv_style_set_text_color(&styles->scr, COLOR_DIM);
style_init_reset(&styles->transp);
lv_style_set_bg_opa(&styles->transp, LV_OPA_TRANSP);
style_init_reset(&styles->white);
lv_style_set_bg_opa(&styles->white, LV_OPA_COVER);
lv_style_set_bg_color(&styles->white, COLOR_WHITE);
lv_style_set_line_width(&styles->white, 1);
lv_style_set_line_color(&styles->white, COLOR_WHITE);
lv_style_set_arc_width(&styles->white, 2);
lv_style_set_arc_color(&styles->white, COLOR_WHITE);
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_line_width(&styles->light, 1);
lv_style_set_line_color(&styles->light, COLOR_LIGHT);
lv_style_set_arc_width(&styles->light, 2);
lv_style_set_arc_color(&styles->light, COLOR_LIGHT);
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_line_width(&styles->dark, 1);
lv_style_set_line_color(&styles->dark, COLOR_DARK);
lv_style_set_arc_width(&styles->dark, 2);
lv_style_set_arc_color(&styles->dark, COLOR_DARK);
style_init_reset(&styles->dim);
lv_style_set_bg_opa(&styles->dim, LV_OPA_COVER);
lv_style_set_bg_color(&styles->dim, COLOR_DIM);
lv_style_set_line_width(&styles->dim, 1);
lv_style_set_line_color(&styles->dim, COLOR_DIM);
lv_style_set_arc_width(&styles->dim, 2);
lv_style_set_arc_color(&styles->dim, COLOR_DIM);
#if LV_USE_ARC || LV_USE_COLORWHEEL
style_init_reset(&styles->arc_line);
lv_style_set_arc_width(&styles->arc_line, 6);
style_init_reset(&styles->arc_knob);
lv_style_set_pad_all(&styles->arc_knob, 5);
#endif
#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
**********************/
bool lv_theme_basic_is_inited(void)
{
return LV_GC_ROOT(_lv_theme_basic_styles) == NULL ? false : true;
}
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(!lv_theme_basic_is_inited()) {
inited = false;
LV_GC_ROOT(_lv_theme_basic_styles) = lv_mem_alloc(sizeof(my_theme_styles_t));
styles = (my_theme_styles_t *)LV_GC_ROOT(_lv_theme_basic_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();
if(disp == NULL || lv_disp_get_theme(disp) == &theme) {
lv_obj_report_style_change(NULL);
}
inited = true;
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->scr, 0);
return;
}
/*Tabview pages*/
else if(lv_obj_check_type(lv_obj_get_parent(parent), &lv_tabview_class)) {
lv_obj_add_style(obj, &styles->scr, 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->white, 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);
}
#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);
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);
return;
}
#endif
lv_obj_add_style(obj, &styles->white, 0);
lv_obj_add_style(obj, &styles->light, LV_PART_ITEMS);
}
#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->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->dark, LV_PART_INDICATOR);
lv_obj_add_style(obj, &styles->dim, 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);
}
#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->dark, LV_PART_INDICATOR | LV_STATE_CHECKED);
}
#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->dim, LV_PART_KNOB);
}
#endif
#if LV_USE_CHART
else if(lv_obj_check_type(obj, &lv_chart_class)) {
lv_obj_add_style(obj, &styles->white, 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->dark, LV_PART_TICKS);
lv_obj_add_style(obj, &styles->dark, 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->white, 0);
}
else if(lv_obj_check_type(obj, &lv_dropdownlist_class)) {
lv_obj_add_style(obj, &styles->white, 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);
}
#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->transp, 0);
lv_obj_add_style(obj, &styles->arc_line, 0);
lv_obj_add_style(obj, &styles->dark, LV_PART_INDICATOR);
lv_obj_add_style(obj, &styles->arc_line, LV_PART_INDICATOR);
lv_obj_add_style(obj, &styles->dim, LV_PART_KNOB);
lv_obj_add_style(obj, &styles->arc_knob, LV_PART_KNOB);
}
#endif
#if LV_USE_SPINNER
else if(lv_obj_check_type(obj, &lv_spinner_class)) {
lv_obj_add_style(obj, &styles->light, 0);
lv_obj_add_style(obj, &styles->transp, 0);
lv_obj_add_style(obj, &styles->arc_line, 0);
lv_obj_add_style(obj, &styles->dark, LV_PART_INDICATOR);
lv_obj_add_style(obj, &styles->arc_line, LV_PART_INDICATOR);
}
#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->transp, 0);
lv_obj_add_style(obj, &styles->arc_line, 0);
lv_obj_add_style(obj, &styles->dim, LV_PART_KNOB);
lv_obj_add_style(obj, &styles->arc_knob, 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->white, 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);
}
#endif
#if LV_USE_CALENDAR
else if(lv_obj_check_type(obj, &lv_calendar_class)) {
lv_obj_add_style(obj, &styles->light, 0);
}
#endif
#if LV_USE_KEYBOARD
else if(lv_obj_check_type(obj, &lv_keyboard_class)) {
lv_obj_add_style(obj, &styles->scr, 0);
lv_obj_add_style(obj, &styles->white, LV_PART_ITEMS);
lv_obj_add_style(obj, &styles->light, LV_PART_ITEMS | LV_STATE_CHECKED);
}
#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);
}
#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,55 @@
/**
* @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);
/**
* Check if the theme is initialized
* @return true if default theme is initialized, false otherwise
*/
bool lv_theme_basic_is_inited(void);
/**********************
* MACROS
**********************/
#endif
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_THEME_BASIC_H*/

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,504 @@
/**
* @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
**********************/
bool lv_theme_mono_is_inited(void)
{
return LV_GC_ROOT(_lv_theme_default_styles) == NULL ? false : true;
}
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) {
inited = false;
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);
if(disp == NULL || lv_disp_get_theme(disp) == &theme) lv_obj_report_style_change(NULL);
inited = true;
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,57 @@
/**
* @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);
/**
* Check if the theme is initialized
* @return true if default theme is initialized, false otherwise
*/
bool lv_theme_mono_is_inited(void);
/**********************
* MACROS
**********************/
#endif
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_USE_THEME_MONO_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 parent pointer to an object, it will be the parent of the new button
* @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 obj 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
* @param count the number of times to repeat the animation
*/
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,402 @@
/**
* @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;
uint32_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 < 7) {
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 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 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_HEADER_ARROW_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_HEADER_DROPDOWN_H*/

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,460 @@
/**
* @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*/
#if LV_USE_LARGE_COORD
#define LV_CHART_POINT_NONE (INT32_MAX)
#else
#define LV_CHART_POINT_NONE (INT16_MAX)
#endif
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;
lv_coord_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];
lv_coord_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 object
* @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 to 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,713 @@
/**
* @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 parent 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);
lv_draw_ctx_t * draw_ctx = lv_event_get_draw_ctx(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(draw_ctx, &line_dsc, &p[0], &p[1]);
}
#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);
lv_draw_ctx_t * draw_ctx = lv_event_get_draw_ctx(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(draw_ctx, &cir_dsc, &knob_area);
}
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)
{
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;
static uint16_t angle_saved = 0xffff;
/*If the angle is different recalculate scaling*/
if(angle_saved != angle) m = 255;
angle_saved = angle;
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 object 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,377 @@
/**
* @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 an image button object
* @param parent 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 obj 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 obj 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 obj 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 obj 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;
lv_draw_ctx_t * draw_ctx = lv_event_get_draw_ctx(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(draw_ctx, &img_dsc, &coords_part, src);
}
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(draw_ctx, &img_dsc, &coords_part, src);
}
src = imgbtn->img_src_mid[state];
if(src) {
lv_area_t clip_area_center;
clip_area_center.x1 = coords.x1 + left_w;
clip_area_center.x2 = coords.x2 - right_w;
clip_area_center.y1 = coords.y1;
clip_area_center.y2 = coords.y2;
bool comm_res;
comm_res = _lv_area_intersect(&clip_area_center, &clip_area_center, draw_ctx->clip_area);
if(comm_res) {
lv_coord_t i;
lv_img_decoder_get_info(src, &header);
const lv_area_t * clip_area_ori = draw_ctx->clip_area;
draw_ctx->clip_area = &clip_area_center;
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_area_center.x2 + header.w - 1); i += header.w) {
lv_draw_img(draw_ctx, &img_dsc, &coords_part, src);
coords_part.x1 = coords_part.x2 + 1;
coords_part.x2 += header.w;
}
draw_ctx->clip_area = clip_area_ori;
}
}
}
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 an image button object
* @param parent 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,430 @@
/**
* @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 object
* @param parent 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_keyboard_update_ctrl_map(obj);
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_keyboard_update_ctrl_map(obj);
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_keyboard_update_ctrl_map(obj);
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,187 @@
/**
* @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 object
* @param parent 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);
}
/**
* Get the index of the lastly "activated" button by the user (pressed, released, focused etc)
* Useful in the `event_cb` to get the text of the button, check if hidden etc.
* @param obj pointer to button matrix object
* @return index of the last released button (LV_BTNMATRIX_BTN_NONE: if unset)
*/
static inline uint16_t lv_keyboard_get_selected_btn(const lv_obj_t * obj)
{
return lv_btnmatrix_get_selected_btn(obj);
}
/**
* Get the button's text
* @param obj pointer to button matrix object
* @param btn_id the index a button not counting new line characters.
* @return text of btn_index` button
*/
static inline const char * lv_keyboard_get_btn_text(const lv_obj_t * obj, uint16_t btn_id)
{
return lv_btnmatrix_get_btn_text(obj, btn_id);
}
/*=====================
* 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,221 @@
/**
* @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 object
* @param parent 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 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.stops[0].color = lv_color_mix(led->color, lv_color_black(),
lv_color_brightness(rect_dsc.bg_grad.stops[0].color));
rect_dsc.bg_grad.stops[1].color = lv_color_mix(led->color, lv_color_black(),
lv_color_brightness(rect_dsc.bg_grad.stops[1].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.stops[0].color = lv_color_mix(rect_dsc.bg_grad.stops[0].color, lv_color_black(), led->bright);
rect_dsc.bg_grad.stops[1].color = lv_color_mix(rect_dsc.bg_grad.stops[1].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);
lv_draw_ctx_t * draw_ctx = lv_event_get_draw_ctx(e);
lv_obj_draw_part_dsc_t part_draw_dsc;
lv_obj_draw_dsc_init(&part_draw_dsc, draw_ctx);
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(draw_ctx, &rect_dsc, &obj->coords);
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 object
* @param parent 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 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,120 @@
/**
* @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 void * 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 LV_USE_IMG == 1
if(icon) {
lv_obj_t * img = lv_img_create(obj);
lv_img_set_src(img, icon);
}
#endif
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 void * 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,56 @@
/**
* @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 "menu/lv_menu.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,767 @@
/**
* @file lv_menu.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_menu.h"
#if LV_USE_MENU
/*********************
* DEFINES
*********************/
#define MY_CLASS &lv_menu_class
#include "../../../core/lv_obj.h"
#include "../../layouts/flex/lv_flex.h"
#include "../../../widgets/lv_label.h"
#include "../../../widgets/lv_btn.h"
#include "../../../widgets/lv_img.h"
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_menu_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_menu_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_menu_page_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_menu_page_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_menu_cont_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void lv_menu_section_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
const lv_obj_class_t lv_menu_class = {
.constructor_cb = lv_menu_constructor,
.destructor_cb = lv_menu_destructor,
.base_class = &lv_obj_class,
.width_def = (LV_DPI_DEF * 3) / 2,
.height_def = LV_DPI_DEF * 2,
.instance_size = sizeof(lv_menu_t)
};
const lv_obj_class_t lv_menu_page_class = {
.constructor_cb = lv_menu_page_constructor,
.destructor_cb = lv_menu_page_destructor,
.base_class = &lv_obj_class,
.width_def = LV_PCT(100),
.height_def = LV_SIZE_CONTENT,
.instance_size = sizeof(lv_menu_page_t)
};
const lv_obj_class_t lv_menu_cont_class = {
.constructor_cb = lv_menu_cont_constructor,
.base_class = &lv_obj_class,
.width_def = LV_PCT(100),
.height_def = LV_SIZE_CONTENT
};
const lv_obj_class_t lv_menu_section_class = {
.constructor_cb = lv_menu_section_constructor,
.base_class = &lv_obj_class,
.width_def = LV_PCT(100),
.height_def = LV_SIZE_CONTENT
};
const lv_obj_class_t lv_menu_separator_class = {
.base_class = &lv_obj_class,
.width_def = LV_SIZE_CONTENT,
.height_def = LV_SIZE_CONTENT
};
const lv_obj_class_t lv_menu_sidebar_cont_class = {
.base_class = &lv_obj_class
};
const lv_obj_class_t lv_menu_main_cont_class = {
.base_class = &lv_obj_class
};
const lv_obj_class_t lv_menu_main_header_cont_class = {
.base_class = &lv_obj_class
};
const lv_obj_class_t lv_menu_sidebar_header_cont_class = {
.base_class = &lv_obj_class
};
static void lv_menu_refr(lv_obj_t * obj);
static void lv_menu_refr_sidebar_header_mode(lv_obj_t * obj);
static void lv_menu_refr_main_header_mode(lv_obj_t * obj);
static void lv_menu_load_page_event_cb(lv_event_t * e);
static void lv_menu_obj_del_event_cb(lv_event_t * e);
static void lv_menu_back_event_cb(lv_event_t * e);
static void lv_menu_value_changed_event_cb(lv_event_t * e);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
bool lv_menu_item_back_btn_is_root(lv_obj_t * menu, lv_obj_t * obj);
void lv_menu_clear_history(lv_obj_t * obj);
lv_obj_t * lv_menu_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;
}
lv_obj_t * lv_menu_page_create(lv_obj_t * parent, char * title)
{
LV_LOG_INFO("begin");
lv_obj_t * obj = lv_obj_class_create_obj(&lv_menu_page_class, parent);
lv_obj_class_init_obj(obj);
lv_menu_page_t * page = (lv_menu_page_t *)obj;
if(title) {
page->title = lv_mem_alloc(strlen(title) + 1);
LV_ASSERT_MALLOC(page->title);
if(page->title == NULL) return NULL;
strcpy(page->title, title);
}
else {
page->title = NULL;
}
return obj;
}
lv_obj_t * lv_menu_cont_create(lv_obj_t * parent)
{
LV_LOG_INFO("begin");
lv_obj_t * obj = lv_obj_class_create_obj(&lv_menu_cont_class, parent);
lv_obj_class_init_obj(obj);
return obj;
}
lv_obj_t * lv_menu_section_create(lv_obj_t * parent)
{
LV_LOG_INFO("begin");
lv_obj_t * obj = lv_obj_class_create_obj(&lv_menu_section_class, parent);
lv_obj_class_init_obj(obj);
return obj;
}
lv_obj_t * lv_menu_separator_create(lv_obj_t * parent)
{
LV_LOG_INFO("begin");
lv_obj_t * obj = lv_obj_class_create_obj(&lv_menu_separator_class, parent);
lv_obj_class_init_obj(obj);
return obj;
}
void lv_menu_refr(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_menu_t * menu = (lv_menu_t *)obj;
lv_ll_t * history_ll = &(menu->history_ll);
/* The current menu */
lv_menu_history_t * act_hist = _lv_ll_get_head(history_ll);
lv_obj_t * page = NULL;
if(act_hist != NULL) {
page = act_hist->page;
/* Delete the current item from the history */
_lv_ll_remove(history_ll, act_hist);
lv_mem_free(act_hist);
menu->cur_depth--;
}
/* Set it */
lv_menu_set_page(obj, page);
}
/*=====================
* Setter functions
*====================*/
void lv_menu_set_page(lv_obj_t * obj, lv_obj_t * page)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_menu_t * menu = (lv_menu_t *)obj;
/* Hide previous page */
if(menu->main_page != NULL) {
lv_obj_set_parent(menu->main_page, menu->storage);
}
if(page != NULL) {
/* Add a new node */
lv_ll_t * history_ll = &(menu->history_ll);
lv_menu_history_t * new_node = _lv_ll_ins_head(history_ll);
LV_ASSERT_MALLOC(new_node);
new_node->page = page;
menu->cur_depth++;
/* Place page in main */
lv_obj_set_parent(page, menu->main);
}
else {
/* Empty page, clear history */
lv_menu_clear_history(obj);
}
menu->main_page = page;
/* If there is a selected tab, update checked state */
if(menu->selected_tab != NULL) {
if(menu->sidebar_page != NULL) {
lv_obj_add_state(menu->selected_tab, LV_STATE_CHECKED);
}
else {
lv_obj_clear_state(menu->selected_tab, LV_STATE_CHECKED);
}
}
/* Back btn management */
if(menu->sidebar_page != NULL) {
/* With sidebar enabled */
if(menu->sidebar_generated) {
if(menu->mode_root_back_btn == LV_MENU_ROOT_BACK_BTN_ENABLED) {
/* Root back btn is always shown if enabled*/
lv_obj_clear_flag(menu->sidebar_header_back_btn, LV_OBJ_FLAG_HIDDEN);
lv_obj_add_flag(menu->sidebar_header_back_btn, LV_OBJ_FLAG_CLICKABLE);
}
else {
lv_obj_add_flag(menu->sidebar_header_back_btn, LV_OBJ_FLAG_HIDDEN);
lv_obj_clear_flag(menu->sidebar_header_back_btn, LV_OBJ_FLAG_CLICKABLE);
}
}
if(menu->cur_depth >= 2) {
lv_obj_clear_flag(menu->main_header_back_btn, LV_OBJ_FLAG_HIDDEN);
lv_obj_add_flag(menu->main_header_back_btn, LV_OBJ_FLAG_CLICKABLE);
}
else {
lv_obj_add_flag(menu->main_header_back_btn, LV_OBJ_FLAG_HIDDEN);
lv_obj_clear_flag(menu->main_header_back_btn, LV_OBJ_FLAG_CLICKABLE);
}
}
else {
/* With sidebar disabled */
if(menu->cur_depth >= 2 || menu->mode_root_back_btn == LV_MENU_ROOT_BACK_BTN_ENABLED) {
lv_obj_clear_flag(menu->main_header_back_btn, LV_OBJ_FLAG_HIDDEN);
lv_obj_add_flag(menu->main_header_back_btn, LV_OBJ_FLAG_CLICKABLE);
}
else {
lv_obj_add_flag(menu->main_header_back_btn, LV_OBJ_FLAG_HIDDEN);
lv_obj_clear_flag(menu->main_header_back_btn, LV_OBJ_FLAG_CLICKABLE);
}
}
lv_event_send((lv_obj_t *)menu, LV_EVENT_VALUE_CHANGED, NULL);
lv_menu_refr_main_header_mode(obj);
}
void lv_menu_set_sidebar_page(lv_obj_t * obj, lv_obj_t * page)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_menu_t * menu = (lv_menu_t *)obj;
/* Sidebar management*/
if(page != NULL) {
/* Sidebar should be enabled */
if(!menu->sidebar_generated) {
/* Create sidebar */
lv_obj_t * sidebar_cont = lv_obj_class_create_obj(&lv_menu_sidebar_cont_class, obj);
lv_obj_class_init_obj(sidebar_cont);
lv_obj_move_to_index(sidebar_cont, 1);
lv_obj_set_size(sidebar_cont, LV_PCT(30), LV_PCT(100));
lv_obj_set_flex_flow(sidebar_cont, LV_FLEX_FLOW_COLUMN);
lv_obj_add_flag(sidebar_cont, LV_OBJ_FLAG_EVENT_BUBBLE);
lv_obj_clear_flag(sidebar_cont, LV_OBJ_FLAG_CLICKABLE);
menu->sidebar = sidebar_cont;
lv_obj_t * sidebar_header = lv_obj_class_create_obj(&lv_menu_sidebar_header_cont_class, sidebar_cont);
lv_obj_class_init_obj(sidebar_header);
lv_obj_set_size(sidebar_header, LV_PCT(100), LV_SIZE_CONTENT);
lv_obj_set_flex_flow(sidebar_header, LV_FLEX_FLOW_ROW);
lv_obj_set_flex_align(sidebar_header, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
lv_obj_clear_flag(sidebar_header, LV_OBJ_FLAG_CLICKABLE);
lv_obj_add_flag(sidebar_header, LV_OBJ_FLAG_EVENT_BUBBLE);
menu->sidebar_header = sidebar_header;
lv_obj_t * sidebar_header_back_btn = lv_btn_create(menu->sidebar_header);
lv_obj_add_event_cb(sidebar_header_back_btn, lv_menu_back_event_cb, LV_EVENT_CLICKED, menu);
lv_obj_add_flag(sidebar_header_back_btn, LV_OBJ_FLAG_EVENT_BUBBLE);
lv_obj_set_flex_flow(sidebar_header_back_btn, LV_FLEX_FLOW_ROW);
menu->sidebar_header_back_btn = sidebar_header_back_btn;
lv_obj_t * sidebar_header_back_icon = lv_img_create(menu->sidebar_header_back_btn);
lv_img_set_src(sidebar_header_back_icon, LV_SYMBOL_LEFT);
lv_obj_t * sidebar_header_title = lv_label_create(menu->sidebar_header);
lv_obj_add_flag(sidebar_header_title, LV_OBJ_FLAG_HIDDEN);
menu->sidebar_header_title = sidebar_header_title;
menu->sidebar_generated = true;
}
lv_obj_set_parent(page, menu->sidebar);
lv_menu_refr_sidebar_header_mode(obj);
}
else {
/* Sidebar should be disabled */
if(menu->sidebar_generated) {
lv_obj_set_parent(menu->sidebar_page, menu->storage);
lv_obj_del(menu->sidebar);
menu->sidebar_generated = false;
}
}
menu->sidebar_page = page;
lv_menu_refr(obj);
}
void lv_menu_set_mode_header(lv_obj_t * obj, lv_menu_mode_header_t mode_header)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_menu_t * menu = (lv_menu_t *)obj;
if(menu->mode_header != mode_header) {
menu->mode_header = mode_header;
lv_menu_refr_main_header_mode(obj);
if(menu->sidebar_generated) lv_menu_refr_sidebar_header_mode(obj);
}
}
void lv_menu_set_mode_root_back_btn(lv_obj_t * obj, lv_menu_mode_root_back_btn_t mode_root_back_btn)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_menu_t * menu = (lv_menu_t *)obj;
if(menu->mode_root_back_btn != mode_root_back_btn) {
menu->mode_root_back_btn = mode_root_back_btn;
lv_menu_refr(obj);
}
}
void lv_menu_set_load_page_event(lv_obj_t * menu, lv_obj_t * obj, lv_obj_t * page)
{
LV_ASSERT_OBJ(menu, MY_CLASS);
lv_obj_add_flag(obj, LV_OBJ_FLAG_CLICKABLE);
lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_add_flag(obj, LV_OBJ_FLAG_SCROLL_ON_FOCUS);
/* Remove old event */
if(lv_obj_remove_event_cb(obj, lv_menu_load_page_event_cb)) {
lv_event_send(obj, LV_EVENT_DELETE, NULL);
lv_obj_remove_event_cb(obj, lv_menu_obj_del_event_cb);
}
lv_menu_load_page_event_data_t * event_data = lv_mem_alloc(sizeof(lv_menu_load_page_event_data_t));
event_data->menu = menu;
event_data->page = page;
lv_obj_add_event_cb(obj, lv_menu_load_page_event_cb, LV_EVENT_CLICKED, event_data);
lv_obj_add_event_cb(obj, lv_menu_obj_del_event_cb, LV_EVENT_DELETE, event_data);
}
/*=====================
* Getter functions
*====================*/
lv_obj_t * lv_menu_get_cur_main_page(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_menu_t * menu = (lv_menu_t *)obj;
return menu->main_page;
}
lv_obj_t * lv_menu_get_cur_sidebar_page(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_menu_t * menu = (lv_menu_t *)obj;
return menu->sidebar_page;
}
lv_obj_t * lv_menu_get_main_header(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_menu_t * menu = (lv_menu_t *)obj;
return menu->main_header;
}
lv_obj_t * lv_menu_get_main_header_back_btn(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_menu_t * menu = (lv_menu_t *)obj;
return menu->main_header_back_btn;
}
lv_obj_t * lv_menu_get_sidebar_header(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_menu_t * menu = (lv_menu_t *)obj;
return menu->sidebar_header;
}
lv_obj_t * lv_menu_get_sidebar_header_back_btn(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_menu_t * menu = (lv_menu_t *)obj;
return menu->sidebar_header_back_btn;
}
bool lv_menu_back_btn_is_root(lv_obj_t * menu, lv_obj_t * obj)
{
LV_ASSERT_OBJ(menu, MY_CLASS);
if(obj == ((lv_menu_t *)menu)->sidebar_header_back_btn) {
return true;
}
if(obj == ((lv_menu_t *)menu)->main_header_back_btn && ((lv_menu_t *)menu)->prev_depth <= 1) {
return true;
}
return false;
}
void lv_menu_clear_history(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_menu_t * menu = (lv_menu_t *)obj;
lv_ll_t * history_ll = &(menu->history_ll);
_lv_ll_clear(history_ll);
menu->cur_depth = 0;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_menu_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
LV_TRACE_OBJ_CREATE("begin");
lv_obj_set_layout(obj, LV_LAYOUT_FLEX);
lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_ROW);
lv_menu_t * menu = (lv_menu_t *)obj;
menu->mode_header = LV_MENU_HEADER_TOP_FIXED;
menu->mode_root_back_btn = LV_MENU_ROOT_BACK_BTN_DISABLED;
menu->cur_depth = 0;
menu->prev_depth = 0;
menu->sidebar_generated = false;
_lv_ll_init(&(menu->history_ll), sizeof(lv_menu_history_t));
menu->storage = lv_obj_create(obj);
lv_obj_add_flag(menu->storage, LV_OBJ_FLAG_HIDDEN);
menu->sidebar = NULL;
menu->sidebar_header = NULL;
menu->sidebar_header_back_btn = NULL;
menu->sidebar_header_title = NULL;
menu->sidebar_page = NULL;
lv_obj_t * main_cont = lv_obj_class_create_obj(&lv_menu_main_cont_class, obj);
lv_obj_class_init_obj(main_cont);
lv_obj_set_height(main_cont, LV_PCT(100));
lv_obj_set_flex_grow(main_cont, 1);
lv_obj_set_flex_flow(main_cont, LV_FLEX_FLOW_COLUMN);
lv_obj_add_flag(main_cont, LV_OBJ_FLAG_EVENT_BUBBLE);
lv_obj_clear_flag(main_cont, LV_OBJ_FLAG_CLICKABLE);
menu->main = main_cont;
lv_obj_t * main_header = lv_obj_class_create_obj(&lv_menu_main_header_cont_class, main_cont);
lv_obj_class_init_obj(main_header);
lv_obj_set_size(main_header, LV_PCT(100), LV_SIZE_CONTENT);
lv_obj_set_flex_flow(main_header, LV_FLEX_FLOW_ROW);
lv_obj_set_flex_align(main_header, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
lv_obj_clear_flag(main_header, LV_OBJ_FLAG_CLICKABLE);
lv_obj_add_flag(main_header, LV_OBJ_FLAG_EVENT_BUBBLE);
menu->main_header = main_header;
/* Create the default simple back btn and title */
lv_obj_t * main_header_back_btn = lv_btn_create(menu->main_header);
lv_obj_add_event_cb(main_header_back_btn, lv_menu_back_event_cb, LV_EVENT_CLICKED, menu);
lv_obj_add_flag(main_header_back_btn, LV_OBJ_FLAG_EVENT_BUBBLE);
lv_obj_set_flex_flow(main_header_back_btn, LV_FLEX_FLOW_ROW);
menu->main_header_back_btn = main_header_back_btn;
lv_obj_t * main_header_back_icon = lv_img_create(menu->main_header_back_btn);
lv_img_set_src(main_header_back_icon, LV_SYMBOL_LEFT);
lv_obj_t * main_header_title = lv_label_create(menu->main_header);
lv_obj_add_flag(main_header_title, LV_OBJ_FLAG_HIDDEN);
menu->main_header_title = main_header_title;
menu->main_page = NULL;
menu->selected_tab = NULL;
lv_obj_add_event_cb(obj, lv_menu_value_changed_event_cb, LV_EVENT_VALUE_CHANGED, menu);
LV_TRACE_OBJ_CREATE("finished");
}
static void lv_menu_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
LV_TRACE_OBJ_CREATE("begin");
lv_menu_t * menu = (lv_menu_t *)obj;
lv_ll_t * history_ll = &(menu->history_ll);
_lv_ll_clear(history_ll);
LV_TRACE_OBJ_CREATE("finished");
}
static void lv_menu_page_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
lv_menu_t * menu = (lv_menu_t *)lv_obj_get_parent(obj);
lv_obj_set_parent(obj, ((lv_menu_t *)menu)->storage);
lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_COLUMN);
lv_obj_set_flex_align(obj, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
lv_obj_add_flag(obj, LV_OBJ_FLAG_EVENT_BUBBLE);
}
static void lv_menu_page_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
lv_menu_page_t * page = (lv_menu_page_t *)obj;
if(page->title != NULL) {
lv_mem_free(page->title);
page->title = NULL;
}
}
static void lv_menu_cont_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
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_CENTER);
lv_obj_clear_flag(obj, LV_OBJ_FLAG_CLICKABLE);
}
static void lv_menu_section_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_COLUMN);
lv_obj_clear_flag(obj, LV_OBJ_FLAG_CLICKABLE);
}
static void lv_menu_refr_sidebar_header_mode(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_menu_t * menu = (lv_menu_t *)obj;
if(menu->sidebar_header == NULL || menu->sidebar_page == NULL) return;
switch(menu->mode_header) {
case LV_MENU_HEADER_TOP_FIXED:
/* Content should fill the remaining space */
lv_obj_move_to_index(menu->sidebar_header, 0);
lv_obj_set_flex_grow(menu->sidebar_page, 1);
break;
case LV_MENU_HEADER_TOP_UNFIXED:
lv_obj_move_to_index(menu->sidebar_header, 0);
lv_obj_set_flex_grow(menu->sidebar_page, 0);
break;
case LV_MENU_HEADER_BOTTOM_FIXED:
lv_obj_move_to_index(menu->sidebar_header, 1);
lv_obj_set_flex_grow(menu->sidebar_page, 1);
break;
}
lv_obj_refr_size(menu->sidebar_header);
lv_obj_refr_size(menu->sidebar_page);
if(lv_obj_get_content_height(menu->sidebar_header) == 0) {
lv_obj_add_flag(menu->sidebar_header, LV_OBJ_FLAG_HIDDEN);
}
else {
lv_obj_clear_flag(menu->sidebar_header, LV_OBJ_FLAG_HIDDEN);
}
}
static void lv_menu_refr_main_header_mode(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_menu_t * menu = (lv_menu_t *)obj;
if(menu->main_header == NULL || menu->main_page == NULL) return;
switch(menu->mode_header) {
case LV_MENU_HEADER_TOP_FIXED:
/* Content should fill the remaining space */
lv_obj_move_to_index(menu->main_header, 0);
lv_obj_set_flex_grow(menu->main_page, 1);
break;
case LV_MENU_HEADER_TOP_UNFIXED:
lv_obj_move_to_index(menu->main_header, 0);
lv_obj_set_flex_grow(menu->main_page, 0);
break;
case LV_MENU_HEADER_BOTTOM_FIXED:
lv_obj_move_to_index(menu->main_header, 1);
lv_obj_set_flex_grow(menu->main_page, 1);
break;
}
lv_obj_refr_size(menu->main_header);
lv_obj_refr_size(menu->main_page);
lv_obj_update_layout(menu->main_header);
if(lv_obj_get_content_height(menu->main_header) == 0) {
lv_obj_add_flag(menu->main_header, LV_OBJ_FLAG_HIDDEN);
}
else {
lv_obj_clear_flag(menu->main_header, LV_OBJ_FLAG_HIDDEN);
}
}
static void lv_menu_load_page_event_cb(lv_event_t * e)
{
lv_obj_t * obj = lv_event_get_target(e);
lv_menu_load_page_event_data_t * event_data = lv_event_get_user_data(e);
lv_menu_t * menu = (lv_menu_t *)(event_data->menu);
lv_obj_t * page = event_data->page;
if(menu->sidebar_page != NULL) {
/* Check if clicked obj is in the sidebar */
bool sidebar = false;
lv_obj_t * parent = obj;
while(parent) {
if(parent == (lv_obj_t *)menu) break;
if(parent == menu->sidebar) {
sidebar = true;
break;
}
parent = lv_obj_get_parent(parent);
}
if(sidebar) {
/* Clear checked state of previous obj */
if(menu->selected_tab != obj && menu->selected_tab != NULL) {
lv_obj_clear_state(menu->selected_tab, LV_STATE_CHECKED);
}
lv_menu_clear_history((lv_obj_t *)menu);
menu->selected_tab = obj;
}
}
lv_menu_set_page((lv_obj_t *)menu, page);
if(lv_group_get_default() != NULL && menu->sidebar_page == NULL) {
/* Sidebar is not supported for now*/
lv_group_focus_next(lv_group_get_default());
}
}
static void lv_menu_obj_del_event_cb(lv_event_t * e)
{
lv_menu_load_page_event_data_t * event_data = lv_event_get_user_data(e);
lv_mem_free(event_data);
}
static void lv_menu_back_event_cb(lv_event_t * e)
{
lv_event_code_t code = lv_event_get_code(e);
/* LV_EVENT_CLICKED */
if(code == LV_EVENT_CLICKED) {
lv_obj_t * obj = lv_event_get_target(e);
lv_menu_t * menu = (lv_menu_t *)lv_event_get_user_data(e);
if(!(obj == menu->main_header_back_btn || obj == menu->sidebar_header_back_btn)) return;
menu->prev_depth = menu->cur_depth; /* Save the previous value for user event handler */
if(lv_menu_back_btn_is_root((lv_obj_t *)menu, obj)) return;
lv_ll_t * history_ll = &(menu->history_ll);
/* The current menu */
lv_menu_history_t * act_hist = _lv_ll_get_head(history_ll);
/* The previous menu */
lv_menu_history_t * prev_hist = _lv_ll_get_next(history_ll, act_hist);
if(prev_hist != NULL) {
/* Previous menu exists */
/* Delete the current item from the history */
_lv_ll_remove(history_ll, act_hist);
lv_mem_free(act_hist);
menu->cur_depth--;
/* Create the previous menu.
* Remove it from the history because `lv_menu_set_page` will add it again */
_lv_ll_remove(history_ll, prev_hist);
menu->cur_depth--;
lv_menu_set_page(&(menu->obj), prev_hist->page);
lv_mem_free(prev_hist);
}
}
}
static void lv_menu_value_changed_event_cb(lv_event_t * e)
{
lv_obj_t * obj = lv_event_get_user_data(e);
lv_menu_t * menu = (lv_menu_t *)obj;
lv_menu_page_t * main_page = (lv_menu_page_t *)lv_menu_get_cur_main_page(obj);
if(main_page != NULL && menu->main_header_title != NULL) {
if(main_page->title != NULL) {
lv_label_set_text(menu->main_header_title, main_page->title);
lv_obj_clear_flag(menu->main_header_title, LV_OBJ_FLAG_HIDDEN);
}
else {
lv_obj_add_flag(menu->main_header_title, LV_OBJ_FLAG_HIDDEN);
}
}
lv_menu_page_t * sidebar_page = (lv_menu_page_t *)lv_menu_get_cur_sidebar_page(obj);
if(sidebar_page != NULL && menu->sidebar_header_title != NULL) {
if(sidebar_page->title != NULL) {
lv_label_set_text(menu->sidebar_header_title, sidebar_page->title);
lv_obj_clear_flag(menu->sidebar_header_title, LV_OBJ_FLAG_HIDDEN);
}
else {
lv_obj_add_flag(menu->sidebar_header_title, LV_OBJ_FLAG_HIDDEN);
}
}
}
#endif /*LV_USE_MENU*/

View File

@@ -0,0 +1,233 @@
/**
* @file lv_menu.h
*
*/
#ifndef LV_MENU_H
#define LV_MENU_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../core/lv_obj.h"
#if LV_USE_MENU
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
enum {
LV_MENU_HEADER_TOP_FIXED, /* Header is positioned at the top */
LV_MENU_HEADER_TOP_UNFIXED, /* Header is positioned at the top and can be scrolled out of view*/
LV_MENU_HEADER_BOTTOM_FIXED /* Header is positioned at the bottom */
};
typedef uint8_t lv_menu_mode_header_t;
enum {
LV_MENU_ROOT_BACK_BTN_DISABLED,
LV_MENU_ROOT_BACK_BTN_ENABLED
};
typedef uint8_t lv_menu_mode_root_back_btn_t;
typedef struct lv_menu_load_page_event_data_t {
lv_obj_t * menu;
lv_obj_t * page;
} lv_menu_load_page_event_data_t;
typedef struct {
lv_obj_t * page;
} lv_menu_history_t;
typedef struct {
lv_obj_t obj;
lv_obj_t * storage; /* a pointer to obj that is the parent of all pages not displayed */
lv_obj_t * main;
lv_obj_t * main_page;
lv_obj_t * main_header;
lv_obj_t *
main_header_back_btn; /* a pointer to obj that on click triggers back btn event handler, can be same as 'main_header' */
lv_obj_t * main_header_title;
lv_obj_t * sidebar;
lv_obj_t * sidebar_page;
lv_obj_t * sidebar_header;
lv_obj_t *
sidebar_header_back_btn; /* a pointer to obj that on click triggers back btn event handler, can be same as 'sidebar_header' */
lv_obj_t * sidebar_header_title;
lv_obj_t * selected_tab;
lv_ll_t history_ll;
uint8_t cur_depth;
uint8_t prev_depth;
uint8_t sidebar_generated : 1;
lv_menu_mode_header_t mode_header : 2;
lv_menu_mode_root_back_btn_t mode_root_back_btn : 1;
} lv_menu_t;
typedef struct {
lv_obj_t obj;
char * title;
} lv_menu_page_t;
extern const lv_obj_class_t lv_menu_class;
extern const lv_obj_class_t lv_menu_page_class;
extern const lv_obj_class_t lv_menu_cont_class;
extern const lv_obj_class_t lv_menu_section_class;
extern const lv_obj_class_t lv_menu_separator_class;
extern const lv_obj_class_t lv_menu_sidebar_cont_class;
extern const lv_obj_class_t lv_menu_main_cont_class;
extern const lv_obj_class_t lv_menu_sidebar_header_cont_class;
extern const lv_obj_class_t lv_menu_main_header_cont_class;
/**********************
* GLOBAL PROTOTYPES
**********************/
/**
* Create a menu object
* @param parent pointer to an object, it will be the parent of the new menu
* @return pointer to the created menu
*/
lv_obj_t * lv_menu_create(lv_obj_t * parent);
/**
* Create a menu page object
* @param parent pointer to menu object
* @param title pointer to text for title in header (NULL to not display title)
* @return pointer to the created menu page
*/
lv_obj_t * lv_menu_page_create(lv_obj_t * parent, char * title);
/**
* Create a menu cont object
* @param parent pointer to an object, it will be the parent of the new menu cont object
* @return pointer to the created menu cont
*/
lv_obj_t * lv_menu_cont_create(lv_obj_t * parent);
/**
* Create a menu section object
* @param parent pointer to an object, it will be the parent of the new menu section object
* @return pointer to the created menu section
*/
lv_obj_t * lv_menu_section_create(lv_obj_t * parent);
/**
* Create a menu separator object
* @param parent pointer to an object, it will be the parent of the new menu separator object
* @return pointer to the created menu separator
*/
lv_obj_t * lv_menu_separator_create(lv_obj_t * parent);
/*=====================
* Setter functions
*====================*/
/**
* Set menu page to display in main
* @param obj pointer to the menu
* @param page pointer to the menu page to set (NULL to clear main and clear menu history)
*/
void lv_menu_set_page(lv_obj_t * obj, lv_obj_t * page);
/**
* Set menu page to display in sidebar
* @param obj pointer to the menu
* @param page pointer to the menu page to set (NULL to clear sidebar)
*/
void lv_menu_set_sidebar_page(lv_obj_t * obj, lv_obj_t * page);
/**
* Set the how the header should behave and its position
* @param obj pointer to a menu
* @param mode_header
*/
void lv_menu_set_mode_header(lv_obj_t * obj, lv_menu_mode_header_t mode_header);
/**
* Set whether back button should appear at root
* @param obj pointer to a menu
* @param mode_root_back_btn
*/
void lv_menu_set_mode_root_back_btn(lv_obj_t * obj, lv_menu_mode_root_back_btn_t mode_root_back_btn);
/**
* Add menu to the menu item
* @param menu pointer to the menu
* @param obj pointer to the obj
* @param page pointer to the page to load when obj is clicked
*/
void lv_menu_set_load_page_event(lv_obj_t * menu, lv_obj_t * obj, lv_obj_t * page);
/*=====================
* Getter functions
*====================*/
/**
* Get a pointer to menu page that is currently displayed in main
* @param obj pointer to the menu
* @return pointer to current page
*/
lv_obj_t * lv_menu_get_cur_main_page(lv_obj_t * obj);
/**
* Get a pointer to menu page that is currently displayed in sidebar
* @param obj pointer to the menu
* @return pointer to current page
*/
lv_obj_t * lv_menu_get_cur_sidebar_page(lv_obj_t * obj);
/**
* Get a pointer to main header obj
* @param obj pointer to the menu
* @return pointer to main header obj
*/
lv_obj_t * lv_menu_get_main_header(lv_obj_t * obj);
/**
* Get a pointer to main header back btn obj
* @param obj pointer to the menu
* @return pointer to main header back btn obj
*/
lv_obj_t * lv_menu_get_main_header_back_btn(lv_obj_t * obj);
/**
* Get a pointer to sidebar header obj
* @param obj pointer to the menu
* @return pointer to sidebar header obj
*/
lv_obj_t * lv_menu_get_sidebar_header(lv_obj_t * obj);
/**
* Get a pointer to sidebar header obj
* @param obj pointer to the menu
* @return pointer to sidebar header back btn obj
*/
lv_obj_t * lv_menu_get_sidebar_header_back_btn(lv_obj_t * obj);
/**
* Check if an obj is a root back btn
* @param menu pointer to the menu
* @return true if it is a root back btn
*/
bool lv_menu_back_btn_is_root(lv_obj_t * menu, lv_obj_t * obj);
/**
* Clear menu history
* @param obj pointer to the menu
*/
void lv_menu_clear_history(lv_obj_t * obj);
/**********************
* MACROS
**********************/
#endif /*LV_USE_MENU*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_MENU_H*/

View File

@@ -0,0 +1,697 @@
/**
* @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, lv_draw_ctx_t * draw_ctx, const lv_area_t * scale_area);
static void draw_ticks_and_labels(lv_obj_t * obj, lv_draw_ctx_t * draw_ctx, const lv_area_t * scale_area);
static void draw_needles(lv_obj_t * obj, lv_draw_ctx_t * draw_ctx, 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) {
lv_draw_ctx_t * draw_ctx = lv_event_get_draw_ctx(e);
lv_area_t scale_area;
lv_obj_get_content_coords(obj, &scale_area);
draw_arcs(obj, draw_ctx, &scale_area);
draw_ticks_and_labels(obj, draw_ctx, &scale_area);
draw_needles(obj, draw_ctx, &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(draw_ctx, &mid_dsc, &nm_cord);
}
}
static void draw_arcs(lv_obj_t * obj, lv_draw_ctx_t * draw_ctx, 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, draw_ctx);
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(draw_ctx, &arc_dsc, &scale_center, part_draw_dsc.radius, start_angle, end_angle);
lv_event_send(obj, LV_EVENT_DRAW_PART_END, &part_draw_dsc);
}
}
static void draw_ticks_and_labels(lv_obj_t * obj, lv_draw_ctx_t * draw_ctx, 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;
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, draw_ctx);
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;
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;
uint16_t i;
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);
}
}
}
int32_t angle_upscale = ((i * scale->angle_range) * 10) / (scale->tick_cnt - 1) + + scale->rotation * 10;
line_dsc.color = line_color;
line_dsc.width = line_width;
/*Draw a little bit longer lines to be sure the mask will clip them correctly
*and to get a better precision*/
lv_point_t p_outer;
p_outer.x = p_center.x + r_out + LV_MAX(LV_DPI_DEF, r_out);
p_outer.y = p_center.y;
lv_point_transform(&p_outer, angle_upscale, 256, &p_center);
part_draw_dsc.p1 = &p_center;
part_draw_dsc.p2 = &p_outer;
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 = p_center.x + r_text;
p.y = p_center.y;
lv_point_transform(&p, angle_upscale, 256, &p_center);
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(draw_ctx, part_draw_dsc.label_dsc, &label_cord, 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(draw_ctx, &line_dsc, &p_outer, &p_center);
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, lv_draw_ctx_t * draw_ctx, 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, draw_ctx);
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(draw_ctx, &line_dsc, &scale_center, &p_end);
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(draw_ctx, &img_dsc, &a, indic->type_data.needle_img.src);
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 object
* @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);
if(obj == NULL) return NULL;
lv_obj_class_init_obj(obj);
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,245 @@
/**
* @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;
int32_t lines;
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 object
* @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);
/**
* Set lines of the spangroup.
* @param obj pointer to a spangroup object.
* @param lines max lines that can be displayed in LV_SPAN_MODE_BREAK mode. < 0 means no limit.
*/
void lv_spangroup_set_lines(lv_obj_t * obj, int32_t lines);
/*=====================
* 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 lines of the spangroup.
* @param obj pointer to a spangroup object.
* @return the lines value.
*/
int32_t lv_spangroup_get_lines(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 text content width when all span of spangroup on a line.
* @param obj pointer to a spangroup object.
* @param max_width if text content width >= max_width, return max_width
* to reduce computation, if max_width == 0, returns the text content width.
* @return text content width or max_width.
*/
uint32_t lv_spangroup_get_expand_width(lv_obj_t * obj, uint32_t max_width);
/**
* get the text content height with width fixed.
* @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,516 @@
/**
* @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,
.width_def = LV_DPI_DEF,
.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 obj 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(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_cursor_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 obj 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 obj 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 obj 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 obj 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 obj 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 obj 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 obj 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_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,182 @@
/**
* @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 object
* @param parent 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 obj 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 obj 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 obj 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 obj 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 obj 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 obj pointer to spinbox
* @param pos selected position in spinbox
*/
void lv_spinbox_set_cursor_pos(lv_obj_t * obj, uint8_t pos);
/**
* Set direction of digit step when clicking an encoder button while in editing mode
* @param obj 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 obj 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 obj 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 obj 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 obj 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 obj pointer to spinbox
*/
void lv_spinbox_step_prev(lv_obj_t * obj);
/**
* Increment spinbox value by one step
* @param obj pointer to spinbox
*/
void lv_spinbox_increment(lv_obj_t * obj);
/**
* Decrement spinbox value by one step
* @param obj pointer to spinbox
*/
void lv_spinbox_decrement(lv_obj_t * obj);
/**********************
* MACROS
**********************/
/* It was ambiguous in MicroPython. See https://github.com/lvgl/lvgl/issues/3301
* TODO remove in v9*/
#define lv_spinbox_set_pos lv_spinbox_set_cursor_pos
#endif /*LV_USE_SPINBOX*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_SPINBOX_H*/

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 parent 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,352 @@
/**
* @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_rename_tab(lv_obj_t * obj, uint32_t id, const char * new_name)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_tabview_t * tabview = (lv_tabview_t *)obj;
if(id >= tabview->tab_cnt) return;
if(tabview->tab_pos & LV_DIR_HOR) id *= 2;
lv_mem_free(tabview->map[id]);
tabview->map[id] = lv_mem_alloc(strlen(new_name) + 1);
strcpy(tabview->map[id], new_name);
lv_obj_invalidate(obj);
}
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;
if((tabview->tab_pos & LV_DIR_VER) != 0) {
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);
}
}
else {
lv_coord_t gap = lv_obj_get_style_pad_row(cont, LV_PART_MAIN);
lv_coord_t h = lv_obj_get_content_height(cont);
lv_obj_scroll_to_y(cont, id * (gap + h), 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);
if((tabview->tab_pos & LV_DIR_VER) != 0) {
lv_obj_set_flex_flow(cont, LV_FLEX_FLOW_ROW);
lv_obj_set_scroll_snap_x(cont, LV_SCROLL_SNAP_CENTER);
}
else {
lv_obj_set_flex_flow(cont, LV_FLEX_FLOW_COLUMN);
lv_obj_set_scroll_snap_y(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);
lv_tabview_t * tv_obj = (lv_tabview_t *)tv;
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_indev_t * indev = lv_indev_get_act();
if(indev && indev->proc.state == LV_INDEV_STATE_PRESSED) {
return;
}
lv_point_t p;
lv_obj_get_scroll_end(cont, &p);
lv_coord_t t;
if((tv_obj->tab_pos & LV_DIR_VER) != 0) {
lv_coord_t w = lv_obj_get_content_width(cont);
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;
}
else {
lv_coord_t h = lv_obj_get_content_height(cont);
t = (p.y + h / 2) / h;
}
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,65 @@
/**
* @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);
void lv_tabview_rename_tab(lv_obj_t * obj, uint32_t tab_id, const char * new_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*/

Some files were not shown because too many files have changed in this diff Show More