Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
CTCaer
GitHub Repository: CTCaer/hekate
Path: blob/master/bdk/libs/lvgl/lv_objx/lv_list.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_list.c
19
*
20
*/
21
22
/*********************
23
* INCLUDES
24
*********************/
25
#include "lv_list.h"
26
#if USE_LV_LIST != 0
27
28
#include "../lv_core/lv_group.h"
29
#include "../lv_themes/lv_theme.h"
30
#include "../lv_misc/lv_anim.h"
31
#include "../lv_misc/lv_math.h"
32
33
/*********************
34
* DEFINES
35
*********************/
36
#define LV_LIST_LAYOUT_DEF LV_LAYOUT_COL_M
37
38
#if USE_LV_ANIMATION
39
# ifndef LV_LIST_FOCUS_TIME
40
# define LV_LIST_FOCUS_TIME 100 /*Animation time of focusing to the a list element [ms] (0: no animation) */
41
# endif
42
#else
43
# undef LV_LIST_FOCUS_TIME
44
# define LV_LIST_FOCUS_TIME 0 /*No animations*/
45
#endif
46
47
/**********************
48
* TYPEDEFS
49
**********************/
50
51
/**********************
52
* STATIC PROTOTYPES
53
**********************/
54
static lv_res_t lv_list_signal(lv_obj_t * list, lv_signal_t sign, void * param);
55
static lv_res_t lv_list_btn_signal(lv_obj_t * btn, lv_signal_t sign, void * param);
56
static void refr_btn_width(lv_obj_t * list);
57
static void lv_list_btn_single_selected(lv_obj_t *btn);
58
static bool lv_list_is_list_btn(lv_obj_t * list_btn);
59
static bool lv_list_is_list_img(lv_obj_t * list_btn);
60
static bool lv_list_is_list_label(lv_obj_t * list_btn);
61
62
/**********************
63
* STATIC VARIABLES
64
**********************/
65
#if USE_LV_IMG
66
static lv_signal_func_t img_signal;
67
#endif
68
static lv_signal_func_t label_signal;
69
static lv_signal_func_t ancestor_page_signal;
70
static lv_signal_func_t ancestor_btn_signal;
71
#if USE_LV_GROUP
72
/*Used to make the last clicked button pressed (selected) when the list become focused and `click_focus == 1`*/
73
static lv_obj_t * last_clicked_btn;
74
#endif
75
76
/**********************
77
* MACROS
78
**********************/
79
80
/**********************
81
* GLOBAL FUNCTIONS
82
**********************/
83
84
/**
85
* Create a list objects
86
* @param par pointer to an object, it will be the parent of the new list
87
* @param copy pointer to a list object, if not NULL then the new object will be copied from it
88
* @return pointer to the created list
89
*/
90
lv_obj_t * lv_list_create(lv_obj_t * par, const lv_obj_t * copy)
91
{
92
LV_LOG_TRACE("list create started");
93
94
/*Create the ancestor basic object*/
95
lv_obj_t * new_list = lv_page_create(par, copy);
96
lv_mem_assert(new_list);
97
if(new_list == NULL) return NULL;
98
99
if(ancestor_page_signal == NULL) ancestor_page_signal = lv_obj_get_signal_func(new_list);
100
101
lv_list_ext_t * ext = lv_obj_allocate_ext_attr(new_list, sizeof(lv_list_ext_t));
102
lv_mem_assert(ext);
103
if(ext == NULL) return NULL;
104
105
// Important!
106
static lv_style_t img_btn_color;
107
lv_style_copy( &img_btn_color, &lv_style_plain);
108
img_btn_color.image.color = LV_COLOR_HEX(0xDDDDDD);
109
img_btn_color.image.intense = LV_OPA_50;
110
ext->style_img = &img_btn_color;
111
ext->styles_btn[LV_BTN_STATE_REL] = &lv_style_btn_rel;
112
ext->styles_btn[LV_BTN_STATE_PR] = &lv_style_btn_pr;
113
ext->styles_btn[LV_BTN_STATE_TGL_REL] = &lv_style_btn_tgl_rel;
114
ext->styles_btn[LV_BTN_STATE_TGL_PR] = &lv_style_btn_tgl_pr;
115
ext->styles_btn[LV_BTN_STATE_INA] = &lv_style_btn_ina;
116
ext->anim_time = LV_LIST_FOCUS_TIME;
117
ext->single_mode = false;
118
ext->size = 0;
119
120
#if USE_LV_GROUP
121
ext->last_sel = NULL;
122
ext->selected_btn = NULL;
123
#endif
124
125
lv_obj_set_signal_func(new_list, lv_list_signal);
126
127
/*Init the new list object*/
128
if(copy == NULL) {
129
lv_obj_set_size(new_list, 2 * LV_DPI, 3 * LV_DPI);
130
lv_page_set_scrl_layout(new_list, LV_LIST_LAYOUT_DEF);
131
lv_list_set_sb_mode(new_list, LV_SB_MODE_DRAG);
132
133
/*Set the default styles*/
134
lv_theme_t * th = lv_theme_get_current();
135
if(th) {
136
lv_list_set_style(new_list, LV_LIST_STYLE_BG, th->list.bg);
137
lv_list_set_style(new_list, LV_LIST_STYLE_SCRL, th->list.scrl);
138
lv_list_set_style(new_list, LV_LIST_STYLE_SB, th->list.sb);
139
lv_list_set_style(new_list, LV_LIST_STYLE_BTN_REL, th->list.btn.rel);
140
lv_list_set_style(new_list, LV_LIST_STYLE_BTN_PR, th->list.btn.pr);
141
lv_list_set_style(new_list, LV_LIST_STYLE_BTN_TGL_REL, th->list.btn.tgl_rel);
142
lv_list_set_style(new_list, LV_LIST_STYLE_BTN_TGL_PR, th->list.btn.tgl_pr);
143
lv_list_set_style(new_list, LV_LIST_STYLE_BTN_INA, th->list.btn.ina);
144
} else {
145
lv_list_set_style(new_list, LV_LIST_STYLE_BG, &lv_style_transp_fit);
146
lv_list_set_style(new_list, LV_LIST_STYLE_SCRL, &lv_style_pretty);
147
}
148
} else {
149
lv_list_ext_t * copy_ext = lv_obj_get_ext_attr(copy);
150
151
lv_obj_t * copy_btn = lv_list_get_next_btn(copy, NULL);
152
while(copy_btn) {
153
const void * img_src = NULL;
154
#if USE_LV_IMG
155
lv_obj_t * copy_img = lv_list_get_btn_img(copy_btn);
156
if(copy_img) img_src = lv_img_get_src(copy_img);
157
#endif
158
lv_list_add(new_list, img_src, lv_list_get_btn_text(copy_btn), lv_btn_get_action(copy_btn, LV_BTN_ACTION_CLICK));
159
copy_btn = lv_list_get_next_btn(copy, copy_btn);
160
}
161
162
lv_list_set_style(new_list, LV_LIST_STYLE_BTN_REL, copy_ext->styles_btn[LV_BTN_STATE_REL]);
163
lv_list_set_style(new_list, LV_LIST_STYLE_BTN_PR, copy_ext->styles_btn[LV_BTN_STATE_PR]);
164
lv_list_set_style(new_list, LV_LIST_STYLE_BTN_TGL_REL, copy_ext->styles_btn[LV_BTN_STATE_TGL_REL]);
165
lv_list_set_style(new_list, LV_LIST_STYLE_BTN_TGL_PR, copy_ext->styles_btn[LV_BTN_STATE_TGL_REL]);
166
lv_list_set_style(new_list, LV_LIST_STYLE_BTN_INA, copy_ext->styles_btn[LV_BTN_STATE_INA]);
167
168
/*Refresh the style with new signal function*/
169
lv_obj_refresh_style(new_list);
170
}
171
172
173
LV_LOG_INFO("list created");
174
175
176
return new_list;
177
}
178
179
/**
180
* Delete all children of the scrl object, without deleting scrl child.
181
* @param obj pointer to an object
182
*/
183
void lv_list_clean(lv_obj_t * obj)
184
{
185
lv_obj_t * scrl = lv_page_get_scrl(obj);
186
lv_obj_clean(scrl);
187
lv_list_ext_t * ext = lv_obj_get_ext_attr(obj);
188
ext->size = 0;
189
}
190
191
/*======================
192
* Add/remove functions
193
*=====================*/
194
195
/**
196
* Add a list element to the list
197
* @param list pointer to list object
198
* @param img_fn file name of an image before the text (NULL if unused)
199
* @param txt text of the list element (NULL if unused)
200
* @param rel_action pointer to release action function (like with lv_btn)
201
* @return pointer to the new list element which can be customized (a button)
202
*/
203
lv_obj_t * lv_list_add(lv_obj_t * list, const void * img_src, const char * txt, lv_action_t rel_action)
204
{
205
lv_style_t * style = lv_obj_get_style(list);
206
lv_list_ext_t * ext = lv_obj_get_ext_attr(list);
207
ext->size ++;
208
/*Create a list element with the image an the text*/
209
lv_obj_t * liste;
210
liste = lv_btn_create(list, NULL);
211
212
/*Save the original signal function because it will be required in `lv_list_btn_signal`*/
213
if(ancestor_btn_signal == NULL) ancestor_btn_signal = lv_obj_get_signal_func(liste);
214
215
/*Set the default styles*/
216
lv_btn_set_style(liste, LV_BTN_STYLE_REL, ext->styles_btn[LV_BTN_STATE_REL]);
217
lv_btn_set_style(liste, LV_BTN_STYLE_PR, ext->styles_btn[LV_BTN_STATE_PR]);
218
lv_btn_set_style(liste, LV_BTN_STYLE_TGL_REL, ext->styles_btn[LV_BTN_STATE_TGL_REL]);
219
lv_btn_set_style(liste, LV_BTN_STYLE_TGL_PR, ext->styles_btn[LV_BTN_STATE_TGL_PR]);
220
lv_btn_set_style(liste, LV_BTN_STYLE_INA, ext->styles_btn[LV_BTN_STATE_INA]);
221
222
lv_btn_set_action(liste, LV_BTN_ACTION_CLICK, rel_action);
223
lv_page_glue_obj(liste, true);
224
lv_btn_set_layout(liste, LV_LAYOUT_ROW_M);
225
lv_btn_set_fit(liste, false, true);
226
lv_obj_set_protect(liste, LV_PROTECT_PRESS_LOST);
227
lv_obj_set_signal_func(liste, lv_list_btn_signal);
228
229
/*Make the size adjustment*/
230
lv_coord_t w = lv_obj_get_width(list);
231
lv_style_t * style_scrl = lv_obj_get_style(lv_page_get_scrl(list));
232
lv_coord_t pad_hor_tot = style->body.padding.hor + style_scrl->body.padding.hor;
233
w -= pad_hor_tot * 2;
234
235
lv_obj_set_width(liste, w);
236
#if USE_LV_IMG != 0
237
lv_obj_t * img = NULL;
238
if(img_src) {
239
img = lv_img_create(liste, NULL);
240
lv_img_set_src(img, img_src);
241
lv_obj_set_style(img, ext->style_img);
242
lv_obj_set_click(img, false);
243
if(img_signal == NULL) img_signal = lv_obj_get_signal_func(img);
244
}
245
#endif
246
if(txt != NULL) {
247
lv_coord_t btn_hor_pad = ext->styles_btn[LV_BTN_STYLE_REL]->body.padding.hor;
248
lv_obj_t * label = lv_label_create(liste, NULL);
249
lv_label_set_text(label, txt);
250
lv_obj_set_click(label, false);
251
lv_label_set_long_mode(label, LV_LABEL_LONG_ROLL);
252
lv_obj_set_width(label, liste->coords.x2 - label->coords.x1 - btn_hor_pad);
253
if(label_signal == NULL) label_signal = lv_obj_get_signal_func(label);
254
}
255
#if USE_LV_GROUP
256
/* If this is the first item to be added to the list and the list is
257
* focussed, select it */
258
{
259
lv_group_t *g = lv_obj_get_group(list);
260
if(ext->size == 1 && lv_group_get_focused(g) == list) {
261
lv_list_set_btn_selected(list, liste);
262
}
263
}
264
#endif
265
266
return liste;
267
}
268
269
/**
270
* Remove the index of the button in the list
271
* @param list pointer to a list object
272
* @param index pointer to a the button's index in the list, index must be 0 <= index < lv_list_ext_t.size
273
* @return true: successfully deleted
274
*/
275
bool lv_list_remove(const lv_obj_t * list, uint32_t index)
276
{
277
lv_list_ext_t * ext = lv_obj_get_ext_attr(list);
278
if(index >= ext->size) return false;
279
uint32_t count = 0;
280
lv_obj_t * e = lv_list_get_next_btn(list, NULL);
281
while(e != NULL) {
282
if(count == index) {
283
lv_obj_del(e);
284
ext->size --;
285
return true;
286
}
287
e = lv_list_get_next_btn(list, e);
288
count ++;
289
}
290
return false;
291
}
292
293
/*=====================
294
* Setter functions
295
*====================*/
296
297
/**
298
* Set single button selected mode, only one button will be selected if enabled.
299
* @param list pointer to the currently pressed list object
300
* @param mode, enable(true)/disable(false) single selected mode.
301
*/
302
void lv_list_set_single_mode(lv_obj_t *list, bool mode)
303
{
304
lv_list_ext_t * ext = lv_obj_get_ext_attr(list);
305
306
ext->single_mode = mode;
307
}
308
309
#if USE_LV_GROUP
310
311
/**
312
* Make a button selected
313
* @param list pointer to a list object
314
* @param btn pointer to a button to selectthe
315
*/
316
void lv_list_set_btn_selected(lv_obj_t * list, lv_obj_t * btn)
317
{
318
lv_list_ext_t * ext = lv_obj_get_ext_attr(list);
319
320
if(ext->selected_btn) {
321
lv_btn_state_t s = lv_btn_get_state(ext->selected_btn);
322
if(s == LV_BTN_STATE_PR) lv_btn_set_state(ext->selected_btn, LV_BTN_STATE_REL);
323
else if(s == LV_BTN_STATE_TGL_PR) lv_btn_set_state(ext->selected_btn, LV_BTN_STATE_TGL_REL);
324
}
325
326
ext->selected_btn = btn;
327
if( btn != NULL ) {
328
ext->last_sel = btn;
329
}
330
331
if(ext->selected_btn) {
332
lv_btn_state_t s = lv_btn_get_state(ext->selected_btn);
333
if(s == LV_BTN_STATE_REL) lv_btn_set_state(ext->selected_btn, LV_BTN_STATE_PR);
334
else if(s == LV_BTN_STATE_TGL_REL) lv_btn_set_state(ext->selected_btn, LV_BTN_STATE_TGL_PR);
335
336
lv_page_focus(list, ext->selected_btn, ext->anim_time);
337
}
338
}
339
340
#endif
341
342
/**
343
* Set scroll animation duration on 'list_up()' 'list_down()' 'list_focus()'
344
* @param list pointer to a list object
345
* @param anim_time duration of animation [ms]
346
*/
347
void lv_list_set_anim_time(lv_obj_t * list, uint16_t anim_time)
348
{
349
lv_list_ext_t * ext = lv_obj_get_ext_attr(list);
350
#if USE_LV_ANIMATION == 0
351
anim_time = 0;
352
#endif
353
354
if(ext->anim_time == anim_time) return;
355
ext->anim_time = anim_time;
356
}
357
358
/**
359
* Set a style of a list
360
* @param list pointer to a list object
361
* @param type which style should be set
362
* @param style pointer to a style
363
*/
364
void lv_list_set_style(lv_obj_t * list, lv_list_style_t type, lv_style_t * style)
365
{
366
lv_list_ext_t * ext = lv_obj_get_ext_attr(list);
367
lv_btn_style_t btn_style_refr = LV_BTN_STYLE_REL;
368
lv_obj_t * btn;
369
370
switch(type) {
371
case LV_LIST_STYLE_BG:
372
lv_page_set_style(list, LV_PAGE_STYLE_BG, style);
373
/*style change signal will call 'refr_btn_width' */
374
break;
375
case LV_LIST_STYLE_SCRL:
376
lv_page_set_style(list, LV_PAGE_STYLE_SCRL, style);
377
refr_btn_width(list);
378
break;
379
case LV_LIST_STYLE_SB:
380
lv_page_set_style(list, LV_PAGE_STYLE_SB, style);
381
break;
382
case LV_LIST_STYLE_EDGE_FLASH:
383
lv_page_set_style(list, LV_PAGE_STYLE_EDGE_FLASH, style);
384
break;
385
case LV_LIST_STYLE_BTN_REL:
386
ext->styles_btn[LV_BTN_STATE_REL] = style;
387
btn_style_refr = LV_BTN_STYLE_REL;
388
break;
389
case LV_LIST_STYLE_BTN_PR:
390
ext->styles_btn[LV_BTN_STATE_PR] = style;
391
btn_style_refr = LV_BTN_STYLE_PR;
392
break;
393
case LV_LIST_STYLE_BTN_TGL_REL:
394
ext->styles_btn[LV_BTN_STATE_TGL_REL] = style;
395
btn_style_refr = LV_BTN_STYLE_TGL_REL;
396
break;
397
case LV_LIST_STYLE_BTN_TGL_PR:
398
ext->styles_btn[LV_BTN_STATE_TGL_PR] = style;
399
btn_style_refr = LV_BTN_STYLE_TGL_PR;
400
break;
401
case LV_LIST_STYLE_BTN_INA:
402
ext->styles_btn[LV_BTN_STATE_INA] = style;
403
btn_style_refr = LV_BTN_STYLE_INA;
404
break;
405
}
406
407
408
/*Refresh existing buttons' style*/
409
if(type == LV_LIST_STYLE_BTN_PR || type == LV_LIST_STYLE_BTN_REL ||
410
type == LV_LIST_STYLE_BTN_TGL_REL || type == LV_LIST_STYLE_BTN_TGL_PR ||
411
type == LV_LIST_STYLE_BTN_INA) {
412
btn = lv_list_get_prev_btn(list, NULL);
413
while(btn != NULL) {
414
lv_btn_set_style(btn, btn_style_refr, ext->styles_btn[btn_style_refr]);
415
btn = lv_list_get_prev_btn(list, btn);
416
}
417
}
418
}
419
420
/*=====================
421
* Getter functions
422
*====================*/
423
424
/**
425
* Get single button selected mode.
426
* @param list pointer to the currently pressed list object.
427
*/
428
bool lv_list_get_single_mode(lv_obj_t *list)
429
{
430
lv_list_ext_t * ext = lv_obj_get_ext_attr(list);
431
432
return (ext->single_mode);
433
}
434
435
/**
436
* Get the text of a list element
437
* @param btn pointer to list element
438
* @return pointer to the text
439
*/
440
const char * lv_list_get_btn_text(const lv_obj_t * btn)
441
{
442
lv_obj_t * label = lv_list_get_btn_label(btn);
443
if(label == NULL) return "";
444
return lv_label_get_text(label);
445
}
446
447
/**
448
* Get the label object from a list element
449
* @param btn pointer to a list element (button)
450
* @return pointer to the label from the list element or NULL if not found
451
*/
452
lv_obj_t * lv_list_get_btn_label(const lv_obj_t * btn)
453
{
454
lv_obj_t * label = lv_obj_get_child(btn, NULL);
455
if(label == NULL) return NULL;
456
457
while(lv_list_is_list_label(label) == false) {
458
label = lv_obj_get_child(btn, label);
459
if(label == NULL) break;
460
}
461
462
return label;
463
}
464
465
/**
466
* Get the image object from a list element
467
* @param btn pointer to a list element (button)
468
* @return pointer to the image from the list element or NULL if not found
469
*/
470
lv_obj_t * lv_list_get_btn_img(const lv_obj_t * btn)
471
{
472
#if USE_LV_IMG != 0
473
lv_obj_t * img = lv_obj_get_child(btn, NULL);
474
if(img == NULL) return NULL;
475
476
while(lv_list_is_list_img(img) == false) {
477
img = lv_obj_get_child(btn, img);
478
if(img == NULL) break;
479
}
480
481
return img;
482
#else
483
return NULL;
484
#endif
485
}
486
487
/**
488
* Get the previous button from list. (Starts from the top button)
489
* @param list pointer to a list object
490
* @param prev_btn pointer to button. Search the previous before it.
491
* @return pointer to the previous button or NULL when no more buttons
492
*/
493
lv_obj_t * lv_list_get_prev_btn(const lv_obj_t * list, lv_obj_t * prev_btn)
494
{
495
/* Not a good practice but user can add/create objects to the lists manually.
496
* When getting the next button try to be sure that it is at least a button */
497
498
lv_obj_t * btn ;
499
lv_obj_t * scrl = lv_page_get_scrl(list);
500
501
btn = lv_obj_get_child(scrl, prev_btn);
502
if(btn == NULL) return NULL;
503
504
while(lv_list_is_list_btn(btn) == false) {
505
btn = lv_obj_get_child(scrl, btn);
506
if(btn == NULL) break;
507
}
508
509
return btn;
510
}
511
512
513
514
/**
515
* Get the next button from list. (Starts from the bottom button)
516
* @param list pointer to a list object
517
* @param prev_btn pointer to button. Search the next after it.
518
* @return pointer to the next button or NULL when no more buttons
519
*/
520
lv_obj_t * lv_list_get_next_btn(const lv_obj_t * list, lv_obj_t * prev_btn)
521
{
522
/* Not a good practice but user can add/create objects to the lists manually.
523
* When getting the next button try to be sure that it is at least a button */
524
525
lv_obj_t * btn ;
526
lv_obj_t * scrl = lv_page_get_scrl(list);
527
528
btn = lv_obj_get_child_back(scrl, prev_btn);
529
if(btn == NULL) return NULL;
530
531
while(lv_list_is_list_btn(btn) == false) {
532
btn = lv_obj_get_child_back(scrl, btn);
533
if(btn == NULL) break;
534
}
535
536
return btn;
537
}
538
539
/**
540
* Get the index of the button in the list
541
* @param list pointer to a list object. If NULL, assumes btn is part of a list.
542
* @param btn pointer to a list element (button)
543
* @return the index of the button in the list, or -1 of the button not in this list
544
*/
545
int32_t lv_list_get_btn_index(const lv_obj_t * list, const lv_obj_t * btn)
546
{
547
int index = 0;
548
if( list == NULL ){
549
/* no list provided, assuming btn is part of a list */
550
list = lv_obj_get_parent(lv_obj_get_parent(btn));
551
}
552
lv_obj_t * e = lv_list_get_next_btn(list, NULL);
553
while(e != NULL) {
554
if(e == btn) {
555
return index;
556
}
557
index ++;
558
e = lv_list_get_next_btn(list, e);
559
}
560
return -1;
561
}
562
563
/**
564
* Get the number of buttons in the list
565
* @param list pointer to a list object
566
* @return the number of buttons in the list
567
*/
568
uint32_t lv_list_get_size(const lv_obj_t * list)
569
{
570
lv_list_ext_t * ext = lv_obj_get_ext_attr(list);
571
return ext->size;
572
}
573
574
#if USE_LV_GROUP
575
/**
576
* Get the currently selected button
577
* @param list pointer to a list object
578
* @return pointer to the selected button
579
*/
580
lv_obj_t * lv_list_get_btn_selected(const lv_obj_t * list)
581
{
582
lv_list_ext_t * ext = lv_obj_get_ext_attr(list);
583
return ext->selected_btn;
584
}
585
586
#endif
587
588
/**
589
* Get scroll animation duration
590
* @param list pointer to a list object
591
* @return duration of animation [ms]
592
*/
593
uint16_t lv_list_get_anim_time(const lv_obj_t * list)
594
{
595
lv_list_ext_t * ext = lv_obj_get_ext_attr(list);
596
return ext->anim_time;
597
}
598
599
/**
600
* Get a style of a list
601
* @param list pointer to a list object
602
* @param type which style should be get
603
* @return style pointer to a style
604
* */
605
lv_style_t * lv_list_get_style(const lv_obj_t * list, lv_list_style_t type)
606
{
607
lv_style_t * style = NULL;
608
lv_list_ext_t * ext = lv_obj_get_ext_attr(list);
609
610
switch(type) {
611
case LV_LIST_STYLE_BG:
612
style = lv_page_get_style(list, LV_PAGE_STYLE_BG);
613
break;
614
case LV_LIST_STYLE_SCRL:
615
style = lv_page_get_style(list, LV_PAGE_STYLE_SB);
616
break;
617
case LV_LIST_STYLE_SB:
618
style = lv_page_get_style(list, LV_PAGE_STYLE_SCRL);
619
break;
620
case LV_LIST_STYLE_EDGE_FLASH:
621
style = lv_page_get_style(list, LV_PAGE_STYLE_EDGE_FLASH);
622
break;
623
case LV_LIST_STYLE_BTN_REL:
624
style = ext->styles_btn[LV_BTN_STATE_REL];
625
break;
626
case LV_LIST_STYLE_BTN_PR:
627
style = ext->styles_btn[LV_BTN_STATE_PR];
628
break;
629
case LV_LIST_STYLE_BTN_TGL_REL:
630
style = ext->styles_btn[LV_BTN_STATE_TGL_REL];
631
break;
632
case LV_LIST_STYLE_BTN_TGL_PR:
633
style = ext->styles_btn[LV_BTN_STATE_TGL_PR];
634
break;
635
case LV_LIST_STYLE_BTN_INA:
636
style = ext->styles_btn[LV_BTN_STATE_INA];
637
break;
638
default:
639
style = NULL;
640
break;
641
}
642
643
return style;
644
}
645
/*=====================
646
* Other functions
647
*====================*/
648
649
/**
650
* Move the list elements up by one
651
* @param list pointer a to list object
652
*/
653
void lv_list_up(const lv_obj_t * list)
654
{
655
/*Search the first list element which 'y' coordinate is below the parent
656
* and position the list to show this element on the bottom*/
657
lv_obj_t * scrl = lv_page_get_scrl(list);
658
lv_obj_t * e;
659
lv_obj_t * e_prev = NULL;
660
e = lv_list_get_prev_btn(list, NULL);
661
while(e != NULL) {
662
if(e->coords.y2 <= list->coords.y2) {
663
if(e_prev != NULL) {
664
lv_coord_t new_y = lv_obj_get_height(list) - (lv_obj_get_y(e_prev) + lv_obj_get_height(e_prev));
665
lv_list_ext_t * ext = lv_obj_get_ext_attr(list);
666
if(ext->anim_time == 0) {
667
lv_obj_set_y(scrl, new_y);
668
} else {
669
#if USE_LV_ANIMATION
670
lv_anim_t a;
671
a.var = scrl;
672
a.start = lv_obj_get_y(scrl);
673
a.end = new_y;
674
a.fp = (lv_anim_fp_t)lv_obj_set_y;
675
a.path = lv_anim_path_linear;
676
a.end_cb = NULL;
677
a.act_time = 0;
678
a.time = LV_LIST_FOCUS_TIME;
679
a.playback = 0;
680
a.playback_pause = 0;
681
a.repeat = 0;
682
a.repeat_pause = 0;
683
lv_anim_create(&a);
684
#endif
685
}
686
}
687
break;
688
}
689
e_prev = e;
690
e = lv_list_get_prev_btn(list, e);
691
}
692
}
693
694
/**
695
* Move the list elements down by one
696
* @param list pointer to a list object
697
*/
698
void lv_list_down(const lv_obj_t * list)
699
{
700
/*Search the first list element which 'y' coordinate is above the parent
701
* and position the list to show this element on the top*/
702
lv_obj_t * scrl = lv_page_get_scrl(list);
703
lv_obj_t * e;
704
e = lv_list_get_prev_btn(list, NULL);
705
while(e != NULL) {
706
if(e->coords.y1 < list->coords.y1) {
707
lv_coord_t new_y = -lv_obj_get_y(e);
708
lv_list_ext_t * ext = lv_obj_get_ext_attr(list);
709
if(ext->anim_time == 0) {
710
lv_obj_set_y(scrl, new_y);
711
} else {
712
#if USE_LV_ANIMATION
713
lv_anim_t a;
714
a.var = scrl;
715
a.start = lv_obj_get_y(scrl);
716
a.end = new_y;
717
a.fp = (lv_anim_fp_t)lv_obj_set_y;
718
a.path = lv_anim_path_linear;
719
a.end_cb = NULL;
720
a.act_time = 0;
721
a.time = LV_LIST_FOCUS_TIME;
722
a.playback = 0;
723
a.playback_pause = 0;
724
a.repeat = 0;
725
a.repeat_pause = 0;
726
lv_anim_create(&a);
727
728
#endif
729
}
730
break;
731
}
732
e = lv_list_get_prev_btn(list, e);
733
}
734
}
735
736
/**
737
* Focus on a list button. It ensures that the button will be visible on the list.
738
* @param btn pointer to a list button to focus
739
* @param anim_en true: scroll with animation, false: without animation
740
*/
741
void lv_list_focus(const lv_obj_t * btn, bool anim_en)
742
{
743
744
#if USE_LV_ANIMATION == 0
745
anim_en = false;
746
#endif
747
748
lv_obj_t * list = lv_obj_get_parent(lv_obj_get_parent(btn));
749
750
lv_page_focus(list, btn, anim_en == false ? 0 : lv_list_get_anim_time(list));
751
}
752
753
/**********************
754
* STATIC FUNCTIONS
755
**********************/
756
757
/**
758
* Signal function of the list
759
* @param list pointer to a list object
760
* @param sign a signal type from lv_signal_t enum
761
* @param param pointer to a signal specific variable
762
* @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
763
*/
764
static lv_res_t lv_list_signal(lv_obj_t * list, lv_signal_t sign, void * param)
765
{
766
lv_res_t res;
767
768
/* Include the ancient signal function */
769
res = ancestor_page_signal(list, sign, param);
770
if(res != LV_RES_OK) return res;
771
772
if(sign == LV_SIGNAL_CORD_CHG) {
773
/*Be sure the width of the buttons are correct*/
774
lv_coord_t w = lv_obj_get_width(list);
775
if(w != lv_area_get_width(param)) { /*Width changed*/
776
refr_btn_width(list);
777
}
778
} else if(sign == LV_SIGNAL_STYLE_CHG) {
779
/*Because of the possible change of horizontal and vertical padding refresh buttons width */
780
refr_btn_width(list);
781
} else if(sign == LV_SIGNAL_FOCUS) {
782
783
#if USE_LV_GROUP
784
lv_hal_indev_type_t indev_type = lv_indev_get_type(lv_indev_get_act());
785
/*With ENCODER select the first button only in edit mode*/
786
if(indev_type == LV_INDEV_TYPE_ENCODER) {
787
lv_group_t * g = lv_obj_get_group(list);
788
if(lv_group_get_editing(g)) {
789
lv_list_ext_t * ext = lv_obj_get_ext_attr(list);
790
if(ext->last_sel) {
791
/* Select the last used button */
792
lv_list_set_btn_selected(list, ext->last_sel);
793
}
794
else {
795
/*Get the first button and mark it as selected*/
796
lv_list_set_btn_selected(list, lv_list_get_next_btn(list, NULL));
797
}
798
} else {
799
lv_list_set_btn_selected(list, NULL);
800
}
801
}
802
/*Else select the clicked button*/
803
else {
804
/*Mark the last clicked button (if any) as selected because it triggered the focus*/
805
if(last_clicked_btn) {
806
lv_list_set_btn_selected(list, last_clicked_btn);
807
} else {
808
lv_list_ext_t * ext = lv_obj_get_ext_attr(list);
809
if(ext->last_sel) {
810
/* Select the last used button */
811
lv_list_set_btn_selected(list, ext->last_sel);
812
}
813
else {
814
/*Get the first button and mark it as selected*/
815
lv_list_set_btn_selected(list, lv_list_get_next_btn(list, NULL));
816
}
817
}
818
}
819
#endif
820
} else if(sign == LV_SIGNAL_DEFOCUS) {
821
822
#if USE_LV_GROUP
823
/*De-select the selected btn*/
824
lv_list_set_btn_selected(list, NULL);
825
last_clicked_btn = NULL; /*button click will be set if click happens before focus*/
826
lv_list_ext_t * ext = lv_obj_get_ext_attr(list);
827
ext->selected_btn = NULL;
828
#endif
829
} else if(sign == LV_SIGNAL_GET_EDITABLE) {
830
bool * editable = (bool *)param;
831
*editable = true;
832
} else if(sign == LV_SIGNAL_CONTROLL) {
833
834
#if USE_LV_GROUP
835
char c = *((char *)param);
836
if(c == LV_GROUP_KEY_RIGHT || c == LV_GROUP_KEY_DOWN) {
837
lv_list_ext_t * ext = lv_obj_get_ext_attr(list);
838
/*If there is a valid selected button the make the previous selected*/
839
if(ext->selected_btn) {
840
lv_obj_t * btn_prev = lv_list_get_next_btn(list, ext->selected_btn);
841
if(btn_prev) lv_list_set_btn_selected(list, btn_prev);
842
}
843
/*If there is no selected button the make the first selected*/
844
else {
845
lv_obj_t * btn = lv_list_get_next_btn(list, NULL);
846
if(btn) lv_list_set_btn_selected(list, btn); /*If there are no buttons on the list then there is no first button*/
847
}
848
} else if(c == LV_GROUP_KEY_LEFT || c == LV_GROUP_KEY_UP) {
849
lv_list_ext_t * ext = lv_obj_get_ext_attr(list);
850
/*If there is a valid selected button the make the next selected*/
851
if(ext->selected_btn != NULL) {
852
lv_obj_t * btn_next = lv_list_get_prev_btn(list, ext->selected_btn);
853
if(btn_next) lv_list_set_btn_selected(list, btn_next);
854
}
855
/*If there is no selected button the make the first selected*/
856
else {
857
lv_obj_t * btn = lv_list_get_next_btn(list, NULL);
858
if(btn) lv_list_set_btn_selected(list, btn);
859
}
860
} else if(c == LV_GROUP_KEY_ENTER) {
861
/*Get the 'pressed' button*/
862
lv_obj_t * btn = NULL;
863
btn = lv_list_get_prev_btn(list, btn);
864
while(btn != NULL) {
865
if(lv_btn_get_state(btn) == LV_BTN_STATE_PR) break;
866
btn = lv_list_get_prev_btn(list, btn);
867
}
868
869
if(btn != NULL) {
870
lv_list_ext_t * ext = lv_obj_get_ext_attr(list);
871
ext->last_sel = btn;
872
lv_action_t rel_action;
873
rel_action = lv_btn_get_action(btn, LV_BTN_ACTION_CLICK);
874
if(rel_action != NULL) rel_action(btn);
875
}
876
}
877
#endif
878
} else if(sign == LV_SIGNAL_GET_TYPE) {
879
lv_obj_type_t * buf = param;
880
uint8_t i;
881
for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/
882
if(buf->type[i] == NULL) break;
883
}
884
buf->type[i] = "lv_list";
885
}
886
return res;
887
}
888
889
890
/**
891
* Signal function of the list buttons
892
* @param btn pointer to a button on the list
893
* @param sign a signal type from lv_signal_t enum
894
* @param param pointer to a signal specific variable
895
* @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
896
*/
897
static lv_res_t lv_list_btn_signal(lv_obj_t * btn, lv_signal_t sign, void * param)
898
{
899
lv_res_t res;
900
901
/* Include the ancient signal function */
902
res = ancestor_btn_signal(btn, sign, param);
903
if(res != LV_RES_OK) return res;
904
905
if(sign == LV_SIGNAL_RELEASED) {
906
lv_obj_t * list = lv_obj_get_parent(lv_obj_get_parent(btn));
907
lv_list_ext_t * ext = lv_obj_get_ext_attr(list);
908
ext->page.scroll_prop_ip = 0;
909
910
#if USE_LV_GROUP
911
lv_group_t * g = lv_obj_get_group(list);
912
if(lv_group_get_focused(g) == list && lv_indev_is_dragging(lv_indev_get_act()) == false) {
913
/* Is the list is focused then be sure only the button being released
914
* has a pressed state to indicate the selected state on the list*/
915
lv_obj_t * btn_i = lv_list_get_prev_btn(list, NULL);
916
while(btn_i) {
917
lv_btn_state_t s = lv_btn_get_state(btn_i);
918
if(s == LV_BTN_STATE_PR) lv_btn_set_state(btn_i, LV_BTN_STATE_REL);
919
else if(s == LV_BTN_STATE_TGL_PR) lv_btn_set_state(btn_i, LV_BTN_STATE_TGL_REL);
920
btn_i = lv_list_get_prev_btn(list, btn_i);
921
}
922
923
/*Make the released button "selected"*/
924
lv_list_set_btn_selected(list, btn);
925
}
926
927
/* If `click_focus == 1` then LV_SIGNAL_FOCUS need to know which button triggered the focus
928
* to mark it as selected (pressed state)*/
929
last_clicked_btn = btn;
930
#endif
931
if(lv_indev_is_dragging(lv_indev_get_act()) == false && ext->single_mode)
932
{
933
lv_list_btn_single_selected(btn);
934
}
935
}
936
else if(sign == LV_SIGNAL_PRESS_LOST) {
937
lv_obj_t * list = lv_obj_get_parent(lv_obj_get_parent(btn));
938
lv_list_ext_t * ext = lv_obj_get_ext_attr(list);
939
ext->page.scroll_prop_ip = 0;
940
}
941
else if(sign == LV_SIGNAL_CLEANUP) {
942
943
#if USE_LV_GROUP
944
lv_obj_t * list = lv_obj_get_parent(lv_obj_get_parent(btn));
945
lv_obj_t * sel = lv_list_get_btn_selected(list);
946
if(sel == btn) lv_list_set_btn_selected(list, lv_list_get_next_btn(list, btn));
947
#endif
948
}
949
950
951
return res;
952
}
953
954
static void refr_btn_width(lv_obj_t * list)
955
{
956
lv_style_t * style = lv_list_get_style(list, LV_LIST_STYLE_BG);
957
lv_style_t * style_scrl = lv_obj_get_style(lv_page_get_scrl(list));
958
lv_coord_t w = lv_obj_get_width(list);
959
lv_coord_t btn_w = w - (style->body.padding.hor + style_scrl->body.padding.hor) * 2;
960
961
lv_obj_t * btn = lv_list_get_prev_btn(list, NULL);
962
while(btn) {
963
/*Make the size adjustment for each buttons*/
964
if(lv_obj_get_width(btn) != btn_w) {
965
lv_obj_set_width(btn, btn_w);
966
/*Set the label size to roll its text*/
967
lv_obj_t * label = lv_list_get_btn_label(btn);
968
lv_obj_set_width(label, btn->coords.x2 - label->coords.x1);
969
lv_label_set_text(label, NULL);
970
}
971
btn = lv_list_get_prev_btn(list, btn);
972
}
973
}
974
975
/**
976
* Make a single button selected in the list, deselect others, should be called in list btns call back.
977
* @param btn pointer to the currently pressed list btn object
978
*/
979
static void lv_list_btn_single_selected(lv_obj_t *btn)
980
{
981
lv_obj_t *list = lv_obj_get_parent(lv_obj_get_parent(btn));
982
983
lv_obj_t * e = lv_list_get_next_btn(list, NULL);
984
do
985
{
986
if(e == btn)
987
{
988
lv_btn_set_state(e, LV_BTN_STATE_TGL_REL);
989
}
990
else
991
{
992
lv_btn_set_state(e, LV_BTN_STATE_REL);
993
}
994
e = lv_list_get_next_btn(list, e);
995
} while (e != NULL);
996
}
997
998
/**
999
* Check if this is really a list button or another object.
1000
* @param list_btn List button
1001
*/
1002
static bool lv_list_is_list_btn(lv_obj_t * list_btn)
1003
{
1004
lv_obj_type_t type;
1005
1006
lv_obj_get_type(list_btn, &type);
1007
uint8_t cnt;
1008
for(cnt = 0; cnt < LV_MAX_ANCESTOR_NUM; cnt++) {
1009
if(type.type[cnt] == NULL) break;
1010
if(!strcmp(type.type[cnt], "lv_btn"))
1011
return true;
1012
}
1013
return false;
1014
}
1015
1016
/**
1017
* Check if this is really a list label or another object.
1018
* @param list_label List label
1019
*/
1020
static bool lv_list_is_list_label(lv_obj_t * list_label)
1021
{
1022
lv_obj_type_t type;
1023
1024
lv_obj_get_type(list_label, &type);
1025
uint8_t cnt;
1026
for(cnt = 0; cnt < LV_MAX_ANCESTOR_NUM; cnt++) {
1027
if(type.type[cnt] == NULL) break;
1028
if(!strcmp(type.type[cnt], "lv_label"))
1029
return true;
1030
}
1031
return false;
1032
}
1033
1034
/**
1035
* Check if this is really a list image or another object.
1036
* @param list_image List image
1037
*/
1038
static bool lv_list_is_list_img(lv_obj_t * list_img)
1039
{
1040
lv_obj_type_t type;
1041
1042
lv_obj_get_type(list_img, &type);
1043
uint8_t cnt;
1044
for(cnt = 0; cnt < LV_MAX_ANCESTOR_NUM; cnt++) {
1045
if(type.type[cnt] == NULL) break;
1046
if(!strcmp(type.type[cnt], "lv_img"))
1047
return true;
1048
}
1049
return false;
1050
}
1051
1052
#endif
1053
1054