acid-drop

- Hacking the planet from a LilyGo T-Deck using custom firmware
git clone git://git.acid.vegas/acid-drop.git
Log | Files | Refs | Archive | README | LICENSE

lv_group.c (13024B)

      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 
     45 void _lv_group_init(void)
     46 {
     47     _lv_ll_init(&LV_GC_ROOT(_lv_group_ll), sizeof(lv_group_t));
     48 }
     49 
     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 
     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 
     99 void lv_group_set_default(lv_group_t * group)
    100 {
    101     default_group = group;
    102 }
    103 
    104 lv_group_t * lv_group_get_default(void)
    105 {
    106     return default_group;
    107 }
    108 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    283     if(lv_obj_has_state(act, LV_STATE_DISABLED)) return LV_RES_OK;
    284 
    285     return lv_event_send(act, LV_EVENT_KEY, &c);
    286 }
    287 
    288 void lv_group_set_focus_cb(lv_group_t * group, lv_group_focus_cb_t focus_cb)
    289 {
    290     group->focus_cb = focus_cb;
    291 }
    292 
    293 void lv_group_set_editing(lv_group_t * group, bool edit)
    294 {
    295     if(group == NULL) return;
    296     uint8_t en_val = edit ? 1 : 0;
    297 
    298     if(en_val == group->editing) return; /*Do not set the same mode again*/
    299 
    300     group->editing     = en_val;
    301     lv_obj_t * focused = lv_group_get_focused(group);
    302 
    303     if(focused) {
    304         lv_res_t res = lv_event_send(*group->obj_focus, LV_EVENT_FOCUSED, get_indev(group));
    305         if(res != LV_RES_OK) return;
    306 
    307         lv_obj_invalidate(focused);
    308     }
    309 }
    310 
    311 void lv_group_set_refocus_policy(lv_group_t * group, lv_group_refocus_policy_t policy)
    312 {
    313     group->refocus_policy = policy & 0x01;
    314 }
    315 
    316 void lv_group_set_wrap(lv_group_t * group, bool en)
    317 {
    318     group->wrap = en ? 1 : 0;
    319 }
    320 
    321 lv_obj_t * lv_group_get_focused(const lv_group_t * group)
    322 {
    323     if(!group) return NULL;
    324     if(group->obj_focus == NULL) return NULL;
    325 
    326     return *group->obj_focus;
    327 }
    328 
    329 lv_group_focus_cb_t lv_group_get_focus_cb(const lv_group_t * group)
    330 {
    331     if(!group) return NULL;
    332     return group->focus_cb;
    333 }
    334 
    335 bool lv_group_get_editing(const lv_group_t * group)
    336 {
    337     if(!group) return false;
    338     return group->editing ? true : false;
    339 }
    340 
    341 bool lv_group_get_wrap(lv_group_t * group)
    342 {
    343     if(!group) return false;
    344     return group->wrap ? true : false;
    345 }
    346 
    347 uint32_t lv_group_get_obj_count(lv_group_t * group)
    348 {
    349     return _lv_ll_get_len(&group->obj_ll);
    350 }
    351 /**********************
    352  *   STATIC FUNCTIONS
    353  **********************/
    354 
    355 static void lv_group_refocus(lv_group_t * g)
    356 {
    357     /*Refocus must temporarily allow wrapping to work correctly*/
    358     uint8_t temp_wrap = g->wrap;
    359     g->wrap           = 1;
    360 
    361     if(g->refocus_policy == LV_GROUP_REFOCUS_POLICY_NEXT)
    362         lv_group_focus_next(g);
    363     else if(g->refocus_policy == LV_GROUP_REFOCUS_POLICY_PREV)
    364         lv_group_focus_prev(g);
    365     /*Restore wrap property*/
    366     g->wrap = temp_wrap;
    367 }
    368 
    369 static void focus_next_core(lv_group_t * group, void * (*begin)(const lv_ll_t *),
    370                             void * (*move)(const lv_ll_t *, const void *))
    371 {
    372     if(group->frozen) return;
    373 
    374     lv_obj_t ** obj_next     = group->obj_focus;
    375     lv_obj_t ** obj_sentinel = NULL;
    376     bool can_move            = true;
    377     bool can_begin           = true;
    378 
    379     for(;;) {
    380         if(obj_next == NULL) {
    381             if(group->wrap || obj_sentinel == NULL) {
    382                 if(!can_begin) return;
    383                 obj_next  = begin(&group->obj_ll);
    384                 can_move  = false;
    385                 can_begin = false;
    386             }
    387             else {
    388                 /*Currently focused object is the last/first in the group, keep it that way*/
    389                 return;
    390             }
    391         }
    392 
    393         if(obj_sentinel == NULL) {
    394             obj_sentinel = obj_next;
    395             if(obj_sentinel == NULL) return; /*Group is empty*/
    396         }
    397 
    398         if(can_move) {
    399             obj_next = move(&group->obj_ll, obj_next);
    400 
    401             /*Give up if we walked the entire list and haven't found another visible object*/
    402             if(obj_next == obj_sentinel) return;
    403         }
    404 
    405         can_move = true;
    406 
    407         if(obj_next == NULL) continue;
    408         if(lv_obj_get_state(*obj_next) & LV_STATE_DISABLED) continue;
    409 
    410         /*Hidden objects don't receive focus.
    411          *If any parent is hidden, the object is also hidden)*/
    412         lv_obj_t * parent = *obj_next;
    413         while(parent) {
    414             if(lv_obj_has_flag(parent, LV_OBJ_FLAG_HIDDEN)) break;
    415             parent = lv_obj_get_parent(parent);
    416         }
    417 
    418         if(parent && lv_obj_has_flag(parent, LV_OBJ_FLAG_HIDDEN)) continue;
    419 
    420         /*If we got her a good candidate is found*/
    421         break;
    422     }
    423 
    424     if(obj_next == group->obj_focus) return; /*There's only one visible object and it's already focused*/
    425 
    426     if(group->obj_focus) {
    427         lv_res_t res = lv_event_send(*group->obj_focus, LV_EVENT_DEFOCUSED, get_indev(group));
    428         if(res != LV_RES_OK) return;
    429         lv_obj_invalidate(*group->obj_focus);
    430     }
    431 
    432     group->obj_focus = obj_next;
    433 
    434     lv_res_t res = lv_event_send(*group->obj_focus, LV_EVENT_FOCUSED, get_indev(group));
    435     if(res != LV_RES_OK) return;
    436 
    437     lv_obj_invalidate(*group->obj_focus);
    438 
    439     if(group->focus_cb) group->focus_cb(group);
    440 }
    441 
    442 /**
    443  * Find an indev preferably with KEYPAD or ENCOEDR type that uses the given group.
    444  * In other words, find an indev, that is related to the given group.
    445  * In the worst case simply return the latest indev
    446  * @param g     a group the find in the indevs
    447  * @return      the suggested indev
    448  */
    449 static lv_indev_t * get_indev(const lv_group_t * g)
    450 {
    451     lv_indev_t * indev_encoder = NULL;
    452     lv_indev_t * indev_group = NULL;
    453     lv_indev_t * indev = lv_indev_get_next(NULL);
    454     while(indev) {
    455         lv_indev_type_t indev_type = lv_indev_get_type(indev);
    456         if(indev->group == g) {
    457             /*Prefer KEYPAD*/
    458             if(indev_type == LV_INDEV_TYPE_KEYPAD) return indev;
    459             if(indev_type == LV_INDEV_TYPE_ENCODER) indev_encoder = indev;
    460             indev_group = indev;
    461         }
    462         indev = lv_indev_get_next(indev);
    463     }
    464 
    465     if(indev_encoder) return indev_encoder;
    466     if(indev_group) return indev_group;
    467 
    468     /*In lack of a better option use the first input device. (It can be NULL if there is no input device)*/
    469     return lv_indev_get_next(NULL);
    470 }
    471