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