Initial commit: TXW82x FPV v2.7.0.7-42229 SDK + project sources

This commit is contained in:
2026-07-06 11:30:13 +08:00
commit e76462eeb7
3451 changed files with 1415300 additions and 0 deletions

View File

@@ -0,0 +1,717 @@
/**
* @file lv_file_explorer.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_file_explorer.h"
#if LV_USE_FILE_EXPLORER != 0
/*********************
* DEFINES
*********************/
#define MY_CLASS &lv_file_explorer_class
#define FILE_EXPLORER_QUICK_ACCESS_AREA_WIDTH (22)
#define FILE_EXPLORER_BROWSER_AREA_WIDTH (100 - FILE_EXPLORER_QUICK_ACCESS_AREA_WIDTH)
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_file_explorer_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void browser_file_event_handler(lv_event_t * e);
#if LV_FILE_EXPLORER_QUICK_ACCESS
static void quick_access_event_handler(lv_event_t * e);
static void quick_access_area_event_handler(lv_event_t * e);
#endif
static void init_style(lv_obj_t * obj);
static void show_dir(lv_obj_t * obj, const char * path);
static void strip_ext(char * dir);
static void file_explorer_sort(lv_obj_t * obj);
static void sort_by_file_kind(lv_obj_t * tb, int16_t lo, int16_t hi);
static void exch_table_item(lv_obj_t * tb, int16_t i, int16_t j);
static bool is_end_with(const char * str1, const char * str2);
/**********************
* STATIC VARIABLES
**********************/
static lv_style_t quick_access_list_btn_style;
const lv_obj_class_t lv_file_explorer_class = {
.constructor_cb = lv_file_explorer_constructor,
.width_def = LV_SIZE_CONTENT,
.height_def = LV_SIZE_CONTENT,
.instance_size = sizeof(lv_file_explorer_t),
.base_class = &lv_obj_class
};
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_obj_t * lv_file_explorer_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
*====================*/
#if LV_FILE_EXPLORER_QUICK_ACCESS
void lv_file_explorer_set_quick_access_path(lv_obj_t * obj, lv_file_explorer_dir_t dir, const char * path)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_file_explorer_t * explorer = (lv_file_explorer_t *)obj;
/*If path is unavailable */
if((path == NULL) || (strlen(path) <= 0)) return;
char ** dir_str = NULL;
switch(dir) {
case LV_EXPLORER_HOME_DIR:
dir_str = &(explorer->home_dir);
break;
case LV_EXPLORER_MUSIC_DIR:
dir_str = &(explorer->music_dir);
break;
case LV_EXPLORER_PICTURES_DIR:
dir_str = &(explorer->pictures_dir);
break;
case LV_EXPLORER_VIDEO_DIR:
dir_str = &(explorer->video_dir);
break;
case LV_EXPLORER_DOCS_DIR:
dir_str = &(explorer->docs_dir);
break;
case LV_EXPLORER_FS_DIR:
dir_str = &(explorer->fs_dir);
break;
default:
return;
break;
}
/*Free the old text*/
if(*dir_str != NULL) {
lv_free(*dir_str);
*dir_str = NULL;
}
/*Get the size of the text*/
size_t len = strlen(path) + 1;
/*Allocate space for the new text*/
*dir_str = lv_malloc(len);
LV_ASSERT_MALLOC(*dir_str);
if(*dir_str == NULL) return;
strcpy(*dir_str, path);
}
#endif
void lv_file_explorer_set_sort(lv_obj_t * obj, lv_file_explorer_sort_t sort)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_file_explorer_t * explorer = (lv_file_explorer_t *)obj;
explorer->sort = sort;
file_explorer_sort(obj);
}
/*=====================
* Getter functions
*====================*/
const char * lv_file_explorer_get_selected_file_name(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_file_explorer_t * explorer = (lv_file_explorer_t *)obj;
return explorer->sel_fn;
}
const char * lv_file_explorer_get_current_path(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_file_explorer_t * explorer = (lv_file_explorer_t *)obj;
return explorer->current_path;
}
lv_obj_t * lv_file_explorer_get_file_table(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_file_explorer_t * explorer = (lv_file_explorer_t *)obj;
return explorer->file_table;
}
lv_obj_t * lv_file_explorer_get_header(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_file_explorer_t * explorer = (lv_file_explorer_t *)obj;
return explorer->head_area;
}
lv_obj_t * lv_file_explorer_get_quick_access_area(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_file_explorer_t * explorer = (lv_file_explorer_t *)obj;
return explorer->quick_access_area;
}
lv_obj_t * lv_file_explorer_get_path_label(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_file_explorer_t * explorer = (lv_file_explorer_t *)obj;
return explorer->path_label;
}
#if LV_FILE_EXPLORER_QUICK_ACCESS
lv_obj_t * lv_file_explorer_get_places_list(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_file_explorer_t * explorer = (lv_file_explorer_t *)obj;
return explorer->list_places;
}
lv_obj_t * lv_file_explorer_get_device_list(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_file_explorer_t * explorer = (lv_file_explorer_t *)obj;
return explorer->list_device;
}
#endif
lv_file_explorer_sort_t lv_file_explorer_get_sort(const lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_file_explorer_t * explorer = (lv_file_explorer_t *)obj;
return explorer->sort;
}
/*=====================
* Other functions
*====================*/
void lv_file_explorer_open_dir(lv_obj_t * obj, const char * dir)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
show_dir(obj, dir);
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_file_explorer_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
LV_UNUSED(class_p);
LV_TRACE_OBJ_CREATE("begin");
lv_file_explorer_t * explorer = (lv_file_explorer_t *)obj;
#if LV_FILE_EXPLORER_QUICK_ACCESS
explorer->home_dir = NULL;
explorer->video_dir = NULL;
explorer->pictures_dir = NULL;
explorer->music_dir = NULL;
explorer->docs_dir = NULL;
explorer->fs_dir = NULL;
#endif
explorer->sort = LV_EXPLORER_SORT_NONE;
lv_memzero(explorer->current_path, sizeof(explorer->current_path));
lv_obj_set_size(obj, LV_PCT(100), LV_PCT(100));
lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_COLUMN);
explorer->cont = lv_obj_create(obj);
lv_obj_set_width(explorer->cont, LV_PCT(100));
lv_obj_set_flex_grow(explorer->cont, 1);
#if LV_FILE_EXPLORER_QUICK_ACCESS
/*Quick access bar area on the left*/
explorer->quick_access_area = lv_obj_create(explorer->cont);
lv_obj_set_size(explorer->quick_access_area, LV_PCT(FILE_EXPLORER_QUICK_ACCESS_AREA_WIDTH), LV_PCT(100));
lv_obj_set_flex_flow(explorer->quick_access_area, LV_FLEX_FLOW_COLUMN);
lv_obj_add_event_cb(explorer->quick_access_area, quick_access_area_event_handler, LV_EVENT_ALL,
explorer);
#endif
/*File table area on the right*/
explorer->browser_area = lv_obj_create(explorer->cont);
#if LV_FILE_EXPLORER_QUICK_ACCESS
lv_obj_set_size(explorer->browser_area, LV_PCT(FILE_EXPLORER_BROWSER_AREA_WIDTH), LV_PCT(100));
#else
lv_obj_set_size(explorer->browser_area, LV_PCT(100), LV_PCT(100));
#endif
lv_obj_set_flex_flow(explorer->browser_area, LV_FLEX_FLOW_COLUMN);
/*The area displayed above the file browse list(head)*/
explorer->head_area = lv_obj_create(explorer->browser_area);
lv_obj_set_size(explorer->head_area, LV_PCT(100), LV_PCT(14));
lv_obj_clear_flag(explorer->head_area, LV_OBJ_FLAG_SCROLLABLE);
#if LV_FILE_EXPLORER_QUICK_ACCESS
/*Two lists of quick access bar*/
lv_obj_t * btn;
/*list 1*/
explorer->list_device = lv_list_create(explorer->quick_access_area);
lv_obj_set_size(explorer->list_device, LV_PCT(100), LV_SIZE_CONTENT);
lv_obj_set_style_bg_color(lv_list_add_text(explorer->list_device, "DEVICE"), lv_palette_main(LV_PALETTE_ORANGE), 0);
btn = lv_list_add_btn(explorer->list_device, NULL, LV_SYMBOL_DRIVE " File System");
lv_obj_add_event_cb(btn, quick_access_event_handler, LV_EVENT_CLICKED, obj);
/*list 2*/
explorer->list_places = lv_list_create(explorer->quick_access_area);
lv_obj_set_size(explorer->list_places, LV_PCT(100), LV_SIZE_CONTENT);
lv_obj_set_style_bg_color(lv_list_add_text(explorer->list_places, "PLACES"), lv_palette_main(LV_PALETTE_LIME), 0);
btn = lv_list_add_btn(explorer->list_places, NULL, LV_SYMBOL_HOME " HOME");
lv_obj_add_event_cb(btn, quick_access_event_handler, LV_EVENT_CLICKED, obj);
btn = lv_list_add_btn(explorer->list_places, NULL, LV_SYMBOL_VIDEO " Video");
lv_obj_add_event_cb(btn, quick_access_event_handler, LV_EVENT_CLICKED, obj);
btn = lv_list_add_btn(explorer->list_places, NULL, LV_SYMBOL_IMAGE " Pictures");
lv_obj_add_event_cb(btn, quick_access_event_handler, LV_EVENT_CLICKED, obj);
btn = lv_list_add_btn(explorer->list_places, NULL, LV_SYMBOL_AUDIO " Music");
lv_obj_add_event_cb(btn, quick_access_event_handler, LV_EVENT_CLICKED, obj);
btn = lv_list_add_btn(explorer->list_places, NULL, LV_SYMBOL_FILE " Documents");
lv_obj_add_event_cb(btn, quick_access_event_handler, LV_EVENT_CLICKED, obj);
#endif
/*Show current path*/
explorer->path_label = lv_label_create(explorer->head_area);
lv_label_set_text(explorer->path_label, LV_SYMBOL_EYE_OPEN"https://lvgl.io");
lv_obj_center(explorer->path_label);
/*Table showing the contents of the table of contents*/
explorer->file_table = lv_table_create(explorer->browser_area);
lv_obj_set_size(explorer->file_table, LV_PCT(100), LV_PCT(86));
lv_table_set_col_width(explorer->file_table, 0, LV_PCT(100));
lv_table_set_col_cnt(explorer->file_table, 1);
lv_obj_add_event_cb(explorer->file_table, browser_file_event_handler, LV_EVENT_ALL, obj);
/*only scroll up and down*/
lv_obj_set_scroll_dir(explorer->file_table, LV_DIR_TOP | LV_DIR_BOTTOM);
/*Initialize style*/
init_style(obj);
LV_TRACE_OBJ_CREATE("finished");
}
static void init_style(lv_obj_t * obj)
{
lv_file_explorer_t * explorer = (lv_file_explorer_t *)obj;
/*lv_file_explorer obj style*/
lv_obj_set_style_radius(obj, 0, 0);
lv_obj_set_style_bg_color(obj, lv_color_hex(0xf2f1f6), 0);
/*main container style*/
lv_obj_set_style_radius(explorer->cont, 0, 0);
lv_obj_set_style_bg_opa(explorer->cont, LV_OPA_0, 0);
lv_obj_set_style_border_width(explorer->cont, 0, 0);
lv_obj_set_style_outline_width(explorer->cont, 0, 0);
lv_obj_set_style_pad_column(explorer->cont, 0, 0);
lv_obj_set_style_pad_row(explorer->cont, 0, 0);
lv_obj_set_style_flex_flow(explorer->cont, LV_FLEX_FLOW_ROW, 0);
lv_obj_set_style_pad_all(explorer->cont, 0, 0);
lv_obj_set_style_layout(explorer->cont, LV_LAYOUT_FLEX, 0);
/*head cont style*/
lv_obj_set_style_radius(explorer->head_area, 0, 0);
lv_obj_set_style_border_width(explorer->head_area, 0, 0);
lv_obj_set_style_pad_top(explorer->head_area, 0, 0);
#if LV_FILE_EXPLORER_QUICK_ACCESS
/*Quick access bar container style*/
lv_obj_set_style_pad_all(explorer->quick_access_area, 0, 0);
lv_obj_set_style_pad_row(explorer->quick_access_area, 20, 0);
lv_obj_set_style_radius(explorer->quick_access_area, 0, 0);
lv_obj_set_style_border_width(explorer->quick_access_area, 1, 0);
lv_obj_set_style_outline_width(explorer->quick_access_area, 0, 0);
lv_obj_set_style_bg_color(explorer->quick_access_area, lv_color_hex(0xf2f1f6), 0);
#endif
/*File browser container style*/
lv_obj_set_style_pad_all(explorer->browser_area, 0, 0);
lv_obj_set_style_pad_row(explorer->browser_area, 0, 0);
lv_obj_set_style_radius(explorer->browser_area, 0, 0);
lv_obj_set_style_border_width(explorer->browser_area, 0, 0);
lv_obj_set_style_outline_width(explorer->browser_area, 0, 0);
lv_obj_set_style_bg_color(explorer->browser_area, lv_color_hex(0xffffff), 0);
/*Style of the table in the browser container*/
lv_obj_set_style_bg_color(explorer->file_table, lv_color_hex(0xffffff), 0);
lv_obj_set_style_pad_all(explorer->file_table, 0, 0);
lv_obj_set_style_radius(explorer->file_table, 0, 0);
lv_obj_set_style_border_width(explorer->file_table, 0, 0);
lv_obj_set_style_outline_width(explorer->file_table, 0, 0);
#if LV_FILE_EXPLORER_QUICK_ACCESS
/*Style of the list in the quick access bar*/
lv_obj_set_style_border_width(explorer->list_device, 0, 0);
lv_obj_set_style_outline_width(explorer->list_device, 0, 0);
lv_obj_set_style_radius(explorer->list_device, 0, 0);
lv_obj_set_style_pad_all(explorer->list_device, 0, 0);
lv_obj_set_style_border_width(explorer->list_places, 0, 0);
lv_obj_set_style_outline_width(explorer->list_places, 0, 0);
lv_obj_set_style_radius(explorer->list_places, 0, 0);
lv_obj_set_style_pad_all(explorer->list_places, 0, 0);
/*Style of the quick access list btn in the quick access bar*/
lv_style_init(&quick_access_list_btn_style);
lv_style_set_border_width(&quick_access_list_btn_style, 0);
lv_style_set_bg_color(&quick_access_list_btn_style, lv_color_hex(0xf2f1f6));
uint32_t i, j;
for(i = 0; i < lv_obj_get_child_cnt(explorer->quick_access_area); i++) {
lv_obj_t * child = lv_obj_get_child(explorer->quick_access_area, i);
if(lv_obj_check_type(child, &lv_list_class)) {
for(j = 0; j < lv_obj_get_child_cnt(child); j++) {
lv_obj_t * list_child = lv_obj_get_child(child, j);
if(lv_obj_check_type(list_child, &lv_list_btn_class)) {
lv_obj_add_style(list_child, &quick_access_list_btn_style, 0);
}
}
}
}
#endif
}
#if LV_FILE_EXPLORER_QUICK_ACCESS
static void quick_access_event_handler(lv_event_t * e)
{
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * btn = lv_event_get_target(e);
lv_obj_t * obj = lv_event_get_user_data(e);
lv_file_explorer_t * explorer = (lv_file_explorer_t *)obj;
if(code == LV_EVENT_CLICKED) {
char ** path = NULL;
lv_obj_t * label = lv_obj_get_child(btn, -1);
char * label_text = lv_label_get_text(label);
if((strcmp(label_text, LV_SYMBOL_HOME " HOME") == 0)) {
path = &(explorer->home_dir);
}
else if((strcmp(label_text, LV_SYMBOL_VIDEO " Video") == 0)) {
path = &(explorer->video_dir);
}
else if((strcmp(label_text, LV_SYMBOL_IMAGE " Pictures") == 0)) {
path = &(explorer->pictures_dir);
}
else if((strcmp(label_text, LV_SYMBOL_AUDIO " Music") == 0)) {
path = &(explorer->music_dir);
}
else if((strcmp(label_text, LV_SYMBOL_FILE " Documents") == 0)) {
path = &(explorer->docs_dir);
}
else if((strcmp(label_text, LV_SYMBOL_DRIVE " File System") == 0)) {
path = &(explorer->fs_dir);
}
if(path != NULL)
show_dir(obj, *path);
}
}
static void quick_access_area_event_handler(lv_event_t * e)
{
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * area = lv_event_get_target(e);
lv_obj_t * obj = lv_event_get_user_data(e);
lv_file_explorer_t * explorer = (lv_file_explorer_t *)obj;
if(code == LV_EVENT_LAYOUT_CHANGED) {
if(lv_obj_has_flag(area, LV_OBJ_FLAG_HIDDEN))
lv_obj_set_size(explorer->browser_area, LV_PCT(100), LV_PCT(100));
else
lv_obj_set_size(explorer->browser_area, LV_PCT(FILE_EXPLORER_BROWSER_AREA_WIDTH), LV_PCT(100));
}
}
#endif
static void browser_file_event_handler(lv_event_t * e)
{
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * obj = lv_event_get_user_data(e);
lv_file_explorer_t * explorer = (lv_file_explorer_t *)obj;
if(code == LV_EVENT_VALUE_CHANGED) {
char file_name[LV_FILE_EXPLORER_PATH_MAX_LEN];
const char * str_fn = NULL;
uint16_t row;
uint16_t col;
lv_memzero(file_name, sizeof(file_name));
lv_table_get_selected_cell(explorer->file_table, &row, &col);
str_fn = lv_table_get_cell_value(explorer->file_table, row, col);
str_fn = str_fn + 5;
if((strcmp(str_fn, ".") == 0)) return;
if((strcmp(str_fn, "..") == 0) && (strlen(explorer->current_path) > 3)) {
strip_ext(explorer->current_path);
/*Remove the last '/' character*/
strip_ext(explorer->current_path);
lv_snprintf((char *)file_name, sizeof(file_name), "%s", explorer->current_path);
}
else {
if(strcmp(str_fn, "..") != 0) {
lv_snprintf((char *)file_name, sizeof(file_name), "%s%s", explorer->current_path, str_fn);
}
}
lv_fs_dir_t dir;
if(lv_fs_dir_open(&dir, file_name) == LV_FS_RES_OK) {
lv_fs_dir_close(&dir);
show_dir(obj, (char *)file_name);
}
else {
if(strcmp(str_fn, "..") != 0) {
explorer->sel_fn = str_fn;
lv_event_send(obj, LV_EVENT_VALUE_CHANGED, NULL);
}
}
}
else if(code == LV_EVENT_SIZE_CHANGED) {
lv_table_set_col_width(explorer->file_table, 0, lv_obj_get_width(explorer->file_table));
}
else if((code == LV_EVENT_CLICKED) || (code == LV_EVENT_RELEASED)) {
lv_event_send(obj, LV_EVENT_CLICKED, NULL);
}
}
static void show_dir(lv_obj_t * obj, const char * path)
{
lv_file_explorer_t * explorer = (lv_file_explorer_t *)obj;
char fn[LV_FILE_EXPLORER_PATH_MAX_LEN];
uint16_t index = 0;
lv_fs_dir_t dir;
lv_fs_res_t res;
res = lv_fs_dir_open(&dir, path);
if(res != LV_FS_RES_OK) {
LV_LOG_USER("Open dir error %d!", res);
return;
}
lv_table_set_cell_value_fmt(explorer->file_table, index++, 0, LV_SYMBOL_DIRECTORY " %s", ".");
lv_table_set_cell_value_fmt(explorer->file_table, index++, 0, LV_SYMBOL_DIRECTORY " %s", "..");
lv_table_set_cell_value(explorer->file_table, 0, 1, "0");
lv_table_set_cell_value(explorer->file_table, 1, 1, "0");
while(1) {
res = lv_fs_dir_read(&dir, fn);
if(res != LV_FS_RES_OK) {
LV_LOG_USER("Driver, file or directory is not exists %d!", res);
break;
}
/*fn is empty, if not more files to read*/
if(strlen(fn) == 0) {
LV_LOG_USER("Not more files to read!");
break;
}
if((is_end_with(fn, ".png") == true) || (is_end_with(fn, ".PNG") == true) || \
(is_end_with(fn, ".jpg") == true) || (is_end_with(fn, ".JPG") == true) || \
(is_end_with(fn, ".bmp") == true) || (is_end_with(fn, ".BMP") == true) || \
(is_end_with(fn, ".gif") == true) || (is_end_with(fn, ".GIF") == true)) {
lv_table_set_cell_value_fmt(explorer->file_table, index, 0, LV_SYMBOL_IMAGE " %s", fn);
lv_table_set_cell_value(explorer->file_table, index, 1, "1");
}
else if((is_end_with(fn, ".mp3") == true) || (is_end_with(fn, ".MP3") == true)) {
lv_table_set_cell_value_fmt(explorer->file_table, index, 0, LV_SYMBOL_AUDIO " %s", fn);
lv_table_set_cell_value(explorer->file_table, index, 1, "2");
}
else if((is_end_with(fn, ".mp4") == true) || (is_end_with(fn, ".MP4") == true)) {
lv_table_set_cell_value_fmt(explorer->file_table, index, 0, LV_SYMBOL_VIDEO " %s", fn);
lv_table_set_cell_value(explorer->file_table, index, 1, "3");
}
else if((is_end_with(fn, ".") == true) || (is_end_with(fn, "..") == true)) {
/*is dir*/
continue;
}
else if(fn[0] == '/') {/*is dir*/
lv_table_set_cell_value_fmt(explorer->file_table, index, 0, LV_SYMBOL_DIRECTORY " %s", fn + 1);
lv_table_set_cell_value(explorer->file_table, index, 1, "0");
}
else {
lv_table_set_cell_value_fmt(explorer->file_table, index, 0, LV_SYMBOL_FILE " %s", fn);
lv_table_set_cell_value(explorer->file_table, index, 1, "4");
}
index++;
}
lv_fs_dir_close(&dir);
lv_table_set_row_cnt(explorer->file_table, index);
file_explorer_sort(obj);
lv_event_send(obj, LV_EVENT_READY, NULL);
/*Move the table to the top*/
lv_obj_scroll_to_y(explorer->file_table, 0, LV_ANIM_OFF);
lv_memzero(explorer->current_path, sizeof(explorer->current_path));
strcpy(explorer->current_path, path);
lv_label_set_text_fmt(explorer->path_label, LV_SYMBOL_EYE_OPEN" %s", path);
size_t current_path_len = strlen(explorer->current_path);
if((*((explorer->current_path) + current_path_len) != '/') && (current_path_len < LV_FILE_EXPLORER_PATH_MAX_LEN)) {
*((explorer->current_path) + current_path_len) = '/';
}
}
/*Remove the specified suffix*/
static void strip_ext(char * dir)
{
char * end = dir + strlen(dir);
while(end >= dir && *end != '/') {
--end;
}
if(end > dir) {
*end = '\0';
}
else if(end == dir) {
*(end + 1) = '\0';
}
}
static void exch_table_item(lv_obj_t * tb, int16_t i, int16_t j)
{
const char * tmp;
tmp = lv_table_get_cell_value(tb, i, 0);
lv_table_set_cell_value(tb, 0, 2, tmp);
lv_table_set_cell_value(tb, i, 0, lv_table_get_cell_value(tb, j, 0));
lv_table_set_cell_value(tb, j, 0, lv_table_get_cell_value(tb, 0, 2));
tmp = lv_table_get_cell_value(tb, i, 1);
lv_table_set_cell_value(tb, 0, 2, tmp);
lv_table_set_cell_value(tb, i, 1, lv_table_get_cell_value(tb, j, 1));
lv_table_set_cell_value(tb, j, 1, lv_table_get_cell_value(tb, 0, 2));
}
static void file_explorer_sort(lv_obj_t * obj)
{
LV_ASSERT_OBJ(obj, MY_CLASS);
lv_file_explorer_t * explorer = (lv_file_explorer_t *)obj;
uint16_t sum = lv_table_get_row_cnt(explorer->file_table);
if(sum > 1) {
switch(explorer->sort) {
case LV_EXPLORER_SORT_NONE:
break;
case LV_EXPLORER_SORT_KIND:
sort_by_file_kind(explorer->file_table, 0, (sum - 1));
break;
default:
break;
}
}
}
/*Quick sort 3 way*/
static void sort_by_file_kind(lv_obj_t * tb, int16_t lo, int16_t hi)
{
if(lo >= hi) return;
int16_t lt = lo;
int16_t i = lo + 1;
int16_t gt = hi;
const char * v = lv_table_get_cell_value(tb, lo, 1);
while(i <= gt) {
if(strcmp(lv_table_get_cell_value(tb, i, 1), v) < 0)
exch_table_item(tb, lt++, i++);
else if(strcmp(lv_table_get_cell_value(tb, i, 1), v) > 0)
exch_table_item(tb, i, gt--);
else
i++;
}
sort_by_file_kind(tb, lo, lt - 1);
sort_by_file_kind(tb, gt + 1, hi);
}
static bool is_end_with(const char * str1, const char * str2)
{
if(str1 == NULL || str2 == NULL)
return false;
uint16_t len1 = strlen(str1);
uint16_t len2 = strlen(str2);
if((len1 < len2) || (len1 == 0 || len2 == 0))
return false;
while(len2 >= 1) {
if(str2[len2 - 1] != str1[len1 - 1])
return false;
len2--;
len1--;
}
return true;
}
#endif /*LV_USE_FILE_EXPLORER*/

