Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
CTCaer
GitHub Repository: CTCaer/hekate
Path: blob/master/bdk/libs/lvgl/lv_objx/lv_ddlist.c
1476 views
1
/*
2
* Copyright (c) 2019 CTCaer
3
*
4
* This program is free software; you can redistribute it and/or modify it
5
* under the terms and conditions of the GNU General Public License,
6
* version 2, as published by the Free Software Foundation.
7
*
8
* This program is distributed in the hope it will be useful, but WITHOUT
9
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11
* more details.
12
*
13
* You should have received a copy of the GNU General Public License
14
* along with this program. If not, see <http://www.gnu.org/licenses/>.
15
*/
16
17
/**
18
* @file lv_ddlist.c
19
*
20
*/
21
22
23
/*********************
24
* INCLUDES
25
*********************/
26
#include "lv_ddlist.h"
27
#if USE_LV_DDLIST != 0
28
29
#include "../lv_draw/lv_draw.h"
30
#include "../lv_core/lv_group.h"
31
#include "../lv_core/lv_indev.h"
32
#include "../lv_themes/lv_theme.h"
33
#include "../lv_misc/lv_symbol_def.h"
34
#include "../lv_misc/lv_anim.h"
35
//#include <strings.h>
36
37
/*********************
38
* DEFINES
39
*********************/
40
#if USE_LV_ANIMATION
41
# ifndef LV_DDLIST_ANIM_TIME
42
# define LV_DDLIST_ANIM_TIME 200 /*ms*/
43
# endif
44
#else
45
# undef LV_DDLIST_ANIM_TIME
46
# define LV_DDLIST_ANIM_TIME 0 /*No animation*/
47
#endif
48
49
/**********************
50
* TYPEDEFS
51
**********************/
52
53
/**********************
54
* STATIC PROTOTYPES
55
**********************/
56
static bool lv_ddlist_design(lv_obj_t * ddlist, const lv_area_t * mask, lv_design_mode_t mode);
57
static lv_res_t lv_ddlist_signal(lv_obj_t * ddlist, lv_signal_t sign, void * param);
58
static lv_res_t lv_ddlist_scrl_signal(lv_obj_t * scrl, lv_signal_t sign, void * param);
59
static lv_res_t lv_ddlist_release_action(lv_obj_t * ddlist);
60
static lv_res_t lv_ddlist_press_action(lv_obj_t * ddlist);
61
static void lv_ddlist_refr_size(lv_obj_t * ddlist, bool anim_en);
62
static void lv_ddlist_pos_current_option(lv_obj_t * ddlist);
63
64
/**********************
65
* STATIC VARIABLES
66
**********************/
67
static lv_signal_func_t ancestor_signal;
68
static lv_signal_func_t ancestor_scrl_signal;
69
static lv_design_func_t ancestor_design;
70
71
/**********************
72
* MACROS
73
**********************/
74
75
/**********************
76
* GLOBAL FUNCTIONS
77
**********************/
78
79
/**
80
* Create a drop down list objects
81
* @param par pointer to an object, it will be the parent of the new drop down list
82
* @param copy pointer to a drop down list object, if not NULL then the new object will be copied from it
83
* @return pointer to the created drop down list
84
*/
85
lv_obj_t * lv_ddlist_create(lv_obj_t * par, const lv_obj_t * copy)
86
{
87
LV_LOG_TRACE("drop down list create started");
88
89
/*Create the ancestor drop down list*/
90
lv_obj_t * new_ddlist = lv_page_create(par, copy);
91
lv_mem_assert(new_ddlist);
92
if(new_ddlist == NULL) return NULL;
93
94
if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_ddlist);
95
if(ancestor_scrl_signal == NULL) ancestor_scrl_signal = lv_obj_get_signal_func(lv_page_get_scrl(new_ddlist));
96
if(ancestor_design == NULL) ancestor_design = lv_obj_get_design_func(new_ddlist);
97
98
/*Allocate the drop down list type specific extended data*/
99
lv_ddlist_ext_t * ext = lv_obj_allocate_ext_attr(new_ddlist, sizeof(lv_ddlist_ext_t));
100
lv_mem_assert(ext);
101
if(ext == NULL) return NULL;
102
103
/*Initialize the allocated 'ext' */
104
ext->label = NULL;
105
ext->action = NULL;
106
ext->opened = 0;
107
ext->fix_height = 0;
108
ext->sel_opt_id = 0;
109
ext->sel_opt_id_ori = 0;
110
ext->option_cnt = 0;
111
ext->anim_time = LV_DDLIST_ANIM_TIME;
112
ext->sel_style = &lv_style_plain_color;
113
ext->draw_arrow = 0; /*Do not draw arrow by default*/
114
ext->direction_up = 0;
115
116
/*The signal and design functions are not copied so set them here*/
117
lv_obj_set_signal_func(new_ddlist, lv_ddlist_signal);
118
lv_obj_set_signal_func(lv_page_get_scrl(new_ddlist), lv_ddlist_scrl_signal);
119
lv_obj_set_design_func(new_ddlist, lv_ddlist_design);
120
121
/*Init the new drop down list drop down list*/
122
if(copy == NULL) {
123
lv_obj_t * scrl = lv_page_get_scrl(new_ddlist);
124
lv_obj_set_drag(scrl, false);
125
lv_page_set_scrl_fit(new_ddlist, true, true);
126
127
ext->label = lv_label_create(new_ddlist, NULL);
128
lv_cont_set_fit(new_ddlist, true, false);
129
lv_page_set_rel_action(new_ddlist, lv_ddlist_release_action);
130
lv_page_set_pr_action(new_ddlist, lv_ddlist_press_action);
131
lv_page_set_sb_mode(new_ddlist, LV_SB_MODE_DRAG);
132
lv_page_set_sb_mode(new_ddlist, LV_SB_MODE_HIDE);
133
lv_page_set_style(new_ddlist, LV_PAGE_STYLE_SCRL, &lv_style_transp_tight);
134
135
lv_ddlist_set_options(new_ddlist, "Option 1\nOption 2\nOption 3");
136
137
/*Set the default styles*/
138
lv_theme_t * th = lv_theme_get_current();
139
if(th) {
140
lv_ddlist_set_style(new_ddlist, LV_DDLIST_STYLE_BG, th->ddlist.bg);
141
lv_ddlist_set_style(new_ddlist, LV_DDLIST_STYLE_BGO, th->ddlist.bgo);
142
lv_ddlist_set_style(new_ddlist, LV_DDLIST_STYLE_PR, th->ddlist.pr);
143
lv_ddlist_set_style(new_ddlist, LV_DDLIST_STYLE_SEL, th->ddlist.sel);
144
lv_ddlist_set_style(new_ddlist, LV_DDLIST_STYLE_SB, th->ddlist.sb);
145
} else {
146
lv_ddlist_set_style(new_ddlist, LV_DDLIST_STYLE_BG, &lv_style_pretty);
147
lv_ddlist_set_style(new_ddlist, LV_DDLIST_STYLE_BGO, &lv_style_pretty);
148
lv_ddlist_set_style(new_ddlist, LV_DDLIST_STYLE_PR, &lv_style_pretty);
149
lv_ddlist_set_style(new_ddlist, LV_DDLIST_STYLE_SEL, &lv_style_plain_color);
150
lv_ddlist_set_style(new_ddlist, LV_DDLIST_STYLE_SB, &lv_style_pretty_color);
151
}
152
}
153
/*Copy an existing drop down list*/
154
else {
155
lv_ddlist_ext_t * copy_ext = lv_obj_get_ext_attr(copy);
156
ext->label = lv_label_create(new_ddlist, copy_ext->label);
157
lv_label_set_text(ext->label, lv_label_get_text(copy_ext->label));
158
ext->sel_opt_id = copy_ext->sel_opt_id;
159
ext->fix_height = copy_ext->fix_height;
160
ext->action = copy_ext->action;
161
ext->option_cnt = copy_ext->option_cnt;
162
ext->sel_style = copy_ext->sel_style;
163
ext->anim_time = copy_ext->anim_time;
164
ext->draw_arrow = copy_ext->draw_arrow;
165
166
/*Refresh the style with new signal function*/
167
lv_obj_refresh_style(new_ddlist);
168
}
169
170
LV_LOG_INFO("drop down list created");
171
172
173
return new_ddlist;
174
}
175
176
/*=====================
177
* Setter functions
178
*====================*/
179
180
/**
181
* Set arrow draw in a drop down list
182
* @param ddlist pointer to drop down list object
183
* @param en enable/disable a arrow draw. E.g. "true" for draw.
184
*/
185
void lv_ddlist_set_draw_arrow(lv_obj_t * ddlist, bool en)
186
{
187
lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist);
188
189
/*Set the flag*/
190
ext->draw_arrow = en;
191
}
192
193
/**
194
* Set the options in a drop down list from a string
195
* @param ddlist pointer to drop down list object
196
* @param options a string with '\n' separated options. E.g. "One\nTwo\nThree"
197
*/
198
void lv_ddlist_set_options(lv_obj_t * ddlist, const char * options)
199
{
200
lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist);
201
202
/*Count the '\n'-s to determine the number of options*/
203
ext->option_cnt = 0;
204
uint16_t i;
205
for(i = 0; options[i] != '\0'; i++) {
206
if(options[i] == '\n') ext->option_cnt++;
207
}
208
ext->option_cnt++; /*Last option in the at row*/
209
210
lv_label_set_text(ext->label, options);
211
lv_ddlist_refr_size(ddlist, false);
212
}
213
214
/**
215
* Set the selected option
216
* @param ddlist pointer to drop down list object
217
* @param sel_opt id of the selected option (0 ... number of option - 1);
218
*/
219
void lv_ddlist_set_selected(lv_obj_t * ddlist, uint16_t sel_opt)
220
{
221
lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist);
222
if(ext->sel_opt_id == sel_opt) return;
223
224
ext->sel_opt_id = sel_opt < ext->option_cnt ? sel_opt : ext->option_cnt - 1;
225
ext->sel_opt_id_ori = ext->sel_opt_id;
226
/*Move the list to show the current option*/
227
if(ext->opened == 0) {
228
lv_ddlist_pos_current_option(ddlist);
229
} else {
230
lv_obj_invalidate(ddlist);
231
}
232
}
233
234
/**
235
* Set a function to call when a new option is chosen
236
* @param ddlist pointer to a drop down list
237
* @param action pointer to a call back function
238
*/
239
void lv_ddlist_set_action(lv_obj_t * ddlist, lv_action_t action)
240
{
241
lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist);
242
ext->action = action;
243
}
244
245
/**
246
* Set the fix height for the drop down list
247
* If 0 then the opened ddlist will be auto. sized else the set height will be applied.
248
* @param ddlist pointer to a drop down list
249
* @param h the height when the list is opened (0: auto size)
250
*/
251
void lv_ddlist_set_fix_height(lv_obj_t * ddlist, lv_coord_t h)
252
{
253
lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist);
254
if(ext->fix_height == h) return;
255
256
ext->fix_height = h;
257
258
lv_ddlist_refr_size(ddlist, false);
259
}
260
261
/**
262
* Enable or disable the horizontal fit to the content
263
* @param ddlist pointer to a drop down list
264
* @param en true: enable auto fit; false: disable auto fit
265
*/
266
void lv_ddlist_set_hor_fit(lv_obj_t * ddlist, bool en)
267
{
268
lv_cont_set_fit(ddlist, en, lv_cont_get_ver_fit(ddlist));
269
lv_page_set_scrl_fit(ddlist, en, lv_page_get_scrl_fit_ver(ddlist));
270
271
lv_ddlist_refr_size(ddlist, false);
272
}
273
274
/**
275
* Set the open/close animation time.
276
* @param ddlist pointer to a drop down list
277
* @param anim_time: open/close animation time [ms]
278
*/
279
void lv_ddlist_set_anim_time(lv_obj_t * ddlist, uint16_t anim_time)
280
{
281
lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist);
282
#if USE_LV_ANIMATION == 0
283
anim_time = 0;
284
#endif
285
286
ext->anim_time = anim_time;
287
}
288
289
/**
290
* Set a style of a drop down list
291
* @param ddlist pointer to a drop down list object
292
* @param type which style should be set
293
* @param style pointer to a style
294
*/
295
void lv_ddlist_set_style(lv_obj_t * ddlist, lv_ddlist_style_t type, lv_style_t * style)
296
{
297
lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist);
298
299
switch(type) {
300
case LV_DDLIST_STYLE_BG:
301
lv_page_set_style(ddlist, LV_PAGE_STYLE_BG, style);
302
break;
303
case LV_DDLIST_STYLE_BGO:
304
lv_page_set_style(ddlist, LV_PAGE_STYLE_BGO, style);
305
break;
306
case LV_DDLIST_STYLE_PR:
307
lv_page_set_style(ddlist, LV_PAGE_STYLE_PR, style);
308
break;
309
case LV_DDLIST_STYLE_SB:
310
lv_page_set_style(ddlist, LV_PAGE_STYLE_SB, style);
311
break;
312
case LV_DDLIST_STYLE_SEL:
313
ext->sel_style = style;
314
lv_obj_t * scrl = lv_page_get_scrl(ddlist);
315
lv_obj_refresh_ext_size(scrl); /*Because of the wider selected rectangle*/
316
break;
317
}
318
}
319
320
void lv_ddlist_set_align(lv_obj_t *ddlist, lv_label_align_t align)
321
{
322
lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist);
323
324
lv_label_set_align(ext->label, align);
325
}
326
327
void lv_ddlist_set_direction_up(lv_obj_t *ddlist, bool enable)
328
{
329
lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist);
330
331
ext->direction_up = enable;
332
}
333
/*=====================
334
* Getter functions
335
*====================*/
336
337
/**
338
* Get arrow draw in a drop down list
339
* @param ddlist pointer to drop down list object
340
*/
341
bool lv_ddlist_get_draw_arrow(lv_obj_t * ddlist)
342
{
343
lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist);
344
345
return ext->draw_arrow;
346
}
347
348
/**
349
* Get the options of a drop down list
350
* @param ddlist pointer to drop down list object
351
* @return the options separated by '\n'-s (E.g. "Option1\nOption2\nOption3")
352
*/
353
const char * lv_ddlist_get_options(const lv_obj_t * ddlist)
354
{
355
lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist);
356
return lv_label_get_text(ext->label);
357
}
358
359
/**
360
* Get the selected option
361
* @param ddlist pointer to drop down list object
362
* @return id of the selected option (0 ... number of option - 1);
363
*/
364
uint16_t lv_ddlist_get_selected(const lv_obj_t * ddlist)
365
{
366
lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist);
367
368
return ext->sel_opt_id;
369
}
370
371
/**
372
* Get the current selected option as a string
373
* @param ddlist pointer to ddlist object
374
* @param buf pointer to an array to store the string
375
*/
376
void lv_ddlist_get_selected_str(const lv_obj_t * ddlist, char * buf)
377
{
378
lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist);
379
380
uint16_t i;
381
uint16_t line = 0;
382
const char * opt_txt = lv_label_get_text(ext->label);
383
uint16_t txt_len = strlen(opt_txt);
384
385
386
for(i = 0; i < txt_len && line != ext->sel_opt_id; i++) {
387
if(opt_txt[i] == '\n') line ++;
388
}
389
390
uint16_t c;
391
for(c = 0; opt_txt[i] != '\n' && i < txt_len; c++, i++) buf[c] = opt_txt[i];
392
393
buf[c] = '\0';
394
}
395
396
/**
397
* Get the "option selected" callback function
398
* @param ddlist pointer to a drop down list
399
* @return pointer to the call back function
400
*/
401
lv_action_t lv_ddlist_get_action(const lv_obj_t * ddlist)
402
{
403
lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist);
404
return ext->action;
405
}
406
407
/**
408
* Get the fix height value.
409
* @param ddlist pointer to a drop down list object
410
* @return the height if the ddlist is opened (0: auto size)
411
*/
412
lv_coord_t lv_ddlist_get_fix_height(const lv_obj_t * ddlist)
413
{
414
lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist);
415
return ext->fix_height;
416
}
417
418
/**
419
* Get the open/close animation time.
420
* @param ddlist pointer to a drop down list
421
* @return open/close animation time [ms]
422
*/
423
uint16_t lv_ddlist_get_anim_time(const lv_obj_t * ddlist)
424
{
425
lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist);
426
return ext->anim_time;
427
}
428
429
/**
430
* Get a style of a drop down list
431
* @param ddlist pointer to a drop down list object
432
* @param type which style should be get
433
* @return style pointer to a style
434
*/
435
lv_style_t * lv_ddlist_get_style(const lv_obj_t * ddlist, lv_ddlist_style_t type)
436
{
437
lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist);
438
439
switch(type) {
440
case LV_DDLIST_STYLE_BG:
441
return lv_page_get_style(ddlist, LV_PAGE_STYLE_BG);
442
case LV_DDLIST_STYLE_BGO:
443
return lv_page_get_style(ddlist, LV_PAGE_STYLE_BGO);
444
case LV_DDLIST_STYLE_PR:
445
return lv_page_get_style(ddlist, LV_PAGE_STYLE_PR);
446
case LV_DDLIST_STYLE_SB:
447
return lv_page_get_style(ddlist, LV_PAGE_STYLE_SB);
448
case LV_DDLIST_STYLE_SEL:
449
return ext->sel_style;
450
default:
451
return NULL;
452
}
453
454
/*To avoid warning*/
455
return NULL;
456
}
457
458
lv_label_align_t lv_ddlist_get_align(const lv_obj_t *ddlist)
459
{
460
lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist);
461
462
return lv_label_get_align(ext->label);
463
}
464
465
/*=====================
466
* Other functions
467
*====================*/
468
469
/**
470
* Open the drop down list with or without animation
471
* @param ddlist pointer to drop down list object
472
* @param anim_en true: use animation; false: not use animations
473
*/
474
void lv_ddlist_open(lv_obj_t * ddlist, bool anim_en)
475
{
476
#if USE_LV_ANIMATION == 0
477
anim_en = false;
478
#endif
479
lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist);
480
ext->opened = 1;
481
lv_obj_set_drag(lv_page_get_scrl(ddlist), true);
482
lv_ddlist_refr_size(ddlist, anim_en);
483
}
484
485
/**
486
* Close (Collapse) the drop down list
487
* @param ddlist pointer to drop down list object
488
* @param anim_en true: use animation; false: not use animations
489
*/
490
void lv_ddlist_close(lv_obj_t * ddlist, bool anim_en)
491
{
492
#if USE_LV_ANIMATION == 0
493
anim_en = false;
494
#endif
495
lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist);
496
ext->opened = 0;
497
lv_obj_set_drag(lv_page_get_scrl(ddlist), false);
498
lv_ddlist_refr_size(ddlist, anim_en);
499
}
500
501
/**********************
502
* STATIC FUNCTIONS
503
**********************/
504
505
/**
506
* Get the text alignment flag for a drop down list.
507
* @param ddlist drop down list
508
* @return text alignment flag
509
*/
510
static lv_txt_flag_t lv_ddlist_get_txt_flag(const lv_obj_t *ddlist)
511
{
512
lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist);
513
514
/*The label might be already deleted so just return with some value*/
515
if(!ext->label) return LV_TXT_FLAG_CENTER;
516
517
lv_label_align_t align = lv_label_get_align(ext->label);
518
519
switch(align)
520
{
521
default:
522
case LV_LABEL_ALIGN_LEFT:
523
return LV_TXT_FLAG_NONE;
524
case LV_LABEL_ALIGN_CENTER:
525
return LV_TXT_FLAG_CENTER;
526
case LV_LABEL_ALIGN_RIGHT:
527
return LV_TXT_FLAG_RIGHT;
528
}
529
}
530
531
/**
532
* Handle the drawing related tasks of the drop down lists
533
* @param ddlist pointer to an object
534
* @param mask the object will be drawn only in this area
535
* @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area
536
* (return 'true' if yes)
537
* LV_DESIGN_DRAW: draw the object (always return 'true')
538
* LV_DESIGN_DRAW_POST: drawing after every children are drawn
539
* @param return true/false, depends on 'mode'
540
*/
541
static bool lv_ddlist_design(lv_obj_t * ddlist, const lv_area_t * mask, lv_design_mode_t mode)
542
{
543
/*Return false if the object is not covers the mask_p area*/
544
if(mode == LV_DESIGN_COVER_CHK) {
545
return ancestor_design(ddlist, mask, mode);
546
}
547
/*Draw the object*/
548
else if(mode == LV_DESIGN_DRAW_MAIN) {
549
ancestor_design(ddlist, mask, mode);
550
551
lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist);
552
lv_opa_t opa_scale = lv_obj_get_opa_scale(ddlist);
553
/*If the list is opened draw a rectangle under the selected item*/
554
if(ext->opened != 0) {
555
lv_style_t * style = lv_ddlist_get_style(ddlist, LV_DDLIST_STYLE_BG);
556
const lv_font_t * font = style->text.font;
557
lv_coord_t font_h = lv_font_get_height(font);
558
559
/*Draw the selected*/
560
lv_area_t rect_area;
561
rect_area.y1 = ext->label->coords.y1;
562
rect_area.y1 += ext->sel_opt_id * (font_h + style->text.line_space);
563
rect_area.y1 -= style->text.line_space / 2;
564
565
rect_area.y2 = rect_area.y1 + font_h + style->text.line_space - 1;
566
rect_area.x1 = ddlist->coords.x1;
567
rect_area.x2 = ddlist->coords.x2;
568
569
lv_draw_rect(&rect_area, mask, ext->sel_style, opa_scale);
570
}
571
}
572
/*Post draw when the children are drawn*/
573
else if(mode == LV_DESIGN_DRAW_POST) {
574
/*Redraw the text on the selected area with a different color*/
575
lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist);
576
lv_opa_t opa_scale = lv_obj_get_opa_scale(ddlist);
577
578
/*Redraw only in opened state*/
579
if(ext->opened) {
580
lv_style_t * style = lv_ddlist_get_style(ddlist, LV_DDLIST_STYLE_BG);
581
const lv_font_t * font = style->text.font;
582
lv_coord_t font_h = lv_font_get_height(font);
583
584
lv_area_t area_sel;
585
area_sel.y1 = ext->label->coords.y1;
586
area_sel.y1 += ext->sel_opt_id * (font_h + style->text.line_space);
587
area_sel.y1 -= style->text.line_space / 2;
588
589
area_sel.y2 = area_sel.y1 + font_h + style->text.line_space - 1;
590
area_sel.x1 = ddlist->coords.x1;
591
area_sel.x2 = ddlist->coords.x2;
592
lv_area_t mask_sel;
593
bool area_ok;
594
area_ok = lv_area_intersect(&mask_sel, mask, &area_sel);
595
if(area_ok) {
596
lv_style_t * sel_style = lv_ddlist_get_style(ddlist, LV_DDLIST_STYLE_SEL);
597
lv_style_t new_style;
598
lv_style_copy(&new_style, style);
599
new_style.text.color = sel_style->text.color;
600
new_style.text.opa = sel_style->text.opa;
601
lv_txt_flag_t flag = lv_ddlist_get_txt_flag(ddlist);
602
lv_draw_label(&ext->label->coords, &mask_sel, &new_style, opa_scale,
603
lv_label_get_text(ext->label), flag, NULL);
604
}
605
}
606
607
/*Add a down symbol in ddlist when closed*/
608
else
609
{
610
/*Draw a arrow in ddlist if enabled*/
611
if(ext->draw_arrow)
612
{
613
lv_style_t * style = lv_ddlist_get_style(ddlist, LV_DDLIST_STYLE_BG);
614
const lv_font_t * font = style->text.font;
615
lv_style_t * sel_style = lv_ddlist_get_style(ddlist, LV_DDLIST_STYLE_BG);
616
lv_coord_t font_h = lv_font_get_height(font);
617
lv_style_t new_style;
618
lv_style_copy(&new_style, style);
619
new_style.text.color = sel_style->text.color;
620
new_style.text.opa = sel_style->text.opa;
621
lv_area_t area_arrow;
622
area_arrow.x2 = ddlist->coords.x2 - style->body.padding.hor;
623
if (!ext->direction_up)
624
area_arrow.x1 = area_arrow.x2 - lv_txt_get_width(SYMBOL_DOWN, strlen(SYMBOL_DOWN), sel_style->text.font, 0, 0);
625
else
626
area_arrow.x1 = area_arrow.x2 - lv_txt_get_width(SYMBOL_UP, strlen(SYMBOL_UP), sel_style->text.font, 0, 0);
627
628
area_arrow.y1 = ddlist->coords.y1 + style->text.line_space;
629
area_arrow.y2 = area_arrow.y1 + font_h;
630
631
632
lv_area_t mask_arrow;
633
bool area_ok;
634
area_ok = lv_area_intersect(&mask_arrow, mask, &area_arrow);
635
if (area_ok)
636
{
637
if (!ext->direction_up)
638
lv_draw_label(&area_arrow, &mask_arrow, &new_style, opa_scale,
639
SYMBOL_DOWN, LV_TXT_FLAG_NONE, NULL); /*Use a down arrow in ddlist, you can replace it with your custom symbol*/
640
else
641
lv_draw_label(&area_arrow, &mask_arrow, &new_style, opa_scale,
642
SYMBOL_UP, LV_TXT_FLAG_NONE, NULL); /*Use a down arrow in ddlist, you can replace it with your custom symbol*/
643
}
644
}
645
}
646
/*Draw the scrollbar in the ancestor page design function*/
647
ancestor_design(ddlist, mask, mode);
648
}
649
650
return true;
651
}
652
653
/**
654
* Signal function of the drop down list
655
* @param ddlist pointer to a drop down list object
656
* @param sign a signal type from lv_signal_t enum
657
* @param param pointer to a signal specific variable
658
* @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
659
*/
660
static lv_res_t lv_ddlist_signal(lv_obj_t * ddlist, lv_signal_t sign, void * param)
661
{
662
lv_res_t res;
663
/* Include the ancient signal function */
664
res = ancestor_signal(ddlist, sign, param);
665
if(res != LV_RES_OK) return res;
666
667
lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist);
668
669
if(sign == LV_SIGNAL_STYLE_CHG) {
670
//! lv_ddlist_refr_size(ddlist, 0); // uncommented in OG
671
} else if(sign == LV_SIGNAL_CLEANUP) {
672
ext->label = NULL;
673
} else if(sign == LV_SIGNAL_FOCUS) {
674
#if USE_LV_GROUP
675
lv_group_t * g = lv_obj_get_group(ddlist);
676
bool editing = lv_group_get_editing(g);
677
lv_hal_indev_type_t indev_type = lv_indev_get_type(lv_indev_get_act());
678
679
/*Encoders need special handling*/
680
if(indev_type == LV_INDEV_TYPE_ENCODER) {
681
/*Open the list if editing*/
682
if(editing) {
683
ext->opened = true;
684
ext->sel_opt_id_ori = ext->sel_opt_id;
685
lv_ddlist_refr_size(ddlist, true);
686
}
687
/*Close the lift if navigating*/
688
else {
689
ext->opened = false;
690
ext->sel_opt_id = ext->sel_opt_id_ori;
691
lv_ddlist_refr_size(ddlist, true);
692
693
}
694
} else {
695
/*Open the list if closed*/
696
if(!ext->opened) {
697
ext->opened = true;
698
ext->sel_opt_id_ori = ext->sel_opt_id; /*Save the current value. Used to revert this state if ENER wont't be pressed*/
699
lv_ddlist_refr_size(ddlist, true);
700
}
701
}
702
#endif
703
} else if(sign == LV_SIGNAL_DEFOCUS) {
704
if(ext->opened) {
705
ext->opened = false;
706
ext->sel_opt_id = ext->sel_opt_id_ori;
707
lv_ddlist_refr_size(ddlist, true);
708
}
709
} else if(sign == LV_SIGNAL_CONTROLL) {
710
char c = *((char *)param);
711
if(c == LV_GROUP_KEY_RIGHT || c == LV_GROUP_KEY_DOWN) {
712
if(!ext->opened) {
713
ext->opened = 1;
714
lv_ddlist_refr_size(ddlist, true);
715
}
716
717
if(ext->sel_opt_id + 1 < ext->option_cnt) {
718
ext->sel_opt_id ++;
719
lv_ddlist_pos_current_option(ddlist);
720
lv_obj_invalidate(ddlist);
721
}
722
} else if(c == LV_GROUP_KEY_LEFT || c == LV_GROUP_KEY_UP) {
723
if(!ext->opened) {
724
ext->opened = 1;
725
lv_ddlist_refr_size(ddlist, true);
726
}
727
if(ext->sel_opt_id > 0) {
728
ext->sel_opt_id --;
729
lv_ddlist_pos_current_option(ddlist);
730
lv_obj_invalidate(ddlist);
731
}
732
} else if(c == LV_GROUP_KEY_ENTER) {
733
if(ext->opened) {
734
ext->sel_opt_id_ori = ext->sel_opt_id;
735
ext->opened = 0;
736
if(ext->action) ext->action(ddlist);
737
738
#if USE_LV_GROUP
739
lv_group_t * g = lv_obj_get_group(ddlist);
740
bool editing = lv_group_get_editing(g);
741
if(editing) lv_group_set_editing(g, false); /*In edit mode go to navigate mode if an option is selected*/
742
#endif
743
} else {
744
ext->opened = 1;
745
}
746
747
lv_ddlist_refr_size(ddlist, true);
748
} else if(c == LV_GROUP_KEY_ESC) {
749
if(ext->opened) {
750
ext->opened = 0;
751
ext->sel_opt_id = ext->sel_opt_id_ori;
752
lv_ddlist_refr_size(ddlist, true);
753
}
754
}
755
} else if(sign == LV_SIGNAL_GET_EDITABLE) {
756
bool * editable = (bool *)param;
757
*editable = true;
758
} else if(sign == LV_SIGNAL_GET_TYPE) {
759
lv_obj_type_t * buf = param;
760
uint8_t i;
761
for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/
762
if(buf->type[i] == NULL) break;
763
}
764
buf->type[i] = "lv_ddlist";
765
}
766
767
return res;
768
}
769
770
/**
771
* Signal function of the drop down list's scrollable part
772
* @param scrl pointer to a drop down list's scrollable part
773
* @param sign a signal type from lv_signal_t enum
774
* @param param pointer to a signal specific variable
775
* @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
776
*/
777
static lv_res_t lv_ddlist_scrl_signal(lv_obj_t * scrl, lv_signal_t sign, void * param)
778
{
779
lv_res_t res;
780
781
/* Include the ancient signal function */
782
res = ancestor_scrl_signal(scrl, sign, param);
783
if(res != LV_RES_OK) return res;
784
785
lv_obj_t * ddlist = lv_obj_get_parent(scrl);
786
787
if(sign == LV_SIGNAL_REFR_EXT_SIZE) {
788
/* Because of the wider selected rectangle ext. size
789
* In this way by dragging the scrollable part the wider rectangle area can be redrawn too*/
790
lv_style_t * style = lv_ddlist_get_style(ddlist, LV_DDLIST_STYLE_BG);
791
if(scrl->ext_size < style->body.padding.hor) scrl->ext_size = style->body.padding.hor;
792
} else if(sign == LV_SIGNAL_CLEANUP) {
793
lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist);
794
ext->label = NULL; /*The label is already deleted*/
795
}
796
797
return res;
798
}
799
800
/**
801
* Called when a drop down list is released to open it or set new option
802
* @param ddlist pointer to a drop down list object
803
* @return LV_ACTION_RES_INV if the ddlist it deleted in the user callback else LV_ACTION_RES_OK
804
*/
805
static lv_res_t lv_ddlist_release_action(lv_obj_t * ddlist)
806
{
807
lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist);
808
809
if (!lv_obj_get_click(ddlist)) return LV_RES_OK;
810
811
if(ext->opened == 0) { /*Open the list*/
812
ext->opened = 1;
813
lv_ddlist_set_style(ddlist, LV_DDLIST_STYLE_BG, lv_ddlist_get_style(ddlist, LV_DDLIST_STYLE_BGO));
814
lv_obj_set_drag(lv_page_get_scrl(ddlist), true);
815
} else {
816
ext->opened = 0;
817
//lv_ddlist_set_style(ddlist, LV_DDLIST_STYLE_BG, lv_ddlist_get_style(ddlist, lv_ddlist_get_style(ddlist, LV_DDLIST_STYLE_PR)));
818
lv_obj_set_drag(lv_page_get_scrl(ddlist), false);
819
820
/*Search the clicked option*/
821
lv_indev_t * indev = lv_indev_get_act();
822
lv_point_t p;
823
lv_indev_get_point(indev, &p);
824
p.x -= ext->label->coords.x1;
825
p.y -= ext->label->coords.y1;
826
uint16_t letter_i;
827
letter_i = lv_label_get_letter_on(ext->label, &p);
828
829
uint16_t new_opt = 0;
830
const char * txt = lv_label_get_text(ext->label);
831
uint32_t i = 0;
832
uint32_t line_cnt = 0;
833
uint32_t letter;
834
for(line_cnt = 0; line_cnt < letter_i; line_cnt++) {
835
letter = lv_txt_encoded_next(txt, &i);
836
if(letter == '\n') new_opt ++;
837
}
838
839
ext->sel_opt_id = new_opt;
840
841
if(ext->action != NULL) {
842
ext->action(ddlist);
843
}
844
}
845
lv_ddlist_refr_size(ddlist, true);
846
847
return LV_RES_OK;
848
}
849
850
static lv_res_t lv_ddlist_press_action(lv_obj_t * ddlist)
851
{
852
lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist);
853
854
if (!lv_obj_get_click(ddlist)) return LV_RES_OK;
855
856
if (ext->opened == 0)
857
{ /*Open the list*/
858
lv_ddlist_set_style(ddlist, LV_DDLIST_STYLE_BG, lv_ddlist_get_style(ddlist, LV_DDLIST_STYLE_PR));
859
}
860
else
861
{
862
//lv_ddlist_set_style(ddlist, LV_DDLIST_STYLE_BG, lv_ddlist_get_style(ddlist, LV_DDLIST_STYLE_BGO));
863
//lv_obj_set_drag(lv_page_get_scrl(ddlist), false);
864
865
///*Search the clicked option*/
866
//lv_indev_t * indev = lv_indev_get_act();
867
//lv_point_t p;
868
//lv_indev_get_point(indev, &p);
869
//p.x -= ext->label->coords.x1;
870
//p.y -= ext->label->coords.y1;
871
//uint16_t letter_i;
872
//letter_i = lv_label_get_letter_on(ext->label, &p);
873
874
//uint16_t new_opt = 0;
875
//const char * txt = lv_label_get_text(ext->label);
876
//uint32_t i = 0;
877
//uint32_t line_cnt = 0;
878
//uint32_t letter;
879
//for (line_cnt = 0; line_cnt < letter_i; line_cnt++)
880
//{
881
// letter = lv_txt_encoded_next(txt, &i);
882
// if (letter == '\n') new_opt++;
883
//}
884
885
//ext->sel_opt_id = new_opt;
886
887
//if (ext->action != NULL)
888
//{
889
// ext->action(ddlist);
890
//}
891
}
892
893
return LV_RES_OK;
894
}
895
896
/**
897
* Refresh the size of drop down list according to its status (open or closed)
898
* @param ddlist pointer to a drop down list object
899
* @param anim_en Change the size (open/close) with or without animation (true/false)
900
*/
901
static void lv_ddlist_refr_size(lv_obj_t * ddlist, bool anim_en)
902
{
903
#if USE_LV_ANIMATION == 0
904
anim_en = false;
905
#endif
906
lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist);
907
lv_style_t * style = lv_obj_get_style(ddlist);
908
lv_coord_t new_height, full_height;
909
bool current_state = 0;
910
911
if(ext->opened) { /*Open the list*/
912
if(ext->fix_height == 0) new_height = lv_obj_get_height(lv_page_get_scrl(ddlist)) + 2 * style->body.padding.ver;
913
else new_height = ext->fix_height;
914
current_state = 1;
915
916
lv_page_set_sb_mode(ddlist, LV_SB_MODE_UNHIDE);
917
} else { /*Close the list*/
918
const lv_font_t * font = style->text.font;
919
lv_style_t * label_style = lv_obj_get_style(ext->label);
920
lv_coord_t font_h = lv_font_get_height(font);
921
new_height = font_h + 2 * label_style->text.line_space;
922
//full_height = lv_obj_get_height(lv_page_get_scrl(ddlist)) + 2 * style->body.padding.ver;
923
current_state = 0;
924
925
lv_page_set_sb_mode(ddlist, LV_SB_MODE_HIDE);
926
}
927
928
if(anim_en == 0 || ext->direction_up) {
929
lv_obj_set_height(ddlist, new_height);
930
if (ext->direction_up)
931
{
932
full_height = lv_obj_get_height(lv_page_get_scrl(ddlist)) - lv_font_get_height(style->text.font);
933
if (current_state)
934
lv_obj_set_y(ddlist, lv_obj_get_y(ddlist) - full_height);
935
else
936
lv_obj_set_y(ddlist, lv_obj_get_y(ddlist) + full_height);
937
}
938
939
lv_ddlist_pos_current_option(ddlist);
940
#if USE_LV_ANIMATION
941
lv_anim_del(ddlist, (lv_anim_fp_t)lv_obj_set_height); /*If an animation is in progress then it will overwrite this changes*/
942
} else {
943
lv_anim_t a;
944
a.var = ddlist;
945
a.start = lv_obj_get_height(ddlist);
946
a.end = new_height;
947
a.fp = (lv_anim_fp_t)lv_obj_set_height;
948
a.path = lv_anim_path_linear;
949
a.end_cb = (lv_anim_cb_t)lv_ddlist_pos_current_option;
950
a.act_time = 0;
951
a.time = ext->anim_time;
952
a.playback = 0;
953
a.playback_pause = 0;
954
a.repeat = 0;
955
a.repeat_pause = 0;
956
957
lv_anim_create(&a);
958
#endif
959
}
960
}
961
962
/**
963
* Set the position of list when it is closed to show the selected item
964
* @param ddlist pointer to a drop down list
965
*/
966
static void lv_ddlist_pos_current_option(lv_obj_t * ddlist)
967
{
968
lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist);
969
lv_style_t * style = lv_obj_get_style(ddlist);
970
const lv_font_t * font = style->text.font;
971
lv_coord_t font_h = lv_font_get_height(font);
972
lv_style_t * label_style = lv_obj_get_style(ext->label);
973
lv_obj_t * scrl = lv_page_get_scrl(ddlist);
974
975
lv_coord_t h = lv_obj_get_height(ddlist);
976
lv_coord_t line_y1 = ext->sel_opt_id * (font_h + label_style->text.line_space) + ext->label->coords.y1 - scrl->coords.y1;
977
978
lv_obj_set_y(scrl, - line_y1 + (h - font_h) / 2);
979
}
980
981
#endif
982
983