From 5da17ebf5066ce04c24c05d84a5f8125a1c2995f Mon Sep 17 00:00:00 2001 From: Gabor Date: Mon, 6 Feb 2017 10:06:18 +0100 Subject: [PATCH] lv_ddlist: drop down list new object type added --- lv_objx/lv_ddlist.c | 410 ++++++++++++++++++++++++++++++++++++++++++++ lv_objx/lv_ddlist.h | 113 ++++++++++++ 2 files changed, 523 insertions(+) create mode 100644 lv_objx/lv_ddlist.c create mode 100644 lv_objx/lv_ddlist.h diff --git a/lv_objx/lv_ddlist.c b/lv_objx/lv_ddlist.c new file mode 100644 index 000000000..83954065a --- /dev/null +++ b/lv_objx/lv_ddlist.c @@ -0,0 +1,410 @@ +/** + * @file lv_ddlist.c + * + */ + + +/********************* + * INCLUDES + *********************/ +#include "lv_conf.h" +#if USE_LV_DDLIST != 0 + +#include "lv_ddlist.h" +#include "../lv_draw/lv_draw.h" +#include "../lv_misc/anim.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static bool lv_ddlist_design(lv_obj_t * ddlist, const area_t * mask, lv_design_mode_t mode); +static lv_action_res_t lv_ddlist_rel_action(lv_obj_t * ddlist, lv_dispi_t * dispi); +static void lv_ddlist_refr_size(lv_obj_t * ddlist, bool anim_en); +static void lv_ddlist_pos_act_option(lv_obj_t * ddlist); +static void lv_ddlists_init(void); + +/********************** + * STATIC VARIABLES + **********************/ +static lv_ddlists_t lv_ddlists_def; /*Default drop down list style*/ +static lv_design_f_t ancestor_design_f; +static const char * def_options[] = {"Option 1", "Option 2", "Option 3", ""}; +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/*----------------- + * Create function + *-----------------*/ + +/** + * Create a drop down list objects + * @param par pointer to an object, it will be the parent of the new drop down list + * @param copy pointer to a drop down list object, if not NULL then the new object will be copied from it + * @return pointer to the created drop down list + */ +lv_obj_t * lv_ddlist_create(lv_obj_t * par, lv_obj_t * copy) +{ + /*Create the ancestor drop down list*/ + lv_obj_t * new_ddlist = lv_page_create(par, copy); + dm_assert(new_ddlist); + + /*Allocate the drop down list type specific extended data*/ + lv_ddlist_ext_t * ext = lv_obj_alloc_ext(new_ddlist, sizeof(lv_ddlist_ext_t)); + dm_assert(ext); + + /*Initialize the allocated 'ext' */ + ext->opt_label = NULL; + ext->cb = NULL; + ext->opened = 0; + ext->act_opt = 0; + + /*The signal and design functions are not copied so set them here*/ + if(ancestor_design_f == NULL) ancestor_design_f = lv_obj_get_design_f(new_ddlist); + + lv_obj_set_signal_f(new_ddlist, lv_ddlist_signal); + lv_obj_set_design_f(new_ddlist, lv_ddlist_design); + + /*Init the new drop down list drop down list*/ + if(copy == NULL) { + ext->opt_label = lv_label_create(new_ddlist, NULL); + lv_rect_set_fit(new_ddlist, true, false); + lv_page_set_rel_action(new_ddlist, lv_ddlist_rel_action); + lv_obj_set_style(new_ddlist, lv_ddlists_get(LV_DDLISTS_DEF, NULL)); + lv_ddlist_set_options(new_ddlist, def_options); + } + /*Copy an existing drop down list*/ + else { + lv_ddlist_ext_t * copy_ext = lv_obj_get_ext(copy); + ext->opt_label = lv_label_create(new_ddlist, copy_ext->opt_label); + lv_label_set_text(ext->opt_label, lv_label_get_text(copy_ext->opt_label)); + /*Refresh the style with new signal function*/ + lv_obj_refr_style(new_ddlist); + } + + return new_ddlist; +} + +/** + * Signal function of the drop down list + * @param ddlist pointer to a drop down list 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_ddlist_signal(lv_obj_t * ddlist, lv_signal_t sign, void * param) +{ + bool valid; + + /* Include the ancient signal function */ + valid = lv_page_signal(ddlist, sign, param); + + /* The object can be deleted so check its validity and then + * make the object specific signal handling */ + if(valid != false) { + lv_ddlist_ext_t * ext = lv_obj_get_ext(ddlist); + lv_ddlists_t * style = lv_obj_get_style(ddlist); + + switch(sign) { + case LV_SIGNAL_CLEANUP: + /*Nothing to cleanup. (No dynamically allocated memory in 'ext')*/ + break; + case LV_SIGNAL_STYLE_CHG: + lv_obj_set_style(ext->opt_label, &style->list_labels); + lv_ddlist_refr_size(ddlist, false); + break; + default: + break; + } + } + + return valid; +} + +/*===================== + * Setter functions + *====================*/ + +/** + * Set the options in a drop down list + * @param ddlist pointer to drop down list object + * @param options an array of strings wit the text of the options. + * The lest element has to be "" (empty string) + * E.g. const char * opts[] = {"apple", "banana", "orange", ""}; + */ +void lv_ddlist_set_options(lv_obj_t * ddlist, const char ** options) +{ + lv_ddlist_ext_t * ext = lv_obj_get_ext(ddlist); + + lv_label_set_text(ext->opt_label, ""); + uint16_t i = 0; + while(options[i][0] != '\0') { + lv_label_append_text(ext->opt_label, options[i]); + if(options[i + 1][0] != '\0') lv_label_append_text(ext->opt_label, "\n"); + i++; + } + + lv_ddlist_refr_size(ddlist, false); +} + +/** + * Set a function to call when a new option is chosen + * @param ddlist pointer to drop down list + * @param cb pointer to a call back function. Its prototype is: + * parameter 1: pointer to the drop down list + * parameter 2: id of the chosen item (0 ... number of options - 1) + * return LV_ACTION_RES_INV if the drop down list is deleted in the function else LV_ACTION_RES_OK + */ +void lv_ddlist_set_action(lv_obj_t * ddlist, lv_action_res_t (*cb)(lv_obj_t *, uint16_t)) +{ + lv_ddlist_ext_t * ext = lv_obj_get_ext(ddlist); + ext->cb = cb; +} + +/*===================== + * Getter functions + *====================*/ + +/** + * Get the options of a drop down list + * @param ddlist pointer to drop down list object + * @return the options separated by '\n'-s (E.g. "Option1\nOption2\nOption3") + */ +const char * lv_ddlist_get_options(lv_obj_t * ddlist) +{ + lv_ddlist_ext_t * ext = lv_obj_get_ext(ddlist); + return lv_label_get_text(ext->opt_label); +} + +/** + * Return with a pointer to a built-in style and/or copy it to a variable + * @param style a style name from lv_ddlists_builtin_t enum + * @param copy copy the style to this variable. (NULL if unused) + * @return pointer to an lv_ddlists_t style + */ +lv_ddlists_t * lv_ddlists_get(lv_ddlists_builtin_t style, lv_ddlists_t * copy) +{ + static bool style_inited = false; + + /*Make the style initialization if it is not done yet*/ + if(style_inited == false) { + lv_ddlists_init(); + style_inited = true; + } + + lv_ddlists_t *style_p; + + switch(style) { + case LV_DDLISTS_DEF: + style_p = &lv_ddlists_def; + break; + default: + style_p = &lv_ddlists_def; + } + + if(copy != NULL) { + if(style_p != NULL) memcpy(copy, style_p, sizeof(lv_ddlists_t)); + else memcpy(copy, &lv_ddlists_def, sizeof(lv_ddlists_t)); + } + + return style_p; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + + +/** + * Handle the drawing related tasks of the drop down lists + * @param ddlist 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_ddlist_design(lv_obj_t * ddlist, const area_t * mask, lv_design_mode_t mode) +{ + /*Return false if the object is not covers the mask_p area*/ + if(mode == LV_DESIGN_COVER_CHK) { + return ancestor_design_f(ddlist, mask, mode); + } + /*Draw the object*/ + else if(mode == LV_DESIGN_DRAW_MAIN) { + ancestor_design_f(ddlist, mask, mode); + + /*If the list is opened draw a rectangle below the selected item*/ + lv_ddlist_ext_t * ext = lv_obj_get_ext(ddlist); + if(ext->opened != 0) { + lv_ddlists_t * style = lv_obj_get_style(ddlist); + const font_t * font = font_get(style->list_labels.font); + area_t rect_area; + rect_area.y1 = ext->opt_label->cords.y1; + rect_area.y1 += ext->act_opt * (font_get_height(font) + style->list_labels.line_space); + rect_area.y1 -= style->sel_rects.vpad; + + rect_area.y2 = rect_area.y1 + font_get_height(font) + 2 * style->sel_rects.vpad; + rect_area.x1 = ext->opt_label->cords.x1 - style->pages.scrl_rects.hpad; + rect_area.x2 = rect_area.x1 + lv_obj_get_width(lv_page_get_scrl(ddlist)); + + lv_draw_rect(&rect_area, mask, &style->sel_rects, OPA_COVER); + } + } + /*Post draw when the children are drawn*/ + else if(mode == LV_DESIGN_DRAW_POST) { + ancestor_design_f(ddlist, mask, mode); + + } + + return true; +} + +/** + * Called when a drop down list is released to open it or set new option + * @param ddlist pointer to a drop down list object + * @param dispi pointer to the called display input + * @return LV_ACTION_RES_INV if the ddlist it deleted in the user callback else LV_ACTION_RES_OK + */ +static lv_action_res_t lv_ddlist_rel_action(lv_obj_t * ddlist, lv_dispi_t * dispi) +{ + lv_ddlist_ext_t * ext = lv_obj_get_ext(ddlist); + + if(ext->opened == 0) { /*Open the list*/ + ext->opened = 1; + lv_obj_set_drag(lv_page_get_scrl(ddlist), true); + } else { + ext->opened = 0; + /*Search the clicked option*/ + point_t p; + lv_dispi_get_point(dispi, &p); + p.x -= ext->opt_label->cords.x1; + p.y -= ext->opt_label->cords.y1; + uint16_t letter_i; + letter_i = lv_label_get_letter_on(ext->opt_label, &p); + + uint16_t new_opt = 0; + const char * txt = lv_label_get_text(ext->opt_label); + uint16_t i; + for(i = 0; i < letter_i; i++) { + if(txt[i] == '\n') new_opt ++; + } + + ext->act_opt = new_opt; + + if(ext->cb != NULL) { + ext->cb(ddlist, ext->act_opt); + } + } +#if LV_DDLIST_ANIM_TIME == 0 + lv_ddlist_refr_size(ddlist, false); +#else + lv_ddlist_refr_size(ddlist, true); +#endif + + return LV_ACTION_RES_OK; + +} + +/** + * Refresh the size of drop down list according its start (open or closed) + * @param ddlist poinr to a drop down list object + * @param anim_en true: refresh the size with an animation, false: do not use animations + */ +static void lv_ddlist_refr_size(lv_obj_t * ddlist, bool anim_en) +{ + lv_ddlist_ext_t * ext = lv_obj_get_ext(ddlist); + lv_ddlists_t * style = lv_obj_get_style(ddlist); + cord_t new_height; + if(ext->opened != 0) { /*Open the list*/ + new_height = lv_obj_get_height(lv_page_get_scrl(ddlist)) + 2 * style->pages.bg_rects.vpad; + lv_obj_t * parent = lv_obj_get_parent(ddlist); + if(new_height + ddlist->cords.y1 > parent->cords.y2) { + new_height = parent->cords.y2 - ddlist->cords.y1; + } + } else { /*Close the list*/ + const font_t * font = font_get(style->list_labels.font); + new_height = font_get_height(font) + 2 * style->sel_rects.vpad; + } + if(anim_en == false) { + lv_obj_set_height(ddlist, new_height); + lv_ddlist_pos_act_option(ddlist); + } else { + anim_t a; + a.var = ddlist; + a.start = lv_obj_get_height(ddlist); + a.end = new_height; + a.fp = (anim_fp_t)lv_obj_set_height; + a.path = anim_get_path(ANIM_PATH_LIN); + a.end_cb = (anim_cb_t)lv_ddlist_pos_act_option; + a.act_time = 0; + a.time = LV_DDLIST_ANIM_TIME; + a.playback = 0; + a.playback_pause = 0; + a.repeat = 0; + a.repeat_pause = 0; + + anim_create(&a); + } +} + +/** + * Set the position of list when it is closed to show the selected item + * @param ddlist pointer to a drop down list + */ +static void lv_ddlist_pos_act_option(lv_obj_t * ddlist) +{ + lv_ddlist_ext_t * ext = lv_obj_get_ext(ddlist); + lv_ddlists_t * style = lv_obj_get_style(ddlist); + const font_t * font = font_get(style->list_labels.font); + + lv_obj_set_y(lv_page_get_scrl(ddlist), + -(ext->act_opt * (font_get_height(font) + style->list_labels.line_space) + + style->pages.scrl_rects.vpad) + style->sel_rects.vpad); + +} + +/** + * Initialize the built-in drop down list styles + */ +static void lv_ddlists_init(void) +{ + /*Default style*/ + lv_pages_get(LV_PAGES_SIMPLE, &lv_ddlists_def.pages); + lv_ddlists_def.pages.bg_rects.objs.color = COLOR_WHITE; + lv_ddlists_def.pages.bg_rects.gcolor = COLOR_SILVER; + lv_ddlists_def.pages.bg_rects.bcolor = COLOR_GRAY; + + lv_ddlists_def.pages.bg_rects.hpad = 0 * LV_DOWNSCALE; + lv_ddlists_def.pages.bg_rects.vpad = 0 * LV_DOWNSCALE; + lv_ddlists_def.pages.bg_rects.opad = 0; + + lv_ddlists_def.pages.scrl_rects.hpad = 5 * LV_DOWNSCALE; + lv_ddlists_def.pages.scrl_rects.vpad = 5 * LV_DOWNSCALE; + lv_ddlists_def.pages.scrl_rects.opad = 0 * LV_DOWNSCALE; + + lv_ddlists_def.pages.sb_mode = LV_PAGE_SB_MODE_OFF; + + lv_labels_get(LV_LABELS_DEF, &lv_ddlists_def.list_labels); + lv_ddlists_def.list_labels.line_space = 15 * LV_DOWNSCALE; + + lv_rects_get(LV_RECTS_DEF, &lv_ddlists_def.sel_rects); + lv_ddlists_def.sel_rects.bwidth = 0; + lv_ddlists_def.sel_rects.round = 0; + lv_ddlists_def.sel_rects.vpad = 5 * LV_DOWNSCALE; +} + +#endif diff --git a/lv_objx/lv_ddlist.h b/lv_objx/lv_ddlist.h new file mode 100644 index 000000000..a793e8942 --- /dev/null +++ b/lv_objx/lv_ddlist.h @@ -0,0 +1,113 @@ +/** + * @file lv_ddlist.h + * + */ + +#ifndef LV_DDLIST_H +#define LV_DDLIST_H + +/********************* + * INCLUDES + *********************/ +#include "lv_conf.h" +#if USE_LV_DDLIST != 0 + +#include "../lv_obj/lv_obj.h" +#include "../lv_objx/lv_page.h" +#include "../lv_objx/lv_label.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ +/*Data of drop down list*/ +typedef struct +{ + lv_page_ext_t page; /*Ext. of ancestor*/ + /*New data for this type */ + lv_obj_t * opt_label; /*Label for the options*/ + lv_action_res_t (*cb)(lv_obj_t *, uint16_t); + uint16_t act_opt; + uint8_t opened :1; +}lv_ddlist_ext_t; + +/*Style of drop down list*/ +typedef struct +{ + lv_pages_t pages; /*Style of ancestor*/ + /*New style element for this type */ + lv_rects_t sel_rects; + lv_labels_t list_labels; +}lv_ddlists_t; + +/*Built-in styles of drop down list*/ +typedef enum +{ + LV_DDLISTS_DEF, +}lv_ddlists_builtin_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Create a drop down list objects + * @param par pointer to an object, it will be the parent of the new drop down list + * @param copy pointer to a drop down list object, if not NULL then the new object will be copied from it + * @return pointer to the created drop down list + */ +lv_obj_t * lv_ddlist_create(lv_obj_t * par, lv_obj_t * copy); + +/** + * Signal function of the drop down list + * @param ddlist pointer to a drop down list 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_ddlist_signal(lv_obj_t * ddlist, lv_signal_t sign, void * param); + +/** + * Set the options in a drop down list + * @param ddlist pointer to drop down list object + * @param options an array of strings wit the text of the options. + * The lest element has to be "" (empty string) + * E.g. const char * opts[] = {"apple", "banana", "orange", ""}; + */ +void lv_ddlist_set_options(lv_obj_t * ddlist, const char ** options); + +/** + * Set a function to call when a new option is chosen + * @param ddlist pointer to drop down list + * @param cb pointer to a call back function. Its prototype is: + * parameter 1: pointer to the drop down list + * parameter 2: id of the chosen item (0 ... number of options - 1) + * return LV_ACTION_RES_INV if the drop down list is deleted in the function else LV_ACTION_RES_OK + */ +void lv_ddlist_set_action(lv_obj_t * ddlist, lv_action_res_t (*cb)(lv_obj_t *, uint16_t)); + +/** + * Get the options of a drop down list + * @param ddlist pointer to drop down list object + * @return the options separated by '\n'-s (E.g. "Option1\nOption2\nOption3") + */ +const char * lv_ddlist_get_options(lv_obj_t * ddlist); + +/** + * Return with a pointer to a built-in style and/or copy it to a variable + * @param style a style name from lv_ddlists_builtin_t enum + * @param copy copy the style to this variable. (NULL if unused) + * @return pointer to an lv_ddlists_t style + */ +lv_ddlists_t * lv_ddlists_get(lv_ddlists_builtin_t style, lv_ddlists_t * copy); + +/********************** + * MACROS + **********************/ + +#endif + +#endif