View File

@@ -0,0 +1,190 @@
/**
* @file lv_file_explorer.h
*
*/
#ifndef LV_FILE_EXPLORER_H
#define LV_FILE_EXPLORER_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../../lvgl.h"
#if LV_USE_FILE_EXPLORER != 0
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef enum {
LV_EXPLORER_SORT_NONE,
LV_EXPLORER_SORT_KIND,
} lv_file_explorer_sort_t;
#if LV_FILE_EXPLORER_QUICK_ACCESS
typedef enum {
LV_EXPLORER_HOME_DIR,
LV_EXPLORER_MUSIC_DIR,
LV_EXPLORER_PICTURES_DIR,
LV_EXPLORER_VIDEO_DIR,
LV_EXPLORER_DOCS_DIR,
LV_EXPLORER_FS_DIR,
} lv_file_explorer_dir_t;
#endif
/*Data of canvas*/
typedef struct {
lv_obj_t obj;
lv_obj_t * cont;
lv_obj_t * head_area;
lv_obj_t * browser_area;
lv_obj_t * file_table;
lv_obj_t * path_label;
#if LV_FILE_EXPLORER_QUICK_ACCESS
lv_obj_t * quick_access_area;
lv_obj_t * list_device;
lv_obj_t * list_places;
char * home_dir;
char * music_dir;
char * pictures_dir;
char * video_dir;
char * docs_dir;
char * fs_dir;
#endif
const char * sel_fn;
char current_path[LV_FILE_EXPLORER_PATH_MAX_LEN];
lv_file_explorer_sort_t sort;
} lv_file_explorer_t;
extern const lv_obj_class_t lv_file_explorer_class;
/***********************
* GLOBAL VARIABLES
***********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
lv_obj_t * lv_file_explorer_create(lv_obj_t * parent);
/*=====================
* Setter functions
*====================*/
#if LV_FILE_EXPLORER_QUICK_ACCESS
/**
* Set file_explorer
* @param obj pointer to a label object
* @param dir the dir from 'lv_file_explorer_dir_t' enum.
*/
void lv_file_explorer_set_quick_access_path(lv_obj_t * obj, lv_file_explorer_dir_t dir, const char * path);
#endif
/**
* Set file_explorer sort
* @param obj pointer to a file explorer object
* @param sort the sort from 'lv_file_explorer_sort_t' enum.
*/
void lv_file_explorer_set_sort(lv_obj_t * obj, lv_file_explorer_sort_t sort);
/*=====================
* Getter functions
*====================*/
/**
* Get file explorer Selected file
* @param obj pointer to a file explorer object
* @return pointer to the file explorer selected file name
*/
const char * lv_file_explorer_get_selected_file_name(const lv_obj_t * obj);
/**
* Get file explorer cur path
* @param obj pointer to a file explorer object
* @return pointer to the file explorer cur path
*/
const char * lv_file_explorer_get_current_path(const lv_obj_t * obj);
/**
* Get file explorer head area obj
* @param obj pointer to a file explorer object
* @return pointer to the file explorer head area obj(lv_obj)
*/
lv_obj_t * lv_file_explorer_get_header(lv_obj_t * obj);
/**
* Get file explorer head area obj
* @param obj pointer to a file explorer object
* @return pointer to the file explorer quick access area obj(lv_obj)
*/
lv_obj_t * lv_file_explorer_get_quick_access_area(lv_obj_t * obj);
/**
* Get file explorer path obj(label)
* @param obj pointer to a file explorer object
* @return pointer to the file explorer path obj(lv_label)
*/
lv_obj_t * lv_file_explorer_get_path_label(lv_obj_t * obj);
#if LV_FILE_EXPLORER_QUICK_ACCESS
/**
* Get file explorer places list obj(lv_list)
* @param obj pointer to a file explorer object
* @return pointer to the file explorer places list obj(lv_list)
*/
lv_obj_t * lv_file_explorer_get_places_list(lv_obj_t * obj);
/**
* Get file explorer device list obj(lv_list)
* @param obj pointer to a file explorer object
* @return pointer to the file explorer device list obj(lv_list)
*/
lv_obj_t * lv_file_explorer_get_device_list(lv_obj_t * obj);
#endif
/**
* Get file explorer file list obj(lv_table)
* @param obj pointer to a file explorer object
* @return pointer to the file explorer file table obj(lv_table)
*/
lv_obj_t * lv_file_explorer_get_file_table(lv_obj_t * obj);
/**
* Set file_explorer sort
* @param obj pointer to a file explorer object
* @return the current mode from 'lv_file_explorer_sort_t'
*/
lv_file_explorer_sort_t lv_file_explorer_get_sort(const lv_obj_t * obj);
/*=====================
* Other functions
*====================*/
/**
* Open a specified path
* @param obj pointer to a file explorer object
* @param dir pointer to the path
*/
void lv_file_explorer_open_dir(lv_obj_t * obj, const char * dir);
/**********************
* MACROS
**********************/
#endif /*LV_USE_FILE_EXPLORER*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_FILE_EXPLORER_H*/

