diff --git a/lv_misc/font.c b/lv_misc/font.c index 060a251db..a119a103e 100644 --- a/lv_misc/font.c +++ b/lv_misc/font.c @@ -8,6 +8,7 @@ *********************/ #include #include "font.h" +#include "fonts/dejavu_8.h" #include "fonts/dejavu_10.h" #include "fonts/dejavu_14.h" #include "fonts/dejavu_20.h" diff --git a/lv_obj/lv_obj.c b/lv_obj/lv_obj.c index 75cc974e5..667b85ff6 100644 --- a/lv_obj/lv_obj.c +++ b/lv_obj/lv_obj.c @@ -87,7 +87,7 @@ void lv_obj_inv(lv_obj_t * obj) /*Invalidate the object only if it belongs to the 'act_scr'*/ lv_obj_t * act_scr_p = lv_scr_act(); if(lv_obj_get_scr(obj) == act_scr_p) { - /*Truncate the recursively on the parents*/ + /*Truncate recursively to the parents*/ area_t area_trunc; lv_obj_t * par = lv_obj_get_parent(obj); bool union_ok = true; @@ -1136,6 +1136,21 @@ lv_obj_t * lv_obj_get_child(lv_obj_t * obj, lv_obj_t * child) return NULL; } +/** + * Count the children of an object (only children directly on 'obj') + * @param obj pointer to an object + * @return children number of 'obj' + */ +uint16_t lv_obj_get_child_num(lv_obj_t * obj) +{ + lv_obj_t * i; + uint16_t cnt = 0; + + LL_READ(obj->child_ll, i) cnt++; + + return cnt; +} + /*--------------------- * Coordinate get *--------------------*/ diff --git a/lv_obj/lv_obj.h b/lv_obj/lv_obj.h index 8764f8c4b..08017417e 100644 --- a/lv_obj/lv_obj.h +++ b/lv_obj/lv_obj.h @@ -227,6 +227,8 @@ void lv_scr_load(lv_obj_t * scr); lv_obj_t * lv_obj_get_scr(lv_obj_t * obj); lv_obj_t * lv_obj_get_parent(lv_obj_t * obj); lv_obj_t * lv_obj_get_child(lv_obj_t * obj, lv_obj_t * child); +uint16_t lv_obj_get_child_num(lv_obj_t * obj); + /*Coordinate get*/ void lv_obj_get_cords(lv_obj_t * obj, area_t * cords_p); cord_t lv_obj_get_x(lv_obj_t * obj); diff --git a/lv_objx/lv_btn.c b/lv_objx/lv_btn.c index 53a6a004e..00c8c39d9 100644 --- a/lv_objx/lv_btn.c +++ b/lv_objx/lv_btn.c @@ -340,13 +340,11 @@ lv_btns_t * lv_btns_get(lv_btns_builtin_t style, lv_btns_t * copy) */ static bool lv_btn_design(lv_obj_t * btn, const area_t * mask, lv_design_mode_t mode) { - lv_btns_t * btns_p = lv_obj_get_style(btn); /* Because of the radius it is not sure the area is covered*/ if(mode == LV_DESIGN_COVER_CHK) { return ancestor_design_f(btn, mask, mode); } else if(mode == LV_DESIGN_DRAW_MAIN) { - opa_t opa = lv_obj_get_opa(btn); area_t area; lv_obj_get_cords(btn, &area); @@ -356,7 +354,7 @@ static bool lv_btn_design(lv_obj_t * btn, const area_t * mask, lv_design_mode_t lv_btn_style_load(btn, &rects_tmp); btn->style_p = &rects_tmp; ancestor_design_f(btn, mask, mode); /*Draw the rectangle*/ - btn->style_p = btns_tmp; /*Reload the origial butto style*/ + btn->style_p = btns_tmp; /*Reload the original button style*/ } return true; } diff --git a/lv_objx/lv_label.c b/lv_objx/lv_label.c index 7b12ae3f7..eb95bae5e 100644 --- a/lv_objx/lv_label.c +++ b/lv_objx/lv_label.c @@ -545,11 +545,10 @@ static void lv_labels_init(void) lv_labels_title.line_space = 4 * LV_STYLE_MULT; lv_labels_title.mid = 0; - memcpy(&lv_labels_txt, &lv_labels_def, sizeof(lv_labels_t)); lv_labels_txt.objs.color = COLOR_MAKE(0x16, 0x23, 0x34); - lv_labels_txt.letter_space = 1 * LV_STYLE_MULT; - lv_labels_txt.line_space = 2 * LV_STYLE_MULT; + lv_labels_txt.letter_space = 0 * LV_STYLE_MULT; + lv_labels_txt.line_space = 1 * LV_STYLE_MULT; lv_labels_txt.mid = 0; } diff --git a/lv_objx/lv_mbox.c b/lv_objx/lv_mbox.c new file mode 100644 index 000000000..ea9cf161f --- /dev/null +++ b/lv_objx/lv_mbox.c @@ -0,0 +1,411 @@ +/** + * @file lv_mbox.c + * + */ + + +/********************* + * INCLUDES + *********************/ +#include "lv_conf.h" +#if USE_LV_MBOX != 0 + +#include "lv_mbox.h" +#include "../lv_misc/anim.h" +/********************* + * DEFINES + *********************/ +#define LV_MBOX_CLOSE_FADE_TIME 750 /*ms*/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +#if 0 /*Unused*/ +static bool lv_mbox_design(lv_obj_t * mbox, const area_t * mask, lv_design_mode_t mode); +#endif +static void lv_temps_init(void); +static void lv_mbox_realign(lv_obj_t * mbox); + +/********************** + * STATIC VARIABLES + **********************/ +static lv_mboxs_t lv_mboxs_def; /*Default message box style*/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/*----------------- + * Create function + *-----------------*/ + +/** + * Create a message box objects + * @param par pointer to an object, it will be the parent of the new message box + * @param copy pointer to a message box object, if not NULL then the new object will be copied from it + * @return pointer to the created message box + */ +lv_obj_t * lv_mbox_create(lv_obj_t * par, lv_obj_t * copy) +{ + /*Create the ancestor message box*/ + lv_obj_t * new_mbox = lv_rect_create(par, copy); + dm_assert(new_mbox); + + /*Allocate the message box type specific extended data*/ + lv_mbox_ext_t * ext = lv_obj_alloc_ext(new_mbox, sizeof(lv_mbox_ext_t)); + dm_assert(ext); + + /*The signal and design functions are not copied so set them here*/ + lv_obj_set_signal_f(new_mbox, lv_mbox_signal); + + /* Let the design function of rect + lv_obj_set_design_f(new_mbox, lv_mbox_design); */ + + /*Init the new message box message box*/ + if(copy == NULL) { + lv_rect_set_layout(new_mbox, LV_RECT_LAYOUT_COL_L); + lv_rect_set_fit(new_mbox, true, true); + + ext->title = lv_label_create(new_mbox, NULL); + lv_label_set_text(ext->title, "MESSAGE BOX"); + + ext->txt = lv_label_create(new_mbox, NULL); + lv_label_set_text(ext->txt, "Text of the message box"); + + ext->btnh = lv_rect_create(new_mbox, NULL); + lv_rect_set_fit(ext->btnh, false, true); + lv_rect_set_layout(ext->btnh, LV_RECT_LAYOUT_GRID); + + lv_obj_set_style(new_mbox, lv_mboxs_get(LV_MBOXS_DEF, NULL)); + + lv_mbox_realign(new_mbox); + } + /*Copy an existing message box*/ + else { + lv_mbox_ext_t * copy_ext = lv_obj_get_ext(copy); + } + + return new_mbox; +} + +/** + * Signal function of the message box + * @param mbox pointer to a message box object + * @param sign a signal type from lv_signal_t enum + * @param param pointer to a signal specific variable + * @return true: the object is still valid (not deleted), false: the object become invalid + */ +bool lv_mbox_signal(lv_obj_t * mbox, lv_signal_t sign, void * param) +{ + bool valid; + + /* Include the ancient signal function */ + valid = lv_rect_signal(mbox, sign, param); + + /* The object can be deleted so check its validity and then + * make the object specific signal handling */ + if(valid != false) { + lv_mbox_ext_t * ext = lv_obj_get_ext(mbox); + lv_mboxs_t * style = lv_obj_get_style(mbox); + lv_obj_t * btn; + + switch(sign) { + case LV_SIGNAL_CLEANUP: + /*Nothing to cleanup. (No dynamically allocated memory in 'ext')*/ + break; + case LV_SIGNAL_CORD_CHG: + /*If the size is changed refresh the message box*/ + if(area_get_width(param) != lv_obj_get_width(mbox) || + area_get_height(param) != lv_obj_get_height(mbox)) { + lv_mbox_realign(mbox); + } + break; + case LV_SIGNAL_STYLE_CHG: + lv_obj_set_style(ext->title, &style->title); + lv_obj_set_style(ext->txt, &style->txt); + lv_obj_set_style(ext->btnh, &style->btnh); + + /*Refresh all the buttons*/ + btn = lv_obj_get_child(ext->btnh, NULL); + while(btn != NULL) { + /*Refresh the next button's style*/ + lv_obj_set_style(btn, &style->btn); + lv_obj_set_size(btn, style->btn_w, style->btn_h); + + /*Refresh the button label too*/ + lv_obj_set_style(lv_obj_get_child(btn, NULL), &style->btn_label); + btn = lv_obj_get_child(ext->btnh, NULL); + } + + /*Hide the title and/or buttons*/ + lv_obj_set_hidden(ext->title, style->hide_title == 0 ? false : true); + + if(style->hide_btns != 0 || lv_obj_get_child_num(ext->btnh) != 0) { + lv_obj_set_hidden(ext->btnh, true); + } else { + lv_obj_set_hidden(ext->btnh, false); + } + break; + default: + break; + } + } + + return valid; +} + +/*===================== + * Setter functions + *====================*/ + +/** + * Add a button to the message box + * @param mbox pointer to message box object + * @param btn_txt the text of the button + * @param rel_action a function which will be called when the button is relesed + * @return pointer to the created button (lv_btn) + */ +lv_obj_t * lv_mbox_add_btn(lv_obj_t * mbox, const char * btn_txt, bool (*rel_action)(lv_obj_t *, lv_dispi_t *)) +{ + lv_mbox_ext_t * ext = lv_obj_get_ext(mbox); + lv_mboxs_t * style = lv_obj_get_style(mbox); + + lv_obj_t * btn; + btn = lv_btn_create(ext->btnh, NULL); + lv_btn_set_rel_action(btn, rel_action); + lv_obj_set_style(btn, &style->btn); + lv_obj_set_size(btn, style->btn_w, style->btn_h); + + lv_obj_t * label; + label = lv_label_create(btn, NULL); + lv_obj_set_style(label, &style->btn_label); + lv_label_set_text(label, btn_txt); + + /*With 1 button set center layout but from 3 set grid*/ + uint16_t child_num = lv_obj_get_child_num(ext->btnh); + if(child_num == 1) { + lv_rect_set_layout(ext->btnh, LV_RECT_LAYOUT_CENTER); + } else if (child_num == 2) { + lv_rect_set_layout(ext->btnh, LV_RECT_LAYOUT_GRID); + } + + return btn; +} + +/** + * A release action which can be assigned to a message box button to close it + * @param btn pointer to the released button + * @param dispi pointer to the caller display input + * @return always false because the button is deleted with the mesage box + */ +bool lv_mbox_close_action(lv_obj_t * btn, lv_dispi_t * dispi) +{ + lv_obj_t * mbox = lv_mbox_get_from_btn(btn); + + lv_obj_del(mbox); + + return false; +} + +/** + * Automatically delete the message box after a given time + * @param mbox pointer to a message box object + * @param tout a time (in milliseconds) to wait before delete the message box + */ +void lv_mbox_auto_close(lv_obj_t * mbox, uint16_t tout) +{ + + lv_obj_anim(mbox, LV_ANIM_FADE | ANIM_OUT, LV_MBOX_CLOSE_FADE_TIME, tout, lv_obj_del); +} + + +/** + * Set the title of the message box + * @param mbox pointer to a message box + * @param title a '\0' terminated character string which will be the message box title + */ +void lv_mbox_set_title(lv_obj_t * mbox, const char * title) +{ + lv_mbox_ext_t * ext = lv_obj_get_ext(mbox); + + lv_label_set_text(ext->title, title); +} + +/** + * Set the text of the message box + * @param mbox pointer to a message box + * @param txt a '\0' terminated character string which will be the message box text + */ +void lv_mbox_set_txt(lv_obj_t * mbox, const char * txt) +{ + lv_mbox_ext_t * ext = lv_obj_get_ext(mbox); + + lv_label_set_text(ext->txt, txt); +} + + +/*===================== + * Getter functions + *====================*/ + +/** + * get the title of the message box + * @param mbox pointer to a message box object + * @return pointer to the title of the message box + */ +const char * lv_mbox_get_title(lv_obj_t * mbox) +{ + lv_mbox_ext_t * ext = lv_obj_get_ext(mbox); + + return lv_label_get_text(ext->title); +} + +/** + * Get the text of the message box + * @param mbox pointer to a message box object + * @return pointer to the text of the message box + */ +const char * lv_mbox_get_txt(lv_obj_t * mbox) +{ + lv_mbox_ext_t * ext = lv_obj_get_ext(mbox); + + return lv_label_get_text(ext->txt); +} + +/** + * Get the message box object from one of its button. + * It is useful in the button release actions where only the button is known + * @param btn pointer to a button of a message box + * @return pointer to the button's message box + */ +lv_obj_t * lv_mbox_get_from_btn(lv_obj_t * btn) +{ + lv_obj_t * btnh = lv_obj_get_parent(btn); + lv_obj_t * mbox = lv_obj_get_parent(btnh); + + return mbox; +} + + +/** + * Return with a pointer to a built-in style and/or copy it to a variable + * @param style a style name from lv_mboxs_builtin_t enum + * @param copy copy the style to this variable. (NULL if unused) + * @return pointer to an lv_mboxs_t style + */ +lv_mboxs_t * lv_mboxs_get(lv_mboxs_builtin_t style, lv_mboxs_t * copy) +{ + static bool style_inited = false; + + /*Make the style initialization if it is not done yet*/ + if(style_inited == false) { + lv_temps_init(); + style_inited = true; + } + + lv_mboxs_t *style_p; + + switch(style) { + case LV_MBOXS_DEF: + case LV_MBOXS_INFO: + case LV_MBOXS_WARN: + case LV_MBOXS_ERR: + case LV_MBOXS_BUBBLE: + style_p = &lv_mboxs_def; + break; + default: + style_p = &lv_mboxs_def; + } + + if(copy != NULL) { + if(style_p != NULL) memcpy(copy, style_p, sizeof(lv_mboxs_t)); + else memcpy(copy, &lv_mboxs_def, sizeof(lv_mboxs_t)); + } + + return style_p; +} + + + +/********************** + * STATIC FUNCTIONS + **********************/ + +#if 0 /*Not used*/ +/** + * Handle the drawing related tasks of the message boxs + * @param mbox pointer to an object + * @param mask the object will be drawn only in this area + * @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area + * (return 'true' if yes) + * LV_DESIGN_DRAW: draw the object (always return 'true') + * LV_DESIGN_DRAW_POST: drawing after every children are drawn + * @param return true/false, depends on 'mode' + */ +static bool lv_mbox_design(lv_obj_t * mbox, const area_t * mask, lv_design_mode_t mode) +{ + if(mode == LV_DESIGN_COVER_CHK) { + /*Return false if the object is not covers the mask_p area*/ + return false; + } + + + /*Draw the object*/ + + return true; +} +#endif + +/** + * Initialize the message box styles + */ +static void lv_temps_init(void) +{ + /*Default style*/ + lv_rects_get(LV_RECTS_DEF, &lv_mboxs_def.bg); + lv_mboxs_def.bg.light = 10 * LV_STYLE_MULT; + + lv_btns_get(LV_BTNS_DEF, &lv_mboxs_def.btn); + lv_mboxs_def.btn.light_en[LV_BTN_STATE_PR] = 0; + lv_mboxs_def.btn.light_en[LV_BTN_STATE_REL] = 0; + lv_labels_get(LV_LABELS_TITLE, &lv_mboxs_def.title); + lv_labels_get(LV_LABELS_TXT, &lv_mboxs_def.txt); + lv_labels_get(LV_LABELS_BTN, &lv_mboxs_def.btn_label); + lv_rects_get(LV_RECTS_TRANSP, &lv_mboxs_def.btnh); + lv_mboxs_def.btnh.hpad = 0; + lv_mboxs_def.btnh.vpad = 0; + + lv_mboxs_def.btn_w = 80 * LV_DOWNSCALE; + lv_mboxs_def.btn_h = 50 * LV_DOWNSCALE; + lv_mboxs_def.hide_btns = 0; + lv_mboxs_def.hide_title = 0; + + /*TODO add further styles*/ +} + +/** + * Realign the elements of the message box + * @param mbox pointer to message box object + */ +static void lv_mbox_realign(lv_obj_t * mbox) +{ + lv_mbox_ext_t * ext = lv_obj_get_ext(mbox); + lv_mboxs_t * style = lv_obj_get_style(mbox); + + if(ext->btnh == NULL || ext->title == NULL || ext->txt == NULL) return; + + lv_obj_set_width(ext->btnh, lv_obj_get_width(mbox) - 2 * style->bg.hpad); + + lv_obj_align(mbox, NULL, LV_ALIGN_CENTER, 0, 0); +} + + +#endif diff --git a/lv_objx/lv_mbox.h b/lv_objx/lv_mbox.h new file mode 100644 index 000000000..28aeb9bf3 --- /dev/null +++ b/lv_objx/lv_mbox.h @@ -0,0 +1,88 @@ +/** + * @file lv_mbox.h + * + */ + +#ifndef LV_MBOX_H +#define LV_MBOX_H + +/********************* + * INCLUDES + *********************/ +#include "lv_conf.h" +#if USE_LV_MBOX != 0 + +#include "../lv_obj/lv_obj.h" +#include "lv_rect.h" +#include "lv_btn.h" +#include "lv_label.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/*Style of message box*/ +typedef struct +{ + lv_rects_t bg; /*Style of ancestor*/ + /*New style element for this type */ + lv_labels_t title; + lv_labels_t txt; + lv_rects_t btnh; + lv_btns_t btn; + lv_labels_t btn_label; + cord_t btn_w; + cord_t btn_h; + uint8_t hide_title :1; + uint8_t hide_btns :1; +}lv_mboxs_t; + +/*Built-in styles of message box*/ +typedef enum +{ + LV_MBOXS_DEF, + LV_MBOXS_INFO, + LV_MBOXS_WARN, + LV_MBOXS_ERR, + LV_MBOXS_BUBBLE, +}lv_mboxs_builtin_t; + +/*Data of message box*/ +typedef struct +{ + lv_rect_ext_t rect; /*Ext. of ancestor*/ + /*New data for this type */ + lv_obj_t * title; + lv_obj_t * txt; + lv_obj_t * btnh; +}lv_mbox_ext_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ +lv_obj_t * lv_mbox_create(lv_obj_t * par, lv_obj_t * copy); +bool lv_mbox_signal(lv_obj_t * mbox, lv_signal_t sign, void * param); +lv_mboxs_t * lv_mboxs_get(lv_mboxs_builtin_t style, lv_mboxs_t * copy); + +lv_obj_t * lv_mbox_add_btn(lv_obj_t * mbox, const char * btn_txt, bool (*rel_action)(lv_obj_t *, lv_dispi_t *)); +bool lv_mbox_close_action (lv_obj_t * mbox, lv_dispi_t *dispi); +void lv_mbox_auto_close(lv_obj_t * mbox, uint16_t tout); +void lv_mbox_set_title(lv_obj_t * mbox, const char * title); +void lv_mbox_set_txt(lv_obj_t * mbox, const char * txt); + +const char * lv_mbox_get_title(lv_obj_t * mbox); +const char * lv_mbox_get_txt(lv_obj_t * mbox); +lv_obj_t * lv_mbox_get_from_btn(lv_obj_t * btn); + + +/********************** + * MACROS + **********************/ + +#endif + +#endif diff --git a/lv_objx/lv_objx_templ.c b/lv_objx/lv_objx_templ.c index b9c112891..e4e6c3fe6 100644 --- a/lv_objx/lv_objx_templ.c +++ b/lv_objx/lv_objx_templ.c @@ -56,25 +56,26 @@ static lv_templs_t lv_templs_def; /*Default template style*/ */ lv_obj_t * lv_templ_create(lv_obj_t * par, lv_obj_t * copy) { - /*Create the ancestor templect*/ - lv_templ_t * new_templ = lv_templ_create(par, copy); + /*Create the ancestor template*/ + /*TODO modify it to the ancestor create function */ + lv_obj_t * new_templ = lv_obj_create(par, copy); dm_assert(new_templ); - /*Allocate the templect type specific extended data*/ - lv_templ_ext_t * ext = lv_templ_alloc_ext(new_templ, sizeof(lv_templ_ext_t)); + /*Allocate the template type specific extended data*/ + lv_templ_ext_t * ext = lv_obj_alloc_ext(new_templ, sizeof(lv_templ_ext_t)); dm_assert(ext); /*The signal and design functions are not copied so set them here*/ - lv_templ_set_signal_f(new_templ, lv_templ_signal); - lv_templ_set_design_f(new_templ, lv_templ_design); + lv_obj_set_signal_f(new_templ, lv_templ_signal); + lv_obj_set_design_f(new_templ, lv_templ_design); - /*Init the new template templect*/ + /*Init the new template template*/ if(copy == NULL) { } - /*Copy an existing templect*/ + /*Copy an existing template*/ else { - lv_templ_ext_t * copy_ext = lv_templ_get_ext(copy); + lv_templ_ext_t * copy_ext = lv_obj_get_ext(copy); } return new_templ; diff --git a/lv_objx/lv_win.c b/lv_objx/lv_win.c index 9fd1a22d6..bac1d4b72 100644 --- a/lv_objx/lv_win.c +++ b/lv_objx/lv_win.c @@ -170,6 +170,7 @@ bool lv_win_signal(lv_obj_t * win, lv_signal_t sign, void * param) } break; case LV_SIGNAL_CORD_CHG: + /*If the size is changed refresh the window*/ if(area_get_width(param) != lv_obj_get_width(win) || area_get_height(param) != lv_obj_get_height(win)) { lv_win_realign(win); @@ -213,6 +214,21 @@ lv_obj_t * lv_win_add_ctrl_btn(lv_obj_t * win, const char * img_path, bool (*rel return btn; } +/** + * A release action which can be assigned to a window control button to close it + * @param btn pointer to the released button + * @param dispi pointer to the caller display input + * @return always false because the button is deleted with the window + */ +bool lv_win_close_action(lv_obj_t * btn, lv_dispi_t * dispi) +{ + lv_obj_t * win = lv_win_get_from_ctrl_btn(btn); + + lv_obj_del(win); + + return false; +} + /** * Set the title of a window * @param win pointer to a window object diff --git a/lv_objx/lv_win.h b/lv_objx/lv_win.h index 226eb384d..c3d52f850 100644 --- a/lv_objx/lv_win.h +++ b/lv_objx/lv_win.h @@ -69,6 +69,7 @@ bool lv_win_signal(lv_obj_t * win, lv_signal_t sign, void * param); lv_wins_t * lv_wins_get(lv_wins_builtin_t style, lv_wins_t * copy); lv_obj_t * lv_win_add_ctrl_btn(lv_obj_t * win, const char * img, bool (*rel_action)(lv_obj_t *, lv_dispi_t *)); +bool lv_win_close_action(lv_obj_t * btn, lv_dispi_t * dispi); void lv_win_set_title(lv_obj_t * win, const char * title); const char * lv_win_get_title(lv_obj_t * win); @@ -77,6 +78,6 @@ lv_obj_t * lv_win_get_from_ctrl_btn(lv_obj_t * ctrl_btn); * MACROS **********************/ -#endif +#endif /*USE_LV_WIN*/ -#endif +#endif /*LV_WIN_H*/ diff --git a/lvgl.h b/lvgl.h index 3d8ffebac..0c1fb9fd4 100644 --- a/lvgl.h +++ b/lvgl.h @@ -24,6 +24,7 @@ #include "lv_objx/lv_btnm.h" #include "lv_objx/lv_ta.h" #include "lv_objx/lv_win.h" +#include "lv_objx/lv_mbox.h" #include "lv_app/lv_app.h"