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