View File

@@ -0,0 +1,144 @@
/**
* @file lv_fragment.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_fragment.h"
#if LV_USE_FRAGMENT
/**********************
* STATIC PROTOTYPES
**********************/
static void cb_delete_assertion(lv_event_t * event);
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_fragment_t * lv_fragment_create(const lv_fragment_class_t * cls, void * args)
{
LV_ASSERT_NULL(cls);
LV_ASSERT_NULL(cls->create_obj_cb);
LV_ASSERT(cls->instance_size > 0);
lv_fragment_t * instance = lv_malloc(cls->instance_size);
lv_memzero(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_free(fragment);
}
lv_fragment_manager_t * lv_fragment_get_manager(lv_fragment_t * fragment)
{
LV_ASSERT_NULL(fragment);
LV_ASSERT_NULL(fragment->managed);
return fragment->managed->manager;
}
lv_obj_t * const * lv_fragment_get_container(lv_fragment_t * fragment)
{
LV_ASSERT_NULL(fragment);
LV_ASSERT_NULL(fragment->managed);
return fragment->managed->container;
}
lv_fragment_t * lv_fragment_get_parent(lv_fragment_t * fragment)
{
LV_ASSERT_NULL(fragment);
LV_ASSERT_NULL(fragment->managed);
return lv_fragment_manager_get_parent_fragment(fragment->managed->manager);
}
lv_obj_t * lv_fragment_create_obj(lv_fragment_t * fragment, lv_obj_t * container)
{
lv_fragment_managed_states_t * states = fragment->managed;
if(states) {
states->destroying_obj = false;
}
const lv_fragment_class_t * cls = fragment->cls;
lv_obj_t * obj = cls->create_obj_cb(fragment, container);
LV_ASSERT_NULL(obj);
fragment->obj = obj;
lv_fragment_manager_create_obj(fragment->child_manager);
if(states) {
states->obj_created = true;
lv_obj_add_event_cb(obj, cb_delete_assertion, LV_EVENT_DELETE, NULL);
}
if(cls->obj_created_cb) {
cls->obj_created_cb(fragment, obj);
}
return obj;
}
void lv_fragment_del_obj(lv_fragment_t * fragment)
{
LV_ASSERT_NULL(fragment);
lv_fragment_manager_del_obj(fragment->child_manager);
lv_fragment_managed_states_t * states = fragment->managed;
if(states) {
if(!states->obj_created) return;
states->destroying_obj = true;
bool cb_removed = lv_obj_remove_event_cb(fragment->obj, cb_delete_assertion);
LV_ASSERT(cb_removed);
}
LV_ASSERT_NULL(fragment->obj);
const lv_fragment_class_t * cls = fragment->cls;
if(cls->obj_will_delete_cb) {
cls->obj_will_delete_cb(fragment, fragment->obj);
}
lv_obj_del(fragment->obj);
if(cls->obj_deleted_cb) {
cls->obj_deleted_cb(fragment, fragment->obj);
}
if(states) {
states->obj_created = false;
}
fragment->obj = NULL;
}
void lv_fragment_recreate_obj(lv_fragment_t * fragment)
{
LV_ASSERT_NULL(fragment);
LV_ASSERT_NULL(fragment->managed);
lv_fragment_del_obj(fragment);
lv_fragment_create_obj(fragment, *fragment->managed->container);
}
/**********************
* STATIC FUNCTIONS
**********************/
static void cb_delete_assertion(lv_event_t * event)
{
LV_UNUSED(event);
LV_ASSERT_MSG(0, "Please delete objects with lv_fragment_destroy_obj");
}
#endif /*LV_USE_FRAGMENT*/

View File

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

View File

@@ -0,0 +1,281 @@
/**
* @file lv_fragment_manager.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_fragment.h"
#if LV_USE_FRAGMENT
#include "../../misc/lv_ll.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct _lv_fragment_stack_item_t {
lv_fragment_managed_states_t * states;
} lv_fragment_stack_item_t;
struct _lv_fragment_manager_t {
lv_fragment_t * parent;
/**
* Linked list to store attached fragments
*/
lv_ll_t attached;
/**
* Linked list to store fragments in stack
*/
lv_ll_t stack;
};
/**********************
* STATIC PROTOTYPES
**********************/
static void item_create_obj(lv_fragment_managed_states_t * item);
static void item_del_obj(lv_fragment_managed_states_t * item);
static void item_del_fragment(lv_fragment_managed_states_t * item);
static lv_fragment_managed_states_t * fragment_attach(lv_fragment_manager_t * manager, lv_fragment_t * fragment,
lv_obj_t * const * container);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_fragment_manager_t * lv_fragment_manager_create(lv_fragment_t * parent)
{
lv_fragment_manager_t * instance = lv_malloc(sizeof(lv_fragment_manager_t));
lv_memzero(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_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_free(item);
}
}
item_del_obj(states);
item_del_fragment(states);
_lv_ll_remove(&manager->attached, states);
lv_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_memzero(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_memzero(states, sizeof(lv_fragment_managed_states_t));
states->cls = fragment->cls;
states->manager = manager;
states->container = container;
states->instance = fragment;
fragment->managed = states;
if(fragment->cls->attached_cb) {
fragment->cls->attached_cb(fragment);
}
return states;
}
#endif /*LV_USE_FRAGMENT*/

