acid-drop

- Hacking the planet from a LilyGo T-Deck using custom firmware
git clone git://git.acid.vegas/acid-drop.git
Log | Files | Refs | Archive | README | LICENSE

lv_dropdown.c (37338B)

      1 /**
      2  * @file lv_dropdown.c
      3  *
      4  */
      5 
      6 /*********************
      7  *      INCLUDES
      8  *********************/
      9 #include "../core/lv_obj.h"
     10 #include "lv_dropdown.h"
     11 #if LV_USE_DROPDOWN != 0
     12 
     13 #include "../misc/lv_assert.h"
     14 #include "../draw/lv_draw.h"
     15 #include "../core/lv_group.h"
     16 #include "../core/lv_indev.h"
     17 #include "../core/lv_disp.h"
     18 #include "../font/lv_symbol_def.h"
     19 #include "../misc/lv_anim.h"
     20 #include "../misc/lv_math.h"
     21 #include "../misc/lv_txt_ap.h"
     22 #include <string.h>
     23 
     24 /*********************
     25  *      DEFINES
     26  *********************/
     27 #define MY_CLASS &lv_dropdown_class
     28 #define MY_CLASS_LIST &lv_dropdownlist_class
     29 
     30 #define LV_DROPDOWN_PR_NONE 0xFFFF
     31 
     32 /**********************
     33  *      TYPEDEFS
     34  **********************/
     35 
     36 /**********************
     37  *  STATIC PROTOTYPES
     38  **********************/
     39 static lv_obj_t * lv_dropdown_list_create(lv_obj_t * parent);
     40 static void lv_dropdown_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
     41 static void lv_dropdown_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
     42 static void lv_dropdown_event(const lv_obj_class_t * class_p, lv_event_t * e);
     43 static void draw_main(lv_event_t * e);
     44 
     45 static void lv_dropdownlist_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
     46 static void lv_dropdownlist_destructor(const lv_obj_class_t * class_p, lv_obj_t * list_obj);
     47 static void lv_dropdown_list_event(const lv_obj_class_t * class_p, lv_event_t * e);
     48 static void draw_list(lv_event_t * e);
     49 
     50 static void draw_box(lv_obj_t * dropdown_obj, lv_draw_ctx_t * draw_ctx, uint16_t id, lv_state_t state);
     51 static void draw_box_label(lv_obj_t * dropdown_obj, lv_draw_ctx_t * draw_ctx, uint16_t id, lv_state_t state);
     52 static lv_res_t btn_release_handler(lv_obj_t * obj);
     53 static lv_res_t list_release_handler(lv_obj_t * list_obj);
     54 static void list_press_handler(lv_obj_t * page);
     55 static uint16_t get_id_on_point(lv_obj_t * dropdown_obj, lv_coord_t y);
     56 static void position_to_selected(lv_obj_t * obj);
     57 static lv_obj_t * get_label(const lv_obj_t * obj);
     58 
     59 /**********************
     60  *  STATIC VARIABLES
     61  **********************/
     62 const lv_obj_class_t lv_dropdown_class = {
     63     .constructor_cb = lv_dropdown_constructor,
     64     .destructor_cb = lv_dropdown_destructor,
     65     .event_cb = lv_dropdown_event,
     66     .width_def = LV_DPI_DEF,
     67     .height_def = LV_SIZE_CONTENT,
     68     .instance_size = sizeof(lv_dropdown_t),
     69     .editable = LV_OBJ_CLASS_EDITABLE_TRUE,
     70     .group_def = LV_OBJ_CLASS_GROUP_DEF_TRUE,
     71     .base_class = &lv_obj_class
     72 };
     73 
     74 const lv_obj_class_t lv_dropdownlist_class = {
     75     .constructor_cb = lv_dropdownlist_constructor,
     76     .destructor_cb = lv_dropdownlist_destructor,
     77     .event_cb = lv_dropdown_list_event,
     78     .instance_size = sizeof(lv_dropdown_list_t),
     79     .base_class = &lv_obj_class
     80 };
     81 
     82 
     83 /**********************
     84  *      MACROS
     85  **********************/
     86 
     87 /**********************
     88  *   GLOBAL FUNCTIONS
     89  **********************/
     90 
     91 lv_obj_t * lv_dropdown_create(lv_obj_t * parent)
     92 {
     93     LV_LOG_INFO("begin");
     94     lv_obj_t * obj = lv_obj_class_create_obj(&lv_dropdown_class, parent);
     95     lv_obj_class_init_obj(obj);
     96     return obj;
     97 }
     98 
     99 /*=====================
    100  * Setter functions
    101  *====================*/
    102 
    103 void lv_dropdown_set_text(lv_obj_t * obj, const char * txt)
    104 {
    105     LV_ASSERT_OBJ(obj, MY_CLASS);
    106     lv_dropdown_t * dropdown = (lv_dropdown_t *)obj;
    107     if(dropdown->text == txt) return;
    108 
    109     dropdown->text = txt;
    110 
    111     lv_obj_invalidate(obj);
    112 }
    113 
    114 void lv_dropdown_set_options(lv_obj_t * obj, const char * options)
    115 {
    116     LV_ASSERT_OBJ(obj, MY_CLASS);
    117     LV_ASSERT_NULL(options);
    118 
    119     lv_dropdown_t * dropdown = (lv_dropdown_t *)obj;
    120 
    121     /*Count the '\n'-s to determine the number of options*/
    122     dropdown->option_cnt = 0;
    123     uint32_t i;
    124     for(i = 0; options[i] != '\0'; i++) {
    125         if(options[i] == '\n') dropdown->option_cnt++;
    126     }
    127     dropdown->option_cnt++;   /*Last option has no `\n`*/
    128     dropdown->sel_opt_id      = 0;
    129     dropdown->sel_opt_id_orig = 0;
    130 
    131     /*Allocate space for the new text*/
    132 #if LV_USE_ARABIC_PERSIAN_CHARS == 0
    133     size_t len = strlen(options) + 1;
    134 #else
    135     size_t len = _lv_txt_ap_calc_bytes_cnt(options) + 1;
    136 #endif
    137 
    138     if(dropdown->options != NULL && dropdown->static_txt == 0) {
    139         lv_mem_free(dropdown->options);
    140         dropdown->options = NULL;
    141     }
    142 
    143     dropdown->options = lv_mem_alloc(len);
    144 
    145     LV_ASSERT_MALLOC(dropdown->options);
    146     if(dropdown->options == NULL) return;
    147 
    148 #if LV_USE_ARABIC_PERSIAN_CHARS == 0
    149     strcpy(dropdown->options, options);
    150 #else
    151     _lv_txt_ap_proc(options, dropdown->options);
    152 #endif
    153 
    154     /*Now the text is dynamically allocated*/
    155     dropdown->static_txt = 0;
    156 
    157     lv_obj_invalidate(obj);
    158     if(dropdown->list) lv_obj_invalidate(dropdown->list);
    159 }
    160 
    161 void lv_dropdown_set_options_static(lv_obj_t * obj, const char * options)
    162 {
    163     LV_ASSERT_OBJ(obj, MY_CLASS);
    164     LV_ASSERT_NULL(options);
    165 
    166     lv_dropdown_t * dropdown = (lv_dropdown_t *)obj;
    167 
    168     /*Count the '\n'-s to determine the number of options*/
    169     dropdown->option_cnt = 0;
    170     uint32_t i;
    171     for(i = 0; options[i] != '\0'; i++) {
    172         if(options[i] == '\n') dropdown->option_cnt++;
    173     }
    174     dropdown->option_cnt++;   /*Last option has no `\n`*/
    175     dropdown->sel_opt_id      = 0;
    176     dropdown->sel_opt_id_orig = 0;
    177 
    178     if(dropdown->static_txt == 0 && dropdown->options != NULL) {
    179         lv_mem_free(dropdown->options);
    180         dropdown->options = NULL;
    181     }
    182 
    183     dropdown->static_txt = 1;
    184     dropdown->options = (char *)options;
    185 
    186     lv_obj_invalidate(obj);
    187     if(dropdown->list) lv_obj_invalidate(dropdown->list);
    188 }
    189 
    190 void lv_dropdown_add_option(lv_obj_t * obj, const char * option, uint32_t pos)
    191 {
    192     LV_ASSERT_OBJ(obj, MY_CLASS);
    193     LV_ASSERT_NULL(option);
    194 
    195     lv_dropdown_t * dropdown = (lv_dropdown_t *)obj;
    196 
    197     /*Convert static options to dynamic*/
    198     if(dropdown->static_txt != 0) {
    199         char * static_options = dropdown->options;
    200         size_t len = strlen(static_options) + 1;
    201 
    202         dropdown->options = lv_mem_alloc(len);
    203         LV_ASSERT_MALLOC(dropdown->options);
    204         if(dropdown->options == NULL) return;
    205 
    206         strcpy(dropdown->options, static_options);
    207         dropdown->static_txt = 0;
    208     }
    209 
    210     /*Allocate space for the new option*/
    211     size_t old_len = (dropdown->options == NULL) ? 0 : strlen(dropdown->options);
    212 #if LV_USE_ARABIC_PERSIAN_CHARS == 0
    213     size_t ins_len = strlen(option) + 1;
    214 #else
    215     size_t ins_len = _lv_txt_ap_calc_bytes_cnt(option) + 1;
    216 #endif
    217 
    218     size_t new_len = ins_len + old_len + 2; /*+2 for terminating NULL and possible \n*/
    219     dropdown->options        = lv_mem_realloc(dropdown->options, new_len + 1);
    220     LV_ASSERT_MALLOC(dropdown->options);
    221     if(dropdown->options == NULL) return;
    222 
    223     dropdown->options[old_len] = '\0';
    224 
    225     /*Find the insert character position*/
    226     uint32_t insert_pos = old_len;
    227     if(pos != LV_DROPDOWN_POS_LAST) {
    228         uint32_t opcnt = 0;
    229         for(insert_pos = 0; dropdown->options[insert_pos] != 0; insert_pos++) {
    230             if(opcnt == pos)
    231                 break;
    232             if(dropdown->options[insert_pos] == '\n')
    233                 opcnt++;
    234         }
    235     }
    236 
    237     /*Add delimiter to existing options*/
    238     if((insert_pos > 0) && (pos >= dropdown->option_cnt))
    239         _lv_txt_ins(dropdown->options, _lv_txt_encoded_get_char_id(dropdown->options, insert_pos++), "\n");
    240 
    241     /*Insert the new option, adding \n if necessary*/
    242     char * ins_buf = lv_mem_buf_get(ins_len + 2); /*+ 2 for terminating NULL and possible \n*/
    243     LV_ASSERT_MALLOC(ins_buf);
    244     if(ins_buf == NULL) return;
    245 #if LV_USE_ARABIC_PERSIAN_CHARS == 0
    246     strcpy(ins_buf, option);
    247 #else
    248     _lv_txt_ap_proc(option, ins_buf);
    249 #endif
    250     if(pos < dropdown->option_cnt) strcat(ins_buf, "\n");
    251 
    252     _lv_txt_ins(dropdown->options, _lv_txt_encoded_get_char_id(dropdown->options, insert_pos), ins_buf);
    253     lv_mem_buf_release(ins_buf);
    254 
    255     dropdown->option_cnt++;
    256 
    257     lv_obj_invalidate(obj);
    258     if(dropdown->list) lv_obj_invalidate(dropdown->list);
    259 }
    260 
    261 void lv_dropdown_clear_options(lv_obj_t * obj)
    262 {
    263     LV_ASSERT_OBJ(obj, MY_CLASS);
    264     lv_dropdown_t * dropdown = (lv_dropdown_t *)obj;
    265     if(dropdown->options == NULL) return;
    266 
    267     if(dropdown->static_txt == 0)
    268         lv_mem_free(dropdown->options);
    269 
    270     dropdown->options = NULL;
    271     dropdown->static_txt = 0;
    272     dropdown->option_cnt = 0;
    273 
    274     lv_obj_invalidate(obj);
    275     if(dropdown->list) lv_obj_invalidate(dropdown->list);
    276 }
    277 
    278 void lv_dropdown_set_selected(lv_obj_t * obj, uint16_t sel_opt)
    279 {
    280     LV_ASSERT_OBJ(obj, MY_CLASS);
    281 
    282     lv_dropdown_t * dropdown = (lv_dropdown_t *)obj;
    283     if(dropdown->sel_opt_id == sel_opt) return;
    284 
    285     dropdown->sel_opt_id      = sel_opt < dropdown->option_cnt ? sel_opt : dropdown->option_cnt - 1;
    286     dropdown->sel_opt_id_orig = dropdown->sel_opt_id;
    287 
    288     lv_obj_invalidate(obj);
    289 }
    290 
    291 void lv_dropdown_set_dir(lv_obj_t * obj, lv_dir_t dir)
    292 {
    293     LV_ASSERT_OBJ(obj, MY_CLASS);
    294 
    295     lv_dropdown_t * dropdown = (lv_dropdown_t *)obj;
    296     if(dropdown->dir == dir) return;
    297 
    298     dropdown->dir = dir;
    299 
    300     lv_obj_invalidate(obj);
    301 }
    302 
    303 void lv_dropdown_set_symbol(lv_obj_t * obj, const void * symbol)
    304 {
    305     LV_ASSERT_OBJ(obj, MY_CLASS);
    306 
    307     lv_dropdown_t * dropdown = (lv_dropdown_t *)obj;
    308     dropdown->symbol = symbol;
    309     lv_obj_invalidate(obj);
    310 }
    311 
    312 void lv_dropdown_set_selected_highlight(lv_obj_t * obj, bool en)
    313 {
    314     LV_ASSERT_OBJ(obj, MY_CLASS);
    315 
    316     lv_dropdown_t * dropdown = (lv_dropdown_t *)obj;
    317     dropdown->selected_highlight = en;
    318     if(dropdown->list) lv_obj_invalidate(dropdown->list);
    319 }
    320 
    321 /*=====================
    322  * Getter functions
    323  *====================*/
    324 
    325 lv_obj_t * lv_dropdown_get_list(lv_obj_t * obj)
    326 {
    327     LV_ASSERT_OBJ(obj, MY_CLASS);
    328     lv_dropdown_t * dropdown = (lv_dropdown_t *)obj;
    329 
    330     return dropdown->list;
    331 }
    332 
    333 const char * lv_dropdown_get_text(lv_obj_t * obj)
    334 {
    335     LV_ASSERT_OBJ(obj, MY_CLASS);
    336     lv_dropdown_t * dropdown = (lv_dropdown_t *)obj;
    337 
    338     return dropdown->text;
    339 }
    340 
    341 const char * lv_dropdown_get_options(const lv_obj_t * obj)
    342 {
    343     LV_ASSERT_OBJ(obj, MY_CLASS);
    344 
    345     lv_dropdown_t * dropdown = (lv_dropdown_t *)obj;
    346     return dropdown->options == NULL ? "" : dropdown->options;
    347 }
    348 
    349 uint16_t lv_dropdown_get_selected(const lv_obj_t * obj)
    350 {
    351     LV_ASSERT_OBJ(obj, MY_CLASS);
    352 
    353     lv_dropdown_t * dropdown = (lv_dropdown_t *)obj;
    354 
    355     return dropdown->sel_opt_id;
    356 }
    357 
    358 uint16_t lv_dropdown_get_option_cnt(const lv_obj_t * obj)
    359 {
    360     LV_ASSERT_OBJ(obj, MY_CLASS);
    361 
    362     lv_dropdown_t * dropdown = (lv_dropdown_t *)obj;
    363 
    364     return dropdown->option_cnt;
    365 }
    366 
    367 void lv_dropdown_get_selected_str(const lv_obj_t * obj, char * buf, uint32_t buf_size)
    368 {
    369     LV_ASSERT_OBJ(obj, MY_CLASS);
    370 
    371     lv_dropdown_t * dropdown = (lv_dropdown_t *)obj;
    372 
    373     uint32_t i;
    374     uint32_t line        = 0;
    375     size_t txt_len;
    376 
    377     if(dropdown->options)  {
    378         txt_len     = strlen(dropdown->options);
    379     }
    380     else {
    381         buf[0] = '\0';
    382         return;
    383     }
    384 
    385     for(i = 0; i < txt_len && line != dropdown->sel_opt_id_orig; i++) {
    386         if(dropdown->options[i] == '\n') line++;
    387     }
    388 
    389     uint32_t c;
    390     for(c = 0; i < txt_len && dropdown->options[i] != '\n'; c++, i++) {
    391         if(buf_size && c >= buf_size - 1) {
    392             LV_LOG_WARN("lv_dropdown_get_selected_str: the buffer was too small");
    393             break;
    394         }
    395         buf[c] = dropdown->options[i];
    396     }
    397 
    398     buf[c] = '\0';
    399 }
    400 
    401 int32_t lv_dropdown_get_option_index(lv_obj_t * obj, const char * option)
    402 {
    403     const char * opts = lv_dropdown_get_options(obj);
    404     uint32_t char_i = 0;
    405     uint32_t opt_i = 0;
    406     const char * start = opts;
    407 
    408     while(start[char_i] != '\0') {
    409         for(char_i = 0; (start[char_i] != '\n') && (start[char_i] != '\0'); char_i++);
    410 
    411         if(memcmp(start, option, LV_MIN(strlen(option), char_i)) == 0) return opt_i;
    412         start = &start[char_i];
    413         if(start[0] == '\n') start++;
    414         opt_i++;
    415     }
    416 
    417     return -1;
    418 }
    419 
    420 
    421 const char * lv_dropdown_get_symbol(lv_obj_t * obj)
    422 {
    423     LV_ASSERT_OBJ(obj, MY_CLASS);
    424     lv_dropdown_t * dropdown = (lv_dropdown_t *)obj;
    425     return dropdown->symbol;
    426 }
    427 
    428 bool lv_dropdown_get_selected_highlight(lv_obj_t * obj)
    429 {
    430     LV_ASSERT_OBJ(obj, MY_CLASS);
    431     lv_dropdown_t * dropdown = (lv_dropdown_t *)obj;
    432     return dropdown->selected_highlight;
    433 }
    434 
    435 lv_dir_t lv_dropdown_get_dir(const lv_obj_t * obj)
    436 {
    437     LV_ASSERT_OBJ(obj, MY_CLASS);
    438     lv_dropdown_t * dropdown = (lv_dropdown_t *)obj;
    439     return dropdown->dir;
    440 }
    441 
    442 /*=====================
    443  * Other functions
    444  *====================*/
    445 
    446 void lv_dropdown_open(lv_obj_t * dropdown_obj)
    447 {
    448     LV_ASSERT_OBJ(dropdown_obj, MY_CLASS);
    449 
    450     lv_dropdown_t * dropdown = (lv_dropdown_t *)dropdown_obj;
    451 
    452     lv_obj_add_state(dropdown_obj, LV_STATE_CHECKED);
    453     lv_obj_set_parent(dropdown->list, lv_obj_get_screen(dropdown_obj));
    454     lv_obj_move_to_index(dropdown->list, -1);
    455     lv_obj_clear_flag(dropdown->list, LV_OBJ_FLAG_HIDDEN);
    456 
    457     /*To allow styling the list*/
    458     lv_event_send(dropdown_obj, LV_EVENT_READY, NULL);
    459 
    460     lv_obj_t * label = get_label(dropdown_obj);
    461     lv_label_set_text_static(label, dropdown->options);
    462     lv_obj_set_width(dropdown->list, LV_SIZE_CONTENT);
    463 
    464     lv_obj_update_layout(label);
    465     /*Set smaller width to the width of the button*/
    466     if(lv_obj_get_width(dropdown->list) <= lv_obj_get_width(dropdown_obj) &&
    467        (dropdown->dir == LV_DIR_TOP || dropdown->dir == LV_DIR_BOTTOM)) {
    468         lv_obj_set_width(dropdown->list, lv_obj_get_width(dropdown_obj));
    469     }
    470 
    471     lv_coord_t label_h = lv_obj_get_height(label);
    472     lv_coord_t border_width = lv_obj_get_style_border_width(dropdown->list, LV_PART_MAIN);
    473     lv_coord_t top = lv_obj_get_style_pad_top(dropdown->list, LV_PART_MAIN) + border_width;
    474     lv_coord_t bottom = lv_obj_get_style_pad_bottom(dropdown->list, LV_PART_MAIN) + border_width;
    475 
    476     lv_coord_t list_fit_h = label_h + top + bottom;
    477     lv_coord_t list_h = list_fit_h;
    478 
    479     lv_dir_t dir = dropdown->dir;
    480     /*No space on the bottom? See if top is better.*/
    481     if(dropdown->dir == LV_DIR_BOTTOM) {
    482         if(dropdown_obj->coords.y2 + list_h > LV_VER_RES) {
    483             if(dropdown_obj->coords.y1 > LV_VER_RES - dropdown_obj->coords.y2) {
    484                 /*There is more space on the top, so make it drop up*/
    485                 dir = LV_DIR_TOP;
    486                 list_h = dropdown_obj->coords.y1 - 1;
    487             }
    488             else {
    489                 list_h = LV_VER_RES - dropdown_obj->coords.y2 - 1 ;
    490             }
    491         }
    492     }
    493     /*No space on the top? See if bottom is better.*/
    494     else if(dropdown->dir == LV_DIR_TOP) {
    495         if(dropdown_obj->coords.y1 - list_h < 0) {
    496             if(dropdown_obj->coords.y1 < LV_VER_RES - dropdown_obj->coords.y2) {
    497                 /*There is more space on the top, so make it drop up*/
    498                 dir = LV_DIR_BOTTOM;
    499                 list_h = LV_VER_RES - dropdown_obj->coords.y2;
    500             }
    501             else {
    502                 list_h = dropdown_obj->coords.y1;
    503             }
    504         }
    505     }
    506 
    507     if(list_h > list_fit_h) list_h = list_fit_h;
    508     lv_obj_set_height(dropdown->list, list_h);
    509 
    510     position_to_selected(dropdown_obj);
    511 
    512     if(dir == LV_DIR_BOTTOM)     lv_obj_align_to(dropdown->list, dropdown_obj, LV_ALIGN_OUT_BOTTOM_LEFT, 0, 0);
    513     else if(dir == LV_DIR_TOP)   lv_obj_align_to(dropdown->list, dropdown_obj, LV_ALIGN_OUT_TOP_LEFT, 0, 0);
    514     else if(dir == LV_DIR_LEFT)  lv_obj_align_to(dropdown->list, dropdown_obj, LV_ALIGN_OUT_LEFT_TOP, 0, 0);
    515     else if(dir == LV_DIR_RIGHT) lv_obj_align_to(dropdown->list, dropdown_obj, LV_ALIGN_OUT_RIGHT_TOP, 0, 0);
    516 
    517     lv_obj_update_layout(dropdown->list);
    518 
    519     if(dropdown->dir == LV_DIR_LEFT || dropdown->dir == LV_DIR_RIGHT) {
    520         lv_coord_t y1 = lv_obj_get_y(dropdown->list);
    521         lv_coord_t y2 = lv_obj_get_y2(dropdown->list);
    522         if(y2 >= LV_VER_RES) {
    523             lv_obj_set_y(dropdown->list, y1 - (y2 - LV_VER_RES) - 1);
    524         }
    525     }
    526 
    527     lv_text_align_t align = lv_obj_calculate_style_text_align(label, LV_PART_MAIN, dropdown->options);
    528 
    529     switch(align) {
    530         default:
    531         case LV_TEXT_ALIGN_LEFT:
    532             lv_obj_align(label, LV_ALIGN_TOP_LEFT, 0, 0);
    533             break;
    534         case LV_TEXT_ALIGN_RIGHT:
    535             lv_obj_align(label, LV_ALIGN_TOP_RIGHT, 0, 0);
    536             break;
    537         case LV_TEXT_ALIGN_CENTER:
    538             lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
    539             break;
    540 
    541     }
    542 }
    543 
    544 void lv_dropdown_close(lv_obj_t * obj)
    545 {
    546     LV_ASSERT_OBJ(obj, MY_CLASS);
    547 
    548     lv_obj_clear_state(obj, LV_STATE_CHECKED);
    549     lv_dropdown_t * dropdown = (lv_dropdown_t *)obj;
    550 
    551     dropdown->pr_opt_id = LV_DROPDOWN_PR_NONE;
    552     lv_obj_add_flag(dropdown->list, LV_OBJ_FLAG_HIDDEN);
    553 
    554     lv_event_send(obj, LV_EVENT_CANCEL, NULL);
    555 }
    556 
    557 bool lv_dropdown_is_open(lv_obj_t * obj)
    558 {
    559     LV_ASSERT_OBJ(obj, MY_CLASS);
    560     lv_dropdown_t * dropdown = (lv_dropdown_t *)obj;
    561 
    562     return lv_obj_has_flag(dropdown->list, LV_OBJ_FLAG_HIDDEN) ? false : true;
    563 }
    564 
    565 /**********************
    566  *   STATIC FUNCTIONS
    567  **********************/
    568 
    569 static lv_obj_t * lv_dropdown_list_create(lv_obj_t * parent)
    570 {
    571     LV_LOG_INFO("begin");
    572     lv_obj_t * obj = lv_obj_class_create_obj(&lv_dropdownlist_class, parent);
    573     lv_obj_class_init_obj(obj);
    574     return obj;
    575 }
    576 
    577 static void lv_dropdown_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
    578 {
    579     LV_UNUSED(class_p);
    580     LV_TRACE_OBJ_CREATE("begin");
    581 
    582     lv_dropdown_t * dropdown = (lv_dropdown_t *)obj;
    583 
    584     /*Initialize the allocated 'ext'*/
    585     dropdown->list          = NULL;
    586     dropdown->options     = NULL;
    587     dropdown->symbol         = LV_SYMBOL_DOWN;
    588     dropdown->text         = NULL;
    589     dropdown->static_txt = 1;
    590     dropdown->selected_highlight = 1;
    591     dropdown->sel_opt_id      = 0;
    592     dropdown->sel_opt_id_orig = 0;
    593     dropdown->pr_opt_id = LV_DROPDOWN_PR_NONE;
    594     dropdown->option_cnt      = 0;
    595     dropdown->dir = LV_DIR_BOTTOM;
    596 
    597     lv_obj_add_flag(obj, LV_OBJ_FLAG_SCROLL_ON_FOCUS);
    598     lv_dropdown_set_options_static(obj, "Option 1\nOption 2\nOption 3");
    599 
    600     dropdown->list = lv_dropdown_list_create(lv_obj_get_screen(obj));
    601     lv_dropdown_list_t * list = (lv_dropdown_list_t *)dropdown->list;
    602     list->dropdown = obj;
    603 
    604     LV_TRACE_OBJ_CREATE("finished");
    605 }
    606 
    607 static void lv_dropdown_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
    608 {
    609     LV_UNUSED(class_p);
    610     lv_dropdown_t * dropdown = (lv_dropdown_t *)obj;
    611 
    612     if(dropdown->list) {
    613         lv_obj_del(dropdown->list);
    614         dropdown->list = NULL;
    615     }
    616 
    617     if(!dropdown->static_txt) {
    618         lv_mem_free(dropdown->options);
    619         dropdown->options = NULL;
    620     }
    621 }
    622 
    623 static void lv_dropdownlist_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
    624 {
    625     LV_UNUSED(class_p);
    626     LV_TRACE_OBJ_CREATE("begin");
    627 
    628     lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLL_ON_FOCUS);
    629     lv_obj_clear_flag(obj, LV_OBJ_FLAG_CLICK_FOCUSABLE);
    630     lv_obj_add_flag(obj, LV_OBJ_FLAG_IGNORE_LAYOUT);
    631     lv_obj_add_flag(obj, LV_OBJ_FLAG_HIDDEN);
    632 
    633     lv_label_create(obj);
    634 
    635     LV_TRACE_OBJ_CREATE("finished");
    636 }
    637 
    638 static void lv_dropdownlist_destructor(const lv_obj_class_t * class_p, lv_obj_t * list_obj)
    639 {
    640     LV_UNUSED(class_p);
    641     lv_dropdown_list_t * list = (lv_dropdown_list_t *)list_obj;
    642     lv_obj_t * dropdown_obj = list->dropdown;
    643     lv_dropdown_t * dropdown = (lv_dropdown_t *)dropdown_obj;
    644     dropdown->list = NULL;
    645 }
    646 
    647 static void lv_dropdown_event(const lv_obj_class_t * class_p, lv_event_t * e)
    648 {
    649     LV_UNUSED(class_p);
    650 
    651     lv_res_t res;
    652 
    653     /*Call the ancestor's event handler*/
    654     res = lv_obj_event_base(MY_CLASS, e);
    655     if(res != LV_RES_OK) return;
    656 
    657     lv_event_code_t code = lv_event_get_code(e);
    658     lv_obj_t * obj = lv_event_get_target(e);
    659     lv_dropdown_t * dropdown = (lv_dropdown_t *)obj;
    660 
    661     if(code == LV_EVENT_FOCUSED) {
    662         lv_group_t * g             = lv_obj_get_group(obj);
    663         bool editing               = lv_group_get_editing(g);
    664         lv_indev_type_t indev_type = lv_indev_get_type(lv_indev_get_act());
    665 
    666         /*Encoders need special handling*/
    667         if(indev_type == LV_INDEV_TYPE_ENCODER) {
    668             /*Open the list if editing*/
    669             if(editing) {
    670                 lv_dropdown_open(obj);
    671             }
    672             /*Close the list if navigating*/
    673             else {
    674                 dropdown->sel_opt_id = dropdown->sel_opt_id_orig;
    675                 lv_dropdown_close(obj);
    676             }
    677         }
    678     }
    679     else if(code == LV_EVENT_DEFOCUSED || code == LV_EVENT_LEAVE) {
    680         lv_dropdown_close(obj);
    681     }
    682     else if(code == LV_EVENT_RELEASED) {
    683         res = btn_release_handler(obj);
    684         if(res != LV_RES_OK) return;
    685     }
    686     else if(code == LV_EVENT_STYLE_CHANGED) {
    687         lv_obj_refresh_self_size(obj);
    688     }
    689     else if(code == LV_EVENT_SIZE_CHANGED) {
    690         lv_obj_refresh_self_size(obj);
    691     }
    692     else if(code == LV_EVENT_GET_SELF_SIZE) {
    693         lv_point_t * p = lv_event_get_param(e);
    694         const lv_font_t * font = lv_obj_get_style_text_font(obj, LV_PART_MAIN);
    695         p->y = lv_font_get_line_height(font);
    696     }
    697     else if(code == LV_EVENT_KEY) {
    698         char c = *((char *)lv_event_get_param(e));
    699         if(c == LV_KEY_RIGHT || c == LV_KEY_DOWN) {
    700             if(!lv_dropdown_is_open(obj)) {
    701                 lv_dropdown_open(obj);
    702             }
    703             else if(dropdown->sel_opt_id + 1 < dropdown->option_cnt) {
    704                 dropdown->sel_opt_id++;
    705                 position_to_selected(obj);
    706             }
    707         }
    708         else if(c == LV_KEY_LEFT || c == LV_KEY_UP) {
    709 
    710             if(!lv_dropdown_is_open(obj)) {
    711                 lv_dropdown_open(obj);
    712             }
    713             else if(dropdown->sel_opt_id > 0) {
    714                 dropdown->sel_opt_id--;
    715                 position_to_selected(obj);
    716             }
    717         }
    718         else if(c == LV_KEY_ESC) {
    719             dropdown->sel_opt_id = dropdown->sel_opt_id_orig;
    720             lv_dropdown_close(obj);
    721         }
    722         else if(c == LV_KEY_ENTER) {
    723             /* Handle the ENTER key only if it was send by an other object.
    724              * Do no process it if ENTER is sent by the dropdown because it's handled in LV_EVENT_RELEASED */
    725             lv_obj_t * indev_obj = lv_indev_get_obj_act();
    726             if(indev_obj != obj) {
    727                 res = btn_release_handler(obj);
    728                 if(res != LV_RES_OK) return;
    729             }
    730         }
    731     }
    732     else if(code == LV_EVENT_DRAW_MAIN) {
    733         draw_main(e);
    734     }
    735 }
    736 
    737 static void lv_dropdown_list_event(const lv_obj_class_t * class_p, lv_event_t * e)
    738 {
    739     LV_UNUSED(class_p);
    740 
    741     lv_res_t res;
    742 
    743     /*Call the ancestor's event handler*/
    744     lv_event_code_t code = lv_event_get_code(e);
    745     if(code != LV_EVENT_DRAW_POST) {
    746         res = lv_obj_event_base(MY_CLASS_LIST, e);
    747         if(res != LV_RES_OK) return;
    748     }
    749     lv_obj_t * list = lv_event_get_target(e);
    750     lv_obj_t * dropdown_obj = ((lv_dropdown_list_t *)list)->dropdown;
    751     lv_dropdown_t * dropdown = (lv_dropdown_t *)dropdown_obj;
    752 
    753     if(code == LV_EVENT_RELEASED) {
    754         if(lv_indev_get_scroll_obj(lv_indev_get_act()) == NULL) {
    755             list_release_handler(list);
    756         }
    757     }
    758     else if(code == LV_EVENT_PRESSED) {
    759         list_press_handler(list);
    760     }
    761     else if(code == LV_EVENT_SCROLL_BEGIN) {
    762         dropdown->pr_opt_id = LV_DROPDOWN_PR_NONE;
    763         lv_obj_invalidate(list);
    764     }
    765     else if(code == LV_EVENT_DRAW_POST) {
    766         draw_list(e);
    767         res = lv_obj_event_base(MY_CLASS_LIST, e);
    768         if(res != LV_RES_OK) return;
    769     }
    770 }
    771 
    772 
    773 static void draw_main(lv_event_t * e)
    774 {
    775     lv_obj_t * obj = lv_event_get_target(e);
    776     lv_dropdown_t * dropdown = (lv_dropdown_t *)obj;
    777     lv_draw_ctx_t * draw_ctx = lv_event_get_draw_ctx(e);
    778 
    779     lv_coord_t border_width = lv_obj_get_style_border_width(obj, LV_PART_MAIN);
    780     lv_coord_t left = lv_obj_get_style_pad_left(obj, LV_PART_MAIN) + border_width;
    781     lv_coord_t right = lv_obj_get_style_pad_right(obj, LV_PART_MAIN) + border_width;
    782     lv_coord_t top = lv_obj_get_style_pad_top(obj, LV_PART_MAIN) + border_width;
    783 
    784     lv_draw_label_dsc_t symbol_dsc;
    785     lv_draw_label_dsc_init(&symbol_dsc);
    786     lv_obj_init_draw_label_dsc(obj, LV_PART_INDICATOR, &symbol_dsc);
    787 
    788     /*If no text specified use the selected option*/
    789     const char * opt_txt;
    790     if(dropdown->text) opt_txt = dropdown->text;
    791     else {
    792         char * buf = lv_mem_buf_get(128);
    793         lv_dropdown_get_selected_str(obj, buf, 128);
    794         opt_txt = buf;
    795     }
    796 
    797     bool symbol_to_left = false;
    798     if(dropdown->dir == LV_DIR_LEFT) symbol_to_left = true;
    799     if(lv_obj_get_style_base_dir(obj, LV_PART_MAIN) == LV_BASE_DIR_RTL) symbol_to_left = true;
    800 
    801     if(dropdown->symbol) {
    802         lv_img_src_t symbol_type = lv_img_src_get_type(dropdown->symbol);
    803         lv_coord_t symbol_w;
    804         lv_coord_t symbol_h;
    805         if(symbol_type == LV_IMG_SRC_SYMBOL) {
    806             lv_point_t size;
    807             lv_txt_get_size(&size, dropdown->symbol, symbol_dsc.font, symbol_dsc.letter_space, symbol_dsc.line_space, LV_COORD_MAX,
    808                             symbol_dsc.flag);
    809             symbol_w = size.x;
    810             symbol_h = size.y;
    811         }
    812         else {
    813             lv_img_header_t header;
    814             lv_res_t res = lv_img_decoder_get_info(dropdown->symbol, &header);
    815             if(res == LV_RES_OK) {
    816                 symbol_w = header.w;
    817                 symbol_h = header.h;
    818             }
    819             else {
    820                 symbol_w = -1;
    821                 symbol_h = -1;
    822             }
    823         }
    824 
    825         lv_area_t symbol_area;
    826         if(symbol_to_left) {
    827             symbol_area.x1 = obj->coords.x1 + left;
    828             symbol_area.x2 = symbol_area.x1 + symbol_w - 1;
    829         }
    830         else {
    831             symbol_area.x1 = obj->coords.x2 - right - symbol_w;
    832             symbol_area.x2 = symbol_area.x1 + symbol_w - 1;
    833         }
    834 
    835         if(symbol_type == LV_IMG_SRC_SYMBOL) {
    836             symbol_area.y1 = obj->coords.y1 + top;
    837             symbol_area.y2 = symbol_area.y1 + symbol_h - 1;
    838             lv_draw_label(draw_ctx, &symbol_dsc, &symbol_area, dropdown->symbol, NULL);
    839         }
    840         else {
    841             symbol_area.y1 = obj->coords.y1 + (lv_obj_get_height(obj) - symbol_h) / 2;
    842             symbol_area.y2 = symbol_area.y1 + symbol_h - 1;
    843             lv_draw_img_dsc_t img_dsc;
    844             lv_draw_img_dsc_init(&img_dsc);
    845             lv_obj_init_draw_img_dsc(obj, LV_PART_INDICATOR, &img_dsc);
    846             img_dsc.pivot.x = symbol_w / 2;
    847             img_dsc.pivot.y = symbol_h / 2;
    848             img_dsc.angle = lv_obj_get_style_transform_angle(obj, LV_PART_INDICATOR);
    849             lv_draw_img(draw_ctx, &img_dsc, &symbol_area, dropdown->symbol);
    850         }
    851     }
    852 
    853     lv_draw_label_dsc_t label_dsc;
    854     lv_draw_label_dsc_init(&label_dsc);
    855     lv_obj_init_draw_label_dsc(obj, LV_PART_MAIN, &label_dsc);
    856 
    857     lv_point_t size;
    858     lv_txt_get_size(&size, opt_txt, label_dsc.font, label_dsc.letter_space, label_dsc.line_space, LV_COORD_MAX,
    859                     label_dsc.flag);
    860 
    861     lv_area_t txt_area;
    862     txt_area.y1 = obj->coords.y1 + top;
    863     txt_area.y2 = txt_area.y1 + size.y;
    864     /*Center align the text if no symbol*/
    865     if(dropdown->symbol == NULL) {
    866         txt_area.x1 = obj->coords.x1 + (lv_obj_get_width(obj) - size.x) / 2;
    867         txt_area.x2 = txt_area.x1 + size.x;
    868     }
    869     else {
    870         /*Text to the right*/
    871         if(symbol_to_left) {
    872             txt_area.x1 = obj->coords.x2 - right - size.x;
    873             txt_area.x2 = txt_area.x1 + size.x;
    874         }
    875         else {
    876             txt_area.x1 = obj->coords.x1 + left;
    877             txt_area.x2 = txt_area.x1 + size.x;
    878         }
    879     }
    880     lv_draw_label(draw_ctx, &label_dsc, &txt_area, opt_txt, NULL);
    881 
    882     if(dropdown->text == NULL) {
    883         lv_mem_buf_release((char *)opt_txt);
    884     }
    885 }
    886 
    887 static void draw_list(lv_event_t * e)
    888 {
    889     lv_obj_t * list_obj = lv_event_get_target(e);
    890     lv_dropdown_list_t * list = (lv_dropdown_list_t *)list_obj;
    891     lv_obj_t * dropdown_obj = list->dropdown;
    892     lv_dropdown_t * dropdown = (lv_dropdown_t *)dropdown_obj;
    893     lv_draw_ctx_t * draw_ctx = lv_event_get_draw_ctx(e);
    894 
    895     /* Clip area might be too large too to shadow but
    896      * the selected option can be drawn on only the background*/
    897     lv_area_t clip_area_core;
    898     bool has_common;
    899     has_common = _lv_area_intersect(&clip_area_core, draw_ctx->clip_area, &dropdown->list->coords);
    900     if(has_common) {
    901         const lv_area_t * clip_area_ori = draw_ctx->clip_area;
    902         draw_ctx->clip_area = &clip_area_core;
    903         if(dropdown->selected_highlight) {
    904             if(dropdown->pr_opt_id == dropdown->sel_opt_id) {
    905                 draw_box(dropdown_obj, draw_ctx, dropdown->pr_opt_id, LV_STATE_CHECKED | LV_STATE_PRESSED);
    906                 draw_box_label(dropdown_obj, draw_ctx, dropdown->pr_opt_id, LV_STATE_CHECKED | LV_STATE_PRESSED);
    907             }
    908             else {
    909                 draw_box(dropdown_obj, draw_ctx, dropdown->pr_opt_id, LV_STATE_PRESSED);
    910                 draw_box_label(dropdown_obj, draw_ctx, dropdown->pr_opt_id, LV_STATE_PRESSED);
    911                 draw_box(dropdown_obj, draw_ctx, dropdown->sel_opt_id, LV_STATE_CHECKED);
    912                 draw_box_label(dropdown_obj, draw_ctx, dropdown->sel_opt_id, LV_STATE_CHECKED);
    913             }
    914         }
    915         else {
    916             draw_box(dropdown_obj, draw_ctx, dropdown->pr_opt_id, LV_STATE_PRESSED);
    917             draw_box_label(dropdown_obj, draw_ctx, dropdown->pr_opt_id, LV_STATE_PRESSED);
    918         }
    919         draw_ctx->clip_area = clip_area_ori;
    920     }
    921 }
    922 
    923 static void draw_box(lv_obj_t * dropdown_obj, lv_draw_ctx_t * draw_ctx, uint16_t id, lv_state_t state)
    924 {
    925     if(id == LV_DROPDOWN_PR_NONE) return;
    926 
    927     lv_dropdown_t * dropdown = (lv_dropdown_t *)dropdown_obj;
    928     lv_obj_t * list_obj = dropdown->list;
    929     lv_state_t state_ori = list_obj->state;
    930 
    931     if(state != list_obj->state) {
    932         list_obj->state = state;
    933         list_obj->skip_trans = 1;
    934     }
    935 
    936     /*Draw a rectangle under the selected item*/
    937     const lv_font_t * font    = lv_obj_get_style_text_font(list_obj, LV_PART_SELECTED);
    938     lv_coord_t line_space = lv_obj_get_style_text_line_space(list_obj,  LV_PART_SELECTED);
    939     lv_coord_t font_h         = lv_font_get_line_height(font);
    940 
    941     /*Draw the selected*/
    942     lv_obj_t * label = get_label(dropdown_obj);
    943     lv_area_t rect_area;
    944     rect_area.y1 = label->coords.y1;
    945     rect_area.y1 += id * (font_h + line_space);
    946     rect_area.y1 -= line_space / 2;
    947 
    948     rect_area.y2 = rect_area.y1 + font_h + line_space - 1;
    949     rect_area.x1 = dropdown->list->coords.x1;
    950     rect_area.x2 = dropdown->list->coords.x2;
    951 
    952     lv_draw_rect_dsc_t sel_rect;
    953     lv_draw_rect_dsc_init(&sel_rect);
    954     lv_obj_init_draw_rect_dsc(list_obj,  LV_PART_SELECTED, &sel_rect);
    955     lv_draw_rect(draw_ctx, &sel_rect, &rect_area);
    956 
    957     list_obj->state = state_ori;
    958     list_obj->skip_trans = 0;
    959 }
    960 
    961 static void draw_box_label(lv_obj_t * dropdown_obj, lv_draw_ctx_t * draw_ctx, uint16_t id, lv_state_t state)
    962 {
    963     if(id == LV_DROPDOWN_PR_NONE) return;
    964 
    965     lv_dropdown_t * dropdown = (lv_dropdown_t *)dropdown_obj;
    966     lv_obj_t * list_obj = dropdown->list;
    967     lv_state_t state_orig = list_obj->state;
    968 
    969     if(state != list_obj->state) {
    970         list_obj->state = state;
    971         list_obj->skip_trans = 1;
    972     }
    973 
    974     lv_draw_label_dsc_t label_dsc;
    975     lv_draw_label_dsc_init(&label_dsc);
    976     lv_obj_init_draw_label_dsc(list_obj, LV_PART_SELECTED, &label_dsc);
    977 
    978     label_dsc.line_space = lv_obj_get_style_text_line_space(list_obj,
    979                                                             LV_PART_SELECTED);  /*Line space should come from the list*/
    980 
    981     lv_obj_t * label = get_label(dropdown_obj);
    982     if(label == NULL) return;
    983 
    984     lv_coord_t font_h        = lv_font_get_line_height(label_dsc.font);
    985 
    986     lv_area_t area_sel;
    987     area_sel.y1 = label->coords.y1;
    988     area_sel.y1 += id * (font_h + label_dsc.line_space);
    989     area_sel.y1 -= label_dsc.line_space / 2;
    990 
    991     area_sel.y2 = area_sel.y1 + font_h + label_dsc.line_space - 1;
    992     area_sel.x1 = list_obj->coords.x1;
    993     area_sel.x2 = list_obj->coords.x2;
    994     lv_area_t mask_sel;
    995     bool area_ok;
    996     area_ok = _lv_area_intersect(&mask_sel, draw_ctx->clip_area, &area_sel);
    997     if(area_ok) {
    998         const lv_area_t * clip_area_ori = draw_ctx->clip_area;
    999         draw_ctx->clip_area = &mask_sel;
   1000         lv_draw_label(draw_ctx, &label_dsc, &label->coords, lv_label_get_text(label), NULL);
   1001         draw_ctx->clip_area = clip_area_ori;
   1002     }
   1003     list_obj->state = state_orig;
   1004     list_obj->skip_trans = 0;
   1005 }
   1006 
   1007 
   1008 static lv_res_t btn_release_handler(lv_obj_t * obj)
   1009 {
   1010     lv_dropdown_t * dropdown = (lv_dropdown_t *)obj;
   1011     lv_indev_t * indev = lv_indev_get_act();
   1012     if(lv_indev_get_scroll_obj(indev) == NULL) {
   1013         if(lv_dropdown_is_open(obj)) {
   1014             lv_dropdown_close(obj);
   1015             if(dropdown->sel_opt_id_orig != dropdown->sel_opt_id) {
   1016                 dropdown->sel_opt_id_orig = dropdown->sel_opt_id;
   1017                 lv_res_t res;
   1018                 uint32_t id  = dropdown->sel_opt_id; /*Just to use uint32_t in event data*/
   1019                 res = lv_event_send(obj, LV_EVENT_VALUE_CHANGED, &id);
   1020                 if(res != LV_RES_OK) return res;
   1021                 lv_obj_invalidate(obj);
   1022             }
   1023             lv_indev_type_t indev_type = lv_indev_get_type(indev);
   1024             if(indev_type == LV_INDEV_TYPE_ENCODER) {
   1025                 lv_group_set_editing(lv_obj_get_group(obj), false);
   1026             }
   1027         }
   1028         else {
   1029             lv_dropdown_open(obj);
   1030         }
   1031     }
   1032     else {
   1033         dropdown->sel_opt_id = dropdown->sel_opt_id_orig;
   1034         lv_obj_invalidate(obj);
   1035     }
   1036     return LV_RES_OK;
   1037 }
   1038 
   1039 /**
   1040  * Called when a drop down list is released to open it or set new option
   1041  * @param list pointer to the drop down list's list
   1042  * @return LV_RES_INV if the list is not being deleted in the user callback. Else LV_RES_OK
   1043  */
   1044 static lv_res_t list_release_handler(lv_obj_t * list_obj)
   1045 {
   1046     lv_dropdown_list_t * list = (lv_dropdown_list_t *) list_obj;
   1047     lv_obj_t * dropdown_obj = list->dropdown;
   1048     lv_dropdown_t * dropdown = (lv_dropdown_t *)dropdown_obj;
   1049 
   1050     lv_indev_t * indev = lv_indev_get_act();
   1051     /*Leave edit mode once a new item is selected*/
   1052     if(lv_indev_get_type(indev) == LV_INDEV_TYPE_ENCODER) {
   1053         dropdown->sel_opt_id_orig = dropdown->sel_opt_id;
   1054         lv_group_t * g      = lv_obj_get_group(dropdown_obj);
   1055         if(lv_group_get_editing(g)) {
   1056             lv_group_set_editing(g, false);
   1057         }
   1058     }
   1059 
   1060     /*Search the clicked option (For KEYPAD and ENCODER the new value should be already set)*/
   1061     if(lv_indev_get_type(indev) == LV_INDEV_TYPE_POINTER || lv_indev_get_type(indev) == LV_INDEV_TYPE_BUTTON) {
   1062         lv_point_t p;
   1063         lv_indev_get_point(indev, &p);
   1064         dropdown->sel_opt_id     = get_id_on_point(dropdown_obj, p.y);
   1065         dropdown->sel_opt_id_orig = dropdown->sel_opt_id;
   1066     }
   1067 
   1068     lv_dropdown_close(dropdown_obj);
   1069 
   1070     /*Invalidate to refresh the text*/
   1071     if(dropdown->text == NULL) lv_obj_invalidate(dropdown_obj);
   1072 
   1073     uint32_t id  = dropdown->sel_opt_id; /*Just to use uint32_t in event data*/
   1074     lv_res_t res = lv_event_send(dropdown_obj, LV_EVENT_VALUE_CHANGED, &id);
   1075     if(res != LV_RES_OK) return res;
   1076 
   1077     return LV_RES_OK;
   1078 }
   1079 
   1080 static void list_press_handler(lv_obj_t * list_obj)
   1081 {
   1082     lv_dropdown_list_t * list = (lv_dropdown_list_t *) list_obj;
   1083     lv_obj_t * dropdown_obj = list->dropdown;
   1084     lv_dropdown_t * dropdown = (lv_dropdown_t *)dropdown_obj;
   1085 
   1086     lv_indev_t * indev = lv_indev_get_act();
   1087     if(indev && (lv_indev_get_type(indev) == LV_INDEV_TYPE_POINTER || lv_indev_get_type(indev) == LV_INDEV_TYPE_BUTTON)) {
   1088         lv_point_t p;
   1089         lv_indev_get_point(indev, &p);
   1090         dropdown->pr_opt_id = get_id_on_point(dropdown_obj, p.y);
   1091         lv_obj_invalidate(list_obj);
   1092     }
   1093 }
   1094 
   1095 static uint16_t get_id_on_point(lv_obj_t * dropdown_obj, lv_coord_t y)
   1096 {
   1097     lv_dropdown_t * dropdown = (lv_dropdown_t *)dropdown_obj;
   1098     lv_obj_t * label = get_label(dropdown_obj);
   1099     if(label == NULL) return 0;
   1100     y -= label->coords.y1;
   1101 
   1102     const lv_font_t * font         = lv_obj_get_style_text_font(label, LV_PART_MAIN);
   1103     lv_coord_t font_h              = lv_font_get_line_height(font);
   1104     lv_coord_t line_space = lv_obj_get_style_text_line_space(label, LV_PART_MAIN);
   1105 
   1106     y += line_space / 2;
   1107     lv_coord_t h = font_h + line_space;
   1108 
   1109     uint16_t opt = y / h;
   1110 
   1111     if(opt >= dropdown->option_cnt) opt = dropdown->option_cnt - 1;
   1112     return opt;
   1113 }
   1114 
   1115 /**
   1116  * Set the position of list when it is closed to show the selected item
   1117  * @param ddlist pointer to a drop down list
   1118  */
   1119 static void position_to_selected(lv_obj_t * dropdown_obj)
   1120 {
   1121     lv_dropdown_t * dropdown = (lv_dropdown_t *)dropdown_obj;
   1122 
   1123     lv_obj_t * label = get_label(dropdown_obj);
   1124     if(label == NULL) return;
   1125 
   1126     if(lv_obj_get_height(label) <= lv_obj_get_content_height(dropdown_obj)) return;
   1127 
   1128     const lv_font_t * font         = lv_obj_get_style_text_font(label, LV_PART_MAIN);
   1129     lv_coord_t font_h              = lv_font_get_line_height(font);
   1130     lv_coord_t line_space = lv_obj_get_style_text_line_space(label, LV_PART_MAIN);
   1131     lv_coord_t unit_h = font_h + line_space;
   1132     lv_coord_t line_y1 = dropdown->sel_opt_id * unit_h;
   1133 
   1134     /*Scroll to the selected option*/
   1135     lv_obj_scroll_to_y(dropdown->list, line_y1, LV_ANIM_OFF);
   1136     lv_obj_invalidate(dropdown->list);
   1137 }
   1138 
   1139 static lv_obj_t * get_label(const lv_obj_t * obj)
   1140 {
   1141     lv_dropdown_t * dropdown = (lv_dropdown_t *)obj;
   1142     if(dropdown->list == NULL) return NULL;
   1143 
   1144     return lv_obj_get_child(dropdown->list, 0);
   1145 }
   1146 
   1147 #endif