feat: added PicoW_Sensor code template
Credits to @ext-erich.styger that provided the template
This commit is contained in:
committed by
Sylvan Arnold
parent
b2e9eab44e
commit
6cd510e749
31
TSM_PicoW_Sensor/McuLib/LittlevGL/lvgl/src/extra/README.md
Normal file
31
TSM_PicoW_Sensor/McuLib/LittlevGL/lvgl/src/extra/README.md
Normal 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
|
||||
@@ -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*/
|
||||
@@ -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*/
|
||||
@@ -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*/
|
||||
@@ -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*/
|
||||
@@ -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*/
|
||||
@@ -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*/
|
||||
@@ -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*/
|
||||
@@ -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*/
|
||||
@@ -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*/
|
||||
Binary file not shown.
@@ -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*/
|
||||
@@ -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 */
|
||||
@@ -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*/
|
||||
|
||||
@@ -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*/
|
||||
@@ -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*/
|
||||
|
||||
@@ -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
|
||||
@@ -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*/
|
||||
|
||||
@@ -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*/
|
||||
@@ -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 */
|
||||
@@ -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*/
|
||||
@@ -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*/
|
||||
@@ -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*/
|
||||
6469
TSM_PicoW_Sensor/McuLib/LittlevGL/lvgl/src/extra/libs/png/lodepng.c
Normal file
6469
TSM_PicoW_Sensor/McuLib/LittlevGL/lvgl/src/extra/libs/png/lodepng.c
Normal file
File diff suppressed because it is too large
Load Diff
1981
TSM_PicoW_Sensor/McuLib/LittlevGL/lvgl/src/extra/libs/png/lodepng.h
Normal file
1981
TSM_PicoW_Sensor/McuLib/LittlevGL/lvgl/src/extra/libs/png/lodepng.h
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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*/
|
||||
|
||||
|
||||
@@ -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*/
|
||||
@@ -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*/
|
||||
@@ -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
@@ -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
|
||||
@@ -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*/
|
||||
@@ -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*/
|
||||
@@ -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*/
|
||||
@@ -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 */
|
||||
1155
TSM_PicoW_Sensor/McuLib/LittlevGL/lvgl/src/extra/libs/sjpg/tjpgd.c
Normal file
1155
TSM_PicoW_Sensor/McuLib/LittlevGL/lvgl/src/extra/libs/sjpg/tjpgd.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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 */
|
||||
@@ -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)
|
||||
*/
|
||||
|
||||
93
TSM_PicoW_Sensor/McuLib/LittlevGL/lvgl/src/extra/lv_extra.c
Normal file
93
TSM_PicoW_Sensor/McuLib/LittlevGL/lvgl/src/extra/lv_extra.c
Normal 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
|
||||
**********************/
|
||||
48
TSM_PicoW_Sensor/McuLib/LittlevGL/lvgl/src/extra/lv_extra.h
Normal file
48
TSM_PicoW_Sensor/McuLib/LittlevGL/lvgl/src/extra/lv_extra.h
Normal 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*/
|
||||
@@ -0,0 +1 @@
|
||||
CSRCS += $(shell find -L $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/extra -name \*.c)
|
||||
@@ -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*/
|
||||
@@ -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*/
|
||||
@@ -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*/
|
||||
@@ -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*/
|
||||
@@ -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
@@ -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*/
|
||||
|
||||
|
||||
@@ -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*/
|
||||
@@ -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 */
|
||||
@@ -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*/
|
||||
@@ -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*/
|
||||
@@ -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*/
|
||||
@@ -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*/
|
||||
@@ -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*/
|
||||
@@ -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*/
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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*/
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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*/
|
||||
@@ -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*/
|
||||
@@ -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
|
||||
@@ -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*/
|
||||
@@ -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
|
||||
@@ -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*/
|
||||
@@ -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*/
|
||||
@@ -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*/
|
||||
@@ -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*/
|
||||
|
||||
@@ -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*/
|
||||
@@ -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*/
|
||||
|
||||
@@ -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
@@ -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*/
|
||||
@@ -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*/
|
||||
@@ -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*/
|
||||
|
||||
@@ -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
|
||||
@@ -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*/
|
||||
@@ -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*/
|
||||
@@ -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*/
|
||||
@@ -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
|
||||
@@ -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*/
|
||||
@@ -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*/
|
||||
@@ -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*/
|
||||
@@ -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*/
|
||||
@@ -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*/
|
||||
@@ -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*/
|
||||
@@ -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
|
||||
@@ -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*/
|
||||
@@ -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*/
|
||||
@@ -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
@@ -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*/
|
||||
@@ -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*/
|
||||
@@ -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*/
|
||||
@@ -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*/
|
||||
@@ -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*/
|
||||
@@ -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*/
|
||||
@@ -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
Reference in New Issue
Block a user