View File

@@ -0,0 +1,378 @@
/**
* @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_malloc(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_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;
}
if(dsc->focused_obj) {
lv_obj_clear_state(dsc->focused_obj, LV_STATE_FOCUSED | LV_STATE_FOCUS_KEY);
}
lv_obj_add_state(to_focus, LV_STATE_FOCUSED | LV_STATE_FOCUS_KEY);
lv_obj_scroll_to_view(to_focus, anim_en);
dsc->focused_obj = to_focus;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void gridnav_event_cb(lv_event_t * e)
{
lv_obj_t * obj = lv_event_get_current_target(e);
lv_gridnav_dsc_t * dsc = lv_event_get_user_data(e);
lv_event_code_t code = lv_event_get_code(e);
if(code == LV_EVENT_KEY) {
uint32_t child_cnt = lv_obj_get_child_cnt(obj);
if(child_cnt == 0) return;
if(dsc->focused_obj == NULL) dsc->focused_obj = find_first_focusable(obj);
if(dsc->focused_obj == NULL) return;
uint32_t key = lv_event_get_key(e);
lv_obj_t * guess = NULL;
if(key == LV_KEY_RIGHT) {
if((dsc->ctrl & LV_GRIDNAV_CTRL_SCROLL_FIRST) && lv_obj_has_flag(dsc->focused_obj, LV_OBJ_FLAG_SCROLLABLE) &&
lv_obj_get_scroll_right(dsc->focused_obj) > 0) {
lv_coord_t d = lv_obj_get_width(dsc->focused_obj) / 4;
if(d <= 0) d = 1;
lv_obj_scroll_by_bounded(dsc->focused_obj, -d, 0, LV_ANIM_ON);
}
else {
guess = find_chid(obj, dsc->focused_obj, FIND_RIGHT);
if(guess == NULL) {
if(dsc->ctrl & LV_GRIDNAV_CTRL_ROLLOVER) {
guess = find_chid(obj, dsc->focused_obj, FIND_NEXT_ROW_FIRST_ITEM);
if(guess == NULL) guess = find_first_focusable(obj);
}
else {
lv_group_focus_next(lv_obj_get_group(obj));
}
}
}
}
else if(key == LV_KEY_LEFT) {
if((dsc->ctrl & LV_GRIDNAV_CTRL_SCROLL_FIRST) && lv_obj_has_flag(dsc->focused_obj, LV_OBJ_FLAG_SCROLLABLE) &&
lv_obj_get_scroll_left(dsc->focused_obj) > 0) {
lv_coord_t d = lv_obj_get_width(dsc->focused_obj) / 4;
if(d <= 0) d = 1;
lv_obj_scroll_by_bounded(dsc->focused_obj, d, 0, LV_ANIM_ON);
}
else {
guess = find_chid(obj, dsc->focused_obj, FIND_LEFT);
if(guess == NULL) {
if(dsc->ctrl & LV_GRIDNAV_CTRL_ROLLOVER) {
guess = find_chid(obj, dsc->focused_obj, FIND_PREV_ROW_LAST_ITEM);
if(guess == NULL) guess = find_last_focusable(obj);
}
else {
lv_group_focus_prev(lv_obj_get_group(obj));
}
}
}
}
else if(key == LV_KEY_DOWN) {
if((dsc->ctrl & LV_GRIDNAV_CTRL_SCROLL_FIRST) && lv_obj_has_flag(dsc->focused_obj, LV_OBJ_FLAG_SCROLLABLE) &&
lv_obj_get_scroll_bottom(dsc->focused_obj) > 0) {
lv_coord_t d = lv_obj_get_height(dsc->focused_obj) / 4;
if(d <= 0) d = 1;
lv_obj_scroll_by_bounded(dsc->focused_obj, 0, -d, LV_ANIM_ON);
}
else {
guess = find_chid(obj, dsc->focused_obj, FIND_BOTTOM);
if(guess == NULL) {
if(dsc->ctrl & LV_GRIDNAV_CTRL_ROLLOVER) {
guess = find_chid(obj, dsc->focused_obj, FIND_FIRST_ROW);
}
else {
lv_group_focus_next(lv_obj_get_group(obj));
}
}
}
}
else if(key == LV_KEY_UP) {
if((dsc->ctrl & LV_GRIDNAV_CTRL_SCROLL_FIRST) && lv_obj_has_flag(dsc->focused_obj, LV_OBJ_FLAG_SCROLLABLE) &&
lv_obj_get_scroll_top(dsc->focused_obj) > 0) {
lv_coord_t d = lv_obj_get_height(dsc->focused_obj) / 4;
if(d <= 0) d = 1;
lv_obj_scroll_by_bounded(dsc->focused_obj, 0, d, LV_ANIM_ON);
}
else {
guess = find_chid(obj, dsc->focused_obj, FIND_TOP);
if(guess == NULL) {
if(dsc->ctrl & LV_GRIDNAV_CTRL_ROLLOVER) {
guess = find_chid(obj, dsc->focused_obj, FIND_LAST_ROW);
}
else {
lv_group_focus_prev(lv_obj_get_group(obj));
}
}
}
}
else {
if(lv_group_get_focused(lv_obj_get_group(obj)) == obj) {
lv_event_send(dsc->focused_obj, LV_EVENT_KEY, &key);
}
}
if(guess && guess != dsc->focused_obj) {
lv_obj_clear_state(dsc->focused_obj, LV_STATE_FOCUSED | LV_STATE_FOCUS_KEY);
lv_obj_add_state(guess, LV_STATE_FOCUSED | LV_STATE_FOCUS_KEY);
lv_obj_scroll_to_view(guess, LV_ANIM_ON);
dsc->focused_obj = guess;
}
}
else if(code == LV_EVENT_FOCUSED) {
if(dsc->focused_obj == NULL) dsc->focused_obj = find_first_focusable(obj);
if(dsc->focused_obj) {
lv_obj_add_state(dsc->focused_obj, LV_STATE_FOCUSED | LV_STATE_FOCUS_KEY);
lv_obj_scroll_to_view(dsc->focused_obj, LV_ANIM_OFF);
}
}
else if(code == LV_EVENT_DEFOCUSED) {
if(dsc->focused_obj) {
lv_obj_clear_state(dsc->focused_obj, LV_STATE_FOCUSED | LV_STATE_FOCUS_KEY);
}
}
else if(code == LV_EVENT_CHILD_CREATED) {
lv_obj_t * child = lv_event_get_target(e);
if(lv_obj_get_parent(child) == obj) {
if(dsc->focused_obj == NULL) {
dsc->focused_obj = child;
if(lv_obj_has_state(obj, LV_STATE_FOCUSED)) {
lv_obj_add_state(child, LV_STATE_FOCUSED | LV_STATE_FOCUS_KEY);
lv_obj_scroll_to_view(child, LV_ANIM_OFF);
}
}
}
}
else if(code == LV_EVENT_CHILD_DELETED) {
/*This event bubble, so be sure this object's child was deleted.
*As we don't know which object was deleted we can't make the next focused.
*So make the first object focused*/
lv_obj_t * target = lv_event_get_target(e);
if(target == obj) {
dsc->focused_obj = find_first_focusable(obj);
}
}
else if(code == LV_EVENT_DELETE) {
lv_gridnav_remove(obj);
}
else if(code == LV_EVENT_PRESSED || code == LV_EVENT_PRESSING || code == LV_EVENT_PRESS_LOST ||
code == LV_EVENT_LONG_PRESSED || code == LV_EVENT_LONG_PRESSED_REPEAT ||
code == LV_EVENT_CLICKED || code == LV_EVENT_RELEASED) {
if(lv_group_get_focused(lv_obj_get_group(obj)) == obj) {
/*Forward press/release related event too*/
lv_indev_type_t t = lv_indev_get_type(lv_indev_get_act());
if(t == LV_INDEV_TYPE_ENCODER || t == LV_INDEV_TYPE_KEYPAD) {
lv_event_send(dsc->focused_obj, code, lv_indev_get_act());
}
}
}
}
static lv_obj_t * find_chid(lv_obj_t * obj, lv_obj_t * start_child, find_mode_t mode)
{
lv_coord_t x_start = get_x_center(start_child);
lv_coord_t y_start = get_y_center(start_child);
uint32_t child_cnt = lv_obj_get_child_cnt(obj);
lv_obj_t * guess = NULL;
lv_coord_t x_err_guess = LV_COORD_MAX;
lv_coord_t y_err_guess = LV_COORD_MAX;
lv_coord_t h_half = lv_obj_get_height(start_child) / 2;
lv_coord_t h_max = lv_obj_get_height(obj) + lv_obj_get_scroll_top(obj) + lv_obj_get_scroll_bottom(obj);
uint32_t i;
for(i = 0; i < child_cnt; i++) {
lv_obj_t * child = lv_obj_get_child(obj, i);
if(child == start_child) continue;
if(obj_is_focuable(child) == false) continue;
lv_coord_t x_err = 0;
lv_coord_t y_err = 0;
switch(mode) {
case FIND_LEFT:
x_err = get_x_center(child) - x_start;
y_err = get_y_center(child) - y_start;
if(x_err >= 0) continue; /*It's on the right*/
if(LV_ABS(y_err) > h_half) continue; /*Too far*/
break;
case FIND_RIGHT:
x_err = get_x_center(child) - x_start;
y_err = get_y_center(child) - y_start;
if(x_err <= 0) continue; /*It's on the left*/
if(LV_ABS(y_err) > h_half) continue; /*Too far*/
break;
case FIND_TOP:
x_err = get_x_center(child) - x_start;
y_err = get_y_center(child) - y_start;
if(y_err >= 0) continue; /*It's on the bottom*/
break;
case FIND_BOTTOM:
x_err = get_x_center(child) - x_start;
y_err = get_y_center(child) - y_start;
if(y_err <= 0) continue; /*It's on the top*/
break;
case FIND_NEXT_ROW_FIRST_ITEM:
y_err = get_y_center(child) - y_start;
if(y_err <= 0) continue; /*It's on the top*/
x_err = lv_obj_get_x(child);
break;
case FIND_PREV_ROW_LAST_ITEM:
y_err = get_y_center(child) - y_start;
if(y_err >= 0) continue; /*It's on the bottom*/
x_err = obj->coords.x2 - child->coords.x2;
break;
case FIND_FIRST_ROW:
x_err = get_x_center(child) - x_start;
y_err = lv_obj_get_y(child);
break;
case FIND_LAST_ROW:
x_err = get_x_center(child) - x_start;
y_err = h_max - lv_obj_get_y(child);
}
if(guess == NULL ||
(y_err * y_err + x_err * x_err < y_err_guess * y_err_guess + x_err_guess * x_err_guess)) {
guess = child;
x_err_guess = x_err;
y_err_guess = y_err;
}
}
return guess;
}
static lv_obj_t * find_first_focusable(lv_obj_t * obj)
{
uint32_t child_cnt = lv_obj_get_child_cnt(obj);
uint32_t i;
for(i = 0; i < child_cnt; i++) {
lv_obj_t * child = lv_obj_get_child(obj, i);
if(obj_is_focuable(child)) return child;
}
return NULL;
}
static lv_obj_t * find_last_focusable(lv_obj_t * obj)
{
uint32_t child_cnt = lv_obj_get_child_cnt(obj);
int32_t i;
for(i = child_cnt - 1; i >= 0; i--) {
lv_obj_t * child = lv_obj_get_child(obj, i);
if(obj_is_focuable(child)) return child;
}
return NULL;
}
static bool obj_is_focuable(lv_obj_t * obj)
{
if(lv_obj_has_flag(obj, LV_OBJ_FLAG_HIDDEN)) return false;
if(lv_obj_has_flag(obj, LV_OBJ_FLAG_CLICKABLE | LV_OBJ_FLAG_CLICK_FOCUSABLE)) return true;
else return false;
}
static lv_coord_t get_x_center(lv_obj_t * obj)
{
return obj->coords.x1 + lv_area_get_width(&obj->coords) / 2;
}
static lv_coord_t get_y_center(lv_obj_t * obj)
{
return obj->coords.y1 + lv_area_get_height(&obj->coords) / 2;
}
#endif /*LV_USE_GRIDNAV*/

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,146 @@
/**
* @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_K9_NUMBER,
} 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 */
lv_ime_pinyin_mode_t mode; /* Set mode, 1: 26-key input(k26), 0: 9-key input(k9). Default: 1. */
} lv_ime_pinyin_t;
/***********************
* GLOBAL VARIABLES
***********************/
extern const lv_obj_class_t lv_ime_pinyin_class;
/**********************
* GLOBAL PROTOTYPES
**********************/
lv_obj_t * lv_ime_pinyin_create(lv_obj_t * parent);
/*=====================
* Setter functions
*====================*/
/**
* Set the keyboard of Pinyin input method.
* @param obj pointer to a Pinyin input method object
* @param dict pointer to a Pinyin input method keyboard
*/
void lv_ime_pinyin_set_keyboard(lv_obj_t * obj, lv_obj_t * kb);
/**
* Set the dictionary of Pinyin input method.
* @param obj pointer to a Pinyin input method object
* @param dict pointer to a Pinyin input method dictionary
*/
void lv_ime_pinyin_set_dict(lv_obj_t * obj, lv_pinyin_dict_t * dict);
/**
* Set mode, 26-key input(k26) or 9-key input(k9).
* @param obj pointer to a Pinyin input method object
* @param mode the mode from 'lv_ime_pinyin_mode_t'
*/
void lv_ime_pinyin_set_mode(lv_obj_t * obj, lv_ime_pinyin_mode_t mode);
/*=====================
* Getter functions
*====================*/
/**
* Set the dictionary of Pinyin input method.
* @param obj pointer to a Pinyin IME object
* @return pointer to the Pinyin IME keyboard
*/
lv_obj_t * lv_ime_pinyin_get_kb(lv_obj_t * obj);
/**
* Set the dictionary of Pinyin input method.
* @param obj pointer to a Pinyin input method object
* @return pointer to the Pinyin input method candidate panel
*/
lv_obj_t * lv_ime_pinyin_get_cand_panel(lv_obj_t * obj);
/**
* Set the dictionary of Pinyin input method.
* @param obj pointer to a Pinyin input method object
* @return pointer to the Pinyin input method dictionary
*/
lv_pinyin_dict_t * lv_ime_pinyin_get_dict(lv_obj_t * obj);
/*=====================
* Other functions
*====================*/
/**********************
* MACROS
**********************/
#endif /*LV_IME_PINYIN*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_USE_IME_PINYIN*/

