1 /**
2 * @file lv_group.c
3 *
4 */
5
6 /*********************
7 * INCLUDES
8 *********************/
9 #include "lv_group_private.h"
10 #include "../core/lv_obj_private.h"
11 #include "../core/lv_global.h"
12 #include "../indev/lv_indev.h"
13 #include "../misc/lv_types.h"
14
15 /*********************
16 * DEFINES
17 *********************/
18 #define default_group LV_GLOBAL_DEFAULT()->group_default
19 #define group_ll_p &(LV_GLOBAL_DEFAULT()->group_ll)
20
21 /**********************
22 * TYPEDEFS
23 **********************/
24
25 /**********************
26 * STATIC PROTOTYPES
27 **********************/
28 static bool focus_next_core(lv_group_t * group, void * (*begin)(const lv_ll_t *),
29 void * (*move)(const lv_ll_t *, const void *));
30 static void lv_group_refocus(lv_group_t * g);
31 static lv_indev_t * get_indev(const lv_group_t * g);
32
33 /**********************
34 * STATIC VARIABLES
35 **********************/
36
37 /**********************
38 * MACROS
39 **********************/
40
41 /**********************
42 * GLOBAL FUNCTIONS
43 **********************/
44
lv_group_init(void)45 void lv_group_init(void)
46 {
47 lv_ll_init(group_ll_p, sizeof(lv_group_t));
48 }
49
lv_group_deinit(void)50 void lv_group_deinit(void)
51 {
52 lv_ll_clear(group_ll_p);
53 }
54
lv_group_create(void)55 lv_group_t * lv_group_create(void)
56 {
57 lv_group_t * group = lv_ll_ins_head(group_ll_p);
58 LV_ASSERT_MALLOC(group);
59 if(group == NULL) return NULL;
60 lv_ll_init(&group->obj_ll, sizeof(lv_obj_t *));
61
62 group->obj_focus = NULL;
63 group->frozen = 0;
64 group->focus_cb = NULL;
65 group->edge_cb = NULL;
66 group->editing = 0;
67 group->refocus_policy = LV_GROUP_REFOCUS_POLICY_PREV;
68 group->wrap = 1;
69 group->user_data = NULL;
70
71 return group;
72 }
73
lv_group_delete(lv_group_t * group)74 void lv_group_delete(lv_group_t * group)
75 {
76 /*Defocus the currently focused object*/
77 LV_ASSERT_NULL(group);
78 if(group->obj_focus != NULL) {
79 lv_obj_send_event(*group->obj_focus, LV_EVENT_DEFOCUSED, get_indev(group));
80 lv_obj_invalidate(*group->obj_focus);
81 }
82
83 /*Remove the objects from the group*/
84 lv_obj_t ** obj;
85 LV_LL_READ(&group->obj_ll, obj) {
86 if((*obj)->spec_attr)(*obj)->spec_attr->group_p = NULL;
87 }
88
89 /*Remove the group from any indev devices */
90 lv_indev_t * indev = lv_indev_get_next(NULL);
91 while(indev) {
92 if(lv_indev_get_group(indev) == group) {
93 lv_indev_set_group(indev, NULL);
94 }
95 indev = lv_indev_get_next(indev);
96 }
97
98 /*If the group is the default group, set the default group as NULL*/
99 if(group == lv_group_get_default()) lv_group_set_default(NULL);
100
101 lv_ll_clear(&(group->obj_ll));
102 lv_ll_remove(group_ll_p, group);
103 lv_free(group);
104 }
105
lv_group_set_default(lv_group_t * group)106 void lv_group_set_default(lv_group_t * group)
107 {
108 default_group = group;
109 }
110
lv_group_get_default(void)111 lv_group_t * lv_group_get_default(void)
112 {
113 return default_group;
114 }
115
lv_group_add_obj(lv_group_t * group,lv_obj_t * obj)116 void lv_group_add_obj(lv_group_t * group, lv_obj_t * obj)
117 {
118 if(group == NULL) return;
119
120 LV_LOG_TRACE("begin");
121
122 /*Be sure the object is removed from its current group*/
123 lv_group_remove_obj(obj);
124
125 if(obj->spec_attr == NULL) lv_obj_allocate_spec_attr(obj);
126 obj->spec_attr->group_p = group;
127
128 lv_obj_t ** next = lv_ll_ins_tail(&group->obj_ll);
129 LV_ASSERT_MALLOC(next);
130 if(next == NULL) return;
131 *next = obj;
132
133 /*If the head and the tail is equal then there is only one object in the linked list.
134 *In this case automatically activate it*/
135 if(lv_ll_get_head(&group->obj_ll) == next) {
136 lv_group_refocus(group);
137 }
138
139 LV_LOG_TRACE("finished");
140 }
141
lv_group_swap_obj(lv_obj_t * obj1,lv_obj_t * obj2)142 void lv_group_swap_obj(lv_obj_t * obj1, lv_obj_t * obj2)
143 {
144 lv_group_t * g1 = lv_obj_get_group(obj1);
145 lv_group_t * g2 = lv_obj_get_group(obj2);
146 if(g1 != g2) return;
147 if(g1 == NULL) return;
148
149 /*Do not add the object twice*/
150 lv_obj_t ** obj_i;
151 LV_LL_READ(&g1->obj_ll, obj_i) {
152 if((*obj_i) == obj1)(*obj_i) = obj2;
153 else if((*obj_i) == obj2)(*obj_i) = obj1;
154 }
155
156 lv_obj_t * focused = lv_group_get_focused(g1);
157 if(focused == obj1) lv_group_focus_obj(obj2);
158 else if(focused == obj2) lv_group_focus_obj(obj1);
159
160 }
161
lv_group_remove_obj(lv_obj_t * obj)162 void lv_group_remove_obj(lv_obj_t * obj)
163 {
164 lv_group_t * g = lv_obj_get_group(obj);
165 if(g == NULL) return;
166
167 LV_LOG_TRACE("begin");
168
169 /*Focus on the next object*/
170 if(g->obj_focus && *g->obj_focus == obj) {
171 if(g->frozen) g->frozen = 0;
172
173 /*If this is the only object in the group then focus to nothing.*/
174 if(lv_ll_get_head(&g->obj_ll) == g->obj_focus && lv_ll_get_tail(&g->obj_ll) == g->obj_focus) {
175 lv_obj_send_event(*g->obj_focus, LV_EVENT_DEFOCUSED, get_indev(g));
176 }
177 /*If there more objects in the group then focus to the next/prev object*/
178 else {
179 lv_group_refocus(g);
180 }
181 }
182
183 /*If the focuses object is still the same then it was the only object in the group but it will
184 *be deleted. Set the `obj_focus` to NULL to get back to the initial state of the group with
185 *zero objects*/
186 if(g->obj_focus && *g->obj_focus == obj) {
187 g->obj_focus = NULL;
188 }
189
190 /*Search the object and remove it from its group*/
191 lv_obj_t ** i;
192 LV_LL_READ(&g->obj_ll, i) {
193 if(*i == obj) {
194 lv_ll_remove(&g->obj_ll, i);
195 lv_free(i);
196 if(obj->spec_attr) obj->spec_attr->group_p = NULL;
197 break;
198 }
199 }
200 LV_LOG_TRACE("finished");
201 }
202
lv_group_remove_all_objs(lv_group_t * group)203 void lv_group_remove_all_objs(lv_group_t * group)
204 {
205 LV_ASSERT_NULL(group);
206
207 /*Defocus the currently focused object*/
208 if(group->obj_focus != NULL) {
209 lv_obj_send_event(*group->obj_focus, LV_EVENT_DEFOCUSED, get_indev(group));
210 lv_obj_invalidate(*group->obj_focus);
211 group->obj_focus = NULL;
212 }
213
214 /*Remove the objects from the group*/
215 lv_obj_t ** obj;
216 LV_LL_READ(&group->obj_ll, obj) {
217 if((*obj)->spec_attr)(*obj)->spec_attr->group_p = NULL;
218 }
219
220 lv_ll_clear(&(group->obj_ll));
221 }
222
lv_group_focus_obj(lv_obj_t * obj)223 void lv_group_focus_obj(lv_obj_t * obj)
224 {
225 if(obj == NULL) return;
226 lv_group_t * g = lv_obj_get_group(obj);
227 if(g == NULL) return;
228
229 if(g->frozen != 0) return;
230
231 /*On defocus edit mode must be leaved*/
232 lv_group_set_editing(g, false);
233
234 lv_obj_t ** i;
235 LV_LL_READ(&g->obj_ll, i) {
236 if(*i == obj) {
237 if(g->obj_focus != NULL && obj != *g->obj_focus) { /*Do not defocus if the same object needs to be focused again*/
238 lv_result_t res = lv_obj_send_event(*g->obj_focus, LV_EVENT_DEFOCUSED, get_indev(g));
239 if(res != LV_RESULT_OK) return;
240 lv_obj_invalidate(*g->obj_focus);
241 }
242
243 g->obj_focus = i;
244
245 if(g->obj_focus != NULL) {
246 if(g->focus_cb) g->focus_cb(g);
247 lv_result_t res = lv_obj_send_event(*g->obj_focus, LV_EVENT_FOCUSED, get_indev(g));
248 if(res != LV_RESULT_OK) return;
249 lv_obj_invalidate(*g->obj_focus);
250 }
251 break;
252 }
253 }
254 }
255
lv_group_focus_next(lv_group_t * group)256 void lv_group_focus_next(lv_group_t * group)
257 {
258 LV_ASSERT_NULL(group);
259
260 bool focus_changed = focus_next_core(group, lv_ll_get_head, lv_ll_get_next);
261 if(group->edge_cb) {
262 if(!focus_changed)
263 group->edge_cb(group, true);
264 }
265 }
266
lv_group_focus_prev(lv_group_t * group)267 void lv_group_focus_prev(lv_group_t * group)
268 {
269 LV_ASSERT_NULL(group);
270
271 bool focus_changed = focus_next_core(group, lv_ll_get_tail, lv_ll_get_prev);
272 if(group->edge_cb) {
273 if(!focus_changed)
274 group->edge_cb(group, false);
275 }
276 }
277
lv_group_focus_freeze(lv_group_t * group,bool en)278 void lv_group_focus_freeze(lv_group_t * group, bool en)
279 {
280 LV_ASSERT_NULL(group);
281
282 if(en == false) group->frozen = 0;
283 else group->frozen = 1;
284 }
285
lv_group_send_data(lv_group_t * group,uint32_t c)286 lv_result_t lv_group_send_data(lv_group_t * group, uint32_t c)
287 {
288 LV_ASSERT_NULL(group);
289
290 lv_obj_t * act = lv_group_get_focused(group);
291 if(act == NULL) return LV_RESULT_OK;
292
293 if(lv_obj_has_state(act, LV_STATE_DISABLED)) return LV_RESULT_OK;
294
295 return lv_obj_send_event(act, LV_EVENT_KEY, &c);
296 }
297
lv_group_set_focus_cb(lv_group_t * group,lv_group_focus_cb_t focus_cb)298 void lv_group_set_focus_cb(lv_group_t * group, lv_group_focus_cb_t focus_cb)
299 {
300 if(group == NULL) return;
301
302 group->focus_cb = focus_cb;
303 }
304
lv_group_set_edge_cb(lv_group_t * group,lv_group_edge_cb_t edge_cb)305 void lv_group_set_edge_cb(lv_group_t * group, lv_group_edge_cb_t edge_cb)
306 {
307 LV_ASSERT_NULL(group);
308
309 group->edge_cb = edge_cb;
310 }
311
lv_group_set_editing(lv_group_t * group,bool edit)312 void lv_group_set_editing(lv_group_t * group, bool edit)
313 {
314 LV_ASSERT_NULL(group);
315 uint8_t en_val = edit ? 1 : 0;
316
317 if(en_val == group->editing) return; /*Do not set the same mode again*/
318
319 group->editing = en_val;
320 lv_obj_t * focused = lv_group_get_focused(group);
321
322 if(focused) {
323 lv_result_t res = lv_obj_send_event(*group->obj_focus, LV_EVENT_FOCUSED, get_indev(group));
324 if(res != LV_RESULT_OK) return;
325
326 lv_obj_invalidate(focused);
327 }
328 }
329
lv_group_set_refocus_policy(lv_group_t * group,lv_group_refocus_policy_t policy)330 void lv_group_set_refocus_policy(lv_group_t * group, lv_group_refocus_policy_t policy)
331 {
332 LV_ASSERT_NULL(group);
333 group->refocus_policy = policy & 0x01;
334 }
335
lv_group_set_wrap(lv_group_t * group,bool en)336 void lv_group_set_wrap(lv_group_t * group, bool en)
337 {
338 LV_ASSERT_NULL(group);
339 group->wrap = en ? 1 : 0;
340 }
341
lv_group_get_focused(const lv_group_t * group)342 lv_obj_t * lv_group_get_focused(const lv_group_t * group)
343 {
344 if(!group) return NULL;
345 if(group->obj_focus == NULL) return NULL;
346
347 return *group->obj_focus;
348 }
349
lv_group_get_focus_cb(const lv_group_t * group)350 lv_group_focus_cb_t lv_group_get_focus_cb(const lv_group_t * group)
351 {
352 if(!group) return NULL;
353 return group->focus_cb;
354 }
355
lv_group_get_edge_cb(const lv_group_t * group)356 lv_group_edge_cb_t lv_group_get_edge_cb(const lv_group_t * group)
357 {
358 if(!group) return NULL;
359 return group->edge_cb;
360 }
361
lv_group_get_editing(const lv_group_t * group)362 bool lv_group_get_editing(const lv_group_t * group)
363 {
364 if(!group) return false;
365 return group->editing;
366 }
367
lv_group_get_wrap(lv_group_t * group)368 bool lv_group_get_wrap(lv_group_t * group)
369 {
370 if(!group) return false;
371 return group->wrap;
372 }
373
lv_group_get_obj_count(lv_group_t * group)374 uint32_t lv_group_get_obj_count(lv_group_t * group)
375 {
376 LV_ASSERT_NULL(group);
377 return lv_ll_get_len(&group->obj_ll);
378 }
379
lv_group_get_obj_by_index(lv_group_t * group,uint32_t index)380 lv_obj_t * lv_group_get_obj_by_index(lv_group_t * group, uint32_t index)
381 {
382 uint32_t len = 0;
383 lv_obj_t ** obj;
384
385 LV_LL_READ(&group->obj_ll, obj) {
386 if(len == index) {
387 return *obj;
388 }
389 len++;
390 }
391 return NULL;
392 }
393
lv_group_get_count(void)394 uint32_t lv_group_get_count(void)
395 {
396 return lv_ll_get_len(group_ll_p);
397 }
398
lv_group_by_index(uint32_t index)399 lv_group_t * lv_group_by_index(uint32_t index)
400 {
401 uint32_t len = 0;
402 lv_group_t * group;
403
404 LV_LL_READ_BACK(group_ll_p, group) {
405 if(len == index) {
406 return group;
407 }
408 len++;
409 }
410
411 return NULL;
412 }
413 /**********************
414 * STATIC FUNCTIONS
415 **********************/
416
lv_group_refocus(lv_group_t * g)417 static void lv_group_refocus(lv_group_t * g)
418 {
419 /*Refocus must temporarily allow wrapping to work correctly*/
420 uint8_t temp_wrap = g->wrap;
421 g->wrap = 1;
422
423 if(g->refocus_policy == LV_GROUP_REFOCUS_POLICY_NEXT)
424 lv_group_focus_next(g);
425 else if(g->refocus_policy == LV_GROUP_REFOCUS_POLICY_PREV)
426 lv_group_focus_prev(g);
427 /*Restore wrap property*/
428 g->wrap = temp_wrap;
429 }
430
focus_next_core(lv_group_t * group,void * (* begin)(const lv_ll_t *),void * (* move)(const lv_ll_t *,const void *))431 static bool focus_next_core(lv_group_t * group, void * (*begin)(const lv_ll_t *),
432 void * (*move)(const lv_ll_t *, const void *))
433 {
434 bool focus_changed = false;
435 if(group->frozen) return focus_changed;
436
437 lv_obj_t ** obj_next = group->obj_focus;
438 lv_obj_t ** obj_sentinel = NULL;
439 bool can_move = true;
440 bool can_begin = true;
441
442 for(;;) {
443 if(obj_next == NULL) {
444 if(group->wrap || obj_sentinel == NULL) {
445 if(!can_begin) return focus_changed;
446 obj_next = begin(&group->obj_ll);
447 can_move = false;
448 can_begin = false;
449 }
450 else {
451 /*Currently focused object is the last/first in the group, keep it that way*/
452 return focus_changed;
453 }
454 }
455
456 if(obj_sentinel == NULL) {
457 obj_sentinel = obj_next;
458 if(obj_sentinel == NULL) return focus_changed; /*Group is empty*/
459 }
460
461 if(can_move) {
462 obj_next = move(&group->obj_ll, obj_next);
463
464 /*Give up if we walked the entire list and haven't found another visible object*/
465 if(obj_next == obj_sentinel) return focus_changed;
466 }
467
468 can_move = true;
469
470 if(obj_next == NULL) continue;
471 if(lv_obj_get_state(*obj_next) & LV_STATE_DISABLED) continue;
472
473 /*Hidden objects don't receive focus.
474 *If any parent is hidden, the object is also hidden)*/
475 lv_obj_t * parent = *obj_next;
476 while(parent) {
477 if(lv_obj_has_flag(parent, LV_OBJ_FLAG_HIDDEN)) break;
478 parent = lv_obj_get_parent(parent);
479 }
480
481 if(parent && lv_obj_has_flag(parent, LV_OBJ_FLAG_HIDDEN)) continue;
482
483 /*If we got her a good candidate is found*/
484 break;
485 }
486
487 if(obj_next == group->obj_focus) return focus_changed; /*There's only one visible object and it's already focused*/
488
489 if(group->obj_focus) {
490 lv_result_t res = lv_obj_send_event(*group->obj_focus, LV_EVENT_DEFOCUSED, get_indev(group));
491 if(res != LV_RESULT_OK) return focus_changed;
492 lv_obj_invalidate(*group->obj_focus);
493 }
494
495 group->obj_focus = obj_next;
496
497 lv_result_t res = lv_obj_send_event(*group->obj_focus, LV_EVENT_FOCUSED, get_indev(group));
498 if(res != LV_RESULT_OK) return focus_changed;
499
500 lv_obj_invalidate(*group->obj_focus);
501
502 if(group->focus_cb) group->focus_cb(group);
503 focus_changed = true;
504 return focus_changed;
505 }
506
507 /**
508 * Find an indev preferably with POINTER type (because it's the most generic) that uses the given group.
509 * In other words, find an indev, that is related to the given group.
510 * In the worst case simply return the latest indev
511 * @param g a group the find in the indevs
512 * @return the suggested indev
513 */
get_indev(const lv_group_t * g)514 static lv_indev_t * get_indev(const lv_group_t * g)
515 {
516 lv_indev_t * indev_guess = NULL;
517 lv_indev_t * indev = lv_indev_get_next(NULL);
518
519 while(indev) {
520 lv_indev_type_t indev_type = lv_indev_get_type(indev);
521 /*Prefer POINTER*/
522 if(indev_type == LV_INDEV_TYPE_POINTER) return indev;
523 if(lv_indev_get_group(indev) == g) {
524 indev_guess = indev;
525 }
526 indev = lv_indev_get_next(indev);
527 }
528
529 return indev_guess;
530 }
531