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