View File

@@ -0,0 +1,142 @@
/**
* @file lv_imgfont.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_imgfont.h"
#if LV_USE_IMGFONT
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_font_t * font;
lv_imgfont_get_path_cb_t path_cb;
void * user_data;
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_imgfont_get_path_cb_t path_cb, void * user_data)
{
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_malloc(size);
if(dsc == NULL) return NULL;
lv_memzero(dsc, size);
dsc->font = (lv_font_t *)(((char *)dsc) + sizeof(imgfont_dsc_t));
dsc->path_cb = path_cb;
dsc->user_data = user_data;
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)
{
LV_ASSERT_NULL(font);
imgfont_dsc_t * dsc = (imgfont_dsc_t *)font->dsc;
lv_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;
lv_coord_t offset_y = 0;
if(!dsc->path_cb(dsc->font, dsc->path, LV_IMGFONT_PATH_MAX_LEN, unicode, unicode_next, &offset_y, dsc->user_data)) {
return false;
}
const lv_img_header_t * img_header;
#if LV_IMGFONT_USE_IMG_CACHE_HEADER
lv_color_t color = { 0 };
_lv_img_cache_entry_t * entry = _lv_img_cache_open(dsc->path, color, 0);
if(entry == NULL) {
return false;
}
img_header = &entry->dec_dsc.header;
#else
lv_img_header_t header;
if(lv_img_decoder_get_info(dsc->path, &header) != LV_RES_OK) {
return false;
}
img_header = &header;
#endif
dsc_out->is_placeholder = 0;
dsc_out->adv_w = img_header->w;
dsc_out->box_w = img_header->w;
dsc_out->box_h = img_header->h;
dsc_out->bpp = LV_IMGFONT_BPP; /* is image identifier */
dsc_out->ofs_x = 0;
dsc_out->ofs_y = offset_y;
return true;
}
#endif /*LV_USE_IMGFONT*/

View File

@@ -0,0 +1,61 @@
/**
* @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_imgfont_get_path_cb_t)(const lv_font_t * font, void * img_src,
uint16_t len, uint32_t unicode, uint32_t unicode_next,
lv_coord_t * offset_y, void * user_data);
/**********************
* 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_imgfont_get_path_cb_t path_cb, void * user_data);
/**
* Destroy a image font that has been created.
* @param font pointer to image font handle.
*/
void lv_imgfont_destroy(lv_font_t * font);
/**********************
* MACROS
**********************/
#endif /*LV_USE_IMGFONT*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /* LV_IMGFONT_H */

View File

@@ -0,0 +1,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_memzero(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_malloc(sizeof(lv_monkey_t));
LV_ASSERT_MALLOC(monkey);
lv_memzero(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_free(monkey);
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_monkey_read_cb(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
{
lv_monkey_t * monkey = indev_drv->user_data;
data->btn_id = monkey->indev_data.btn_id;
data->point = monkey->indev_data.point;
data->enc_diff = monkey->indev_data.enc_diff;
data->state = monkey->indev_data.state;
}
static int32_t lv_monkey_random(int32_t howsmall, int32_t howbig)
{
if(howsmall >= howbig) {
return howsmall;
}
int32_t diff = howbig - howsmall;
return (int32_t)lv_rand(0, diff) + howsmall;
}
static void lv_monkey_timer_cb(lv_timer_t * timer)
{
lv_monkey_t * monkey = timer->user_data;
lv_indev_data_t * data = &monkey->indev_data;
switch(monkey->indev_drv.type) {
case LV_INDEV_TYPE_POINTER:
data->point.x = (lv_coord_t)lv_monkey_random(0, LV_HOR_RES - 1);
data->point.y = (lv_coord_t)lv_monkey_random(0, LV_VER_RES - 1);
break;
case LV_INDEV_TYPE_ENCODER:
data->enc_diff = (int16_t)lv_monkey_random(monkey->config.input_range.min, monkey->config.input_range.max);
break;
case LV_INDEV_TYPE_BUTTON:
data->btn_id = (uint32_t)lv_monkey_random(monkey->config.input_range.min, monkey->config.input_range.max);
break;
case LV_INDEV_TYPE_KEYPAD: {
int32_t index = lv_monkey_random(0, sizeof(lv_key_map) / sizeof(lv_key_map[0]) - 1);
data->key = lv_key_map[index];
break;
}
default:
break;
}
data->state = lv_monkey_random(0, 100) < 50 ? LV_INDEV_STATE_RELEASED : LV_INDEV_STATE_PRESSED;
lv_timer_set_period(monkey->timer, lv_monkey_random(monkey->config.period_range.min, monkey->config.period_range.max));
}
#endif /*LV_USE_MONKEY*/

View File

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

View File

@@ -0,0 +1,172 @@
/**
* @file lv_msg.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_msg.h"
#if LV_USE_MSG
#include "../../misc/lv_assert.h"
#include "../../misc/lv_ll.h"
#include "../../misc/lv_gc.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
typedef struct {
lv_msg_id_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(lv_msg_t * m);
static void obj_delete_event_cb(lv_event_t * e);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* GLOBAL VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_msg_init(void)
{
_lv_ll_init(&LV_GC_ROOT(_subs_ll), sizeof(sub_dsc_t));
}
void * lv_msg_subscribe(lv_msg_id_t msg_id, lv_msg_subscribe_cb_t cb, void * user_data)
{
sub_dsc_t * s = _lv_ll_ins_tail(&LV_GC_ROOT(_subs_ll));
LV_ASSERT_MALLOC(s);
if(s == NULL) return NULL;
lv_memzero(s, sizeof(*s));
s->msg_id = msg_id;
s->callback = cb;
s->user_data = user_data;
return s;
}
void * lv_msg_subscribe_obj(lv_msg_id_t msg_id, lv_obj_t * obj, void * user_data)
{
sub_dsc_t * s = lv_msg_subscribe(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(&LV_GC_ROOT(_subs_ll), s);
lv_free(s);
}
void lv_msg_send(lv_msg_id_t msg_id, const void * payload)
{
lv_msg_t m;
lv_memzero(&m, sizeof(m));
m.id = msg_id;
m.payload = payload;
notify(&m);
}
void lv_msg_update_value(void * v)
{
lv_msg_send((lv_msg_id_t)v, v);
}
lv_msg_id_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(&LV_GC_ROOT(_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(m);
}
}
}
static void obj_notify_cb(lv_msg_t * m)
{
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(&LV_GC_ROOT(_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(&LV_GC_ROOT(_subs_ll), s);
if(s->_priv_data == obj) {
lv_msg_unsubscribe(s);
}
s = s_next;
}
}
#endif /*LV_USE_MSG*/

View File

@@ -0,0 +1,133 @@
/**
* @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 lv_uintptr_t lv_msg_id_t;
typedef struct {
lv_msg_id_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)(lv_msg_t * msg);
/**********************
* 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_subscribe(lv_msg_id_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_subscribe_obj(lv_msg_id_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_subscribe` or `lv_msg_subscribe_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(lv_msg_id_t msg_id, const void * payload);
/**
* Send a message where the message ID is `v` (the value of the pointer)
* and the payload is `v`.
* It can be used to send unique messages when a variable changed.
* @param v pointer to a variable.
* @note to subscribe to a variable use `lv_msg_subscribe((lv_msg_id_t)v, msg_cb, user_data)`
* or `lv_msg_subscribe_obj((lv_msg_id_t)v, obj, user_data)`
*/
void lv_msg_update_value(void * v);
/**
* 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
*/
lv_msg_id_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
**********************/
/**********************
* MACROS
**********************/
#endif /*LV_USE_MSG*/
#ifdef __cplusplus
} /*extern "C"*/
#endif
#endif /*LV_MSG_H*/

View File

@@ -0,0 +1,218 @@
/**
* @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_8BIT:
break;
default:
LV_LOG_WARN("Not supported color format");
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;
if(cf == LV_IMG_CF_TRUE_COLOR_ALPHA) px_size = LV_IMG_PX_SIZE_ALPHA_BYTE;
else px_size = sizeof(lv_color_t);
return w * h * px_size;
}
/** 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_8BIT:
break;
default:
LV_LOG_WARN("Not supported color format");
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_memzero(dsc, sizeof(lv_img_dsc_t));
dsc->data = buf;
dsc->header.w = w;
dsc->header.h = h;
dsc->header.cf = cf;
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_ver_res(obj_disp);
driver.user_data = obj_disp->driver->user_data;
lv_disp_t fake_disp;
lv_memzero(&fake_disp, sizeof(lv_disp_t));
fake_disp.driver = &driver;
lv_draw_ctx_t * draw_ctx = lv_malloc(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;
if(dsc->header.cf == LV_IMG_CF_ALPHA_8BIT) draw_ctx->color_format = LV_COLOR_FORMAT_L8;
else if(dsc->header.cf != LV_IMG_CF_TRUE_COLOR_ALPHA) draw_ctx->render_with_alpha = false;
else draw_ctx->render_with_alpha = true;
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);
if(draw_ctx->buffer_convert) draw_ctx->buffer_convert(draw_ctx);
_lv_refr_set_disp_refreshing(refr_ori);
obj_disp->driver->draw_ctx_deinit(fake_disp.driver, draw_ctx);
lv_free(draw_ctx);
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_malloc(buff_size);
LV_ASSERT_MALLOC(buf);
if(buf == NULL) {
return NULL;
}
lv_img_dsc_t * dsc = lv_malloc(sizeof(lv_img_dsc_t));
LV_ASSERT_MALLOC(buf);
if(dsc == NULL) {
lv_free(buf);
return NULL;
}
if(lv_snapshot_take_to_buf(obj, cf, dsc, buf, buff_size) == LV_RES_INV) {
lv_free(buf);
lv_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_free((void *)dsc->data);
lv_free(dsc);
}
/**********************
* STATIC FUNCTIONS
**********************/
#endif /*LV_USE_SNAPSHOT*/

View File

@@ -0,0 +1,83 @@
/**
* @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 "../../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