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_gridnav.c (13499B)

      1 /**
      2  * @file lv_gridnav.c
      3  *
      4  */
      5 
      6 /*********************
      7  *      INCLUDES
      8  *********************/
      9 #include "lv_gridnav.h"
     10 #if LV_USE_GRIDNAV
     11 
     12 #include "../../../misc/lv_assert.h"
     13 #include "../../../misc/lv_math.h"
     14 #include "../../../core/lv_indev.h"
     15 
     16 /*********************
     17  *      DEFINES
     18  *********************/
     19 
     20 /**********************
     21  *      TYPEDEFS
     22  **********************/
     23 typedef struct {
     24     lv_gridnav_ctrl_t ctrl;
     25     lv_obj_t * focused_obj;
     26 } lv_gridnav_dsc_t;
     27 
     28 typedef enum {
     29     FIND_LEFT,
     30     FIND_RIGHT,
     31     FIND_TOP,
     32     FIND_BOTTOM,
     33     FIND_NEXT_ROW_FIRST_ITEM,
     34     FIND_PREV_ROW_LAST_ITEM,
     35     FIND_FIRST_ROW,
     36     FIND_LAST_ROW,
     37 } find_mode_t;
     38 
     39 /**********************
     40  *  STATIC PROTOTYPES
     41  **********************/
     42 static void gridnav_event_cb(lv_event_t * e);
     43 static lv_obj_t * find_chid(lv_obj_t * obj, lv_obj_t * start_child, find_mode_t mode);
     44 static lv_obj_t * find_first_focusable(lv_obj_t * obj);
     45 static lv_obj_t * find_last_focusable(lv_obj_t * obj);
     46 static bool obj_is_focuable(lv_obj_t * obj);
     47 static lv_coord_t get_x_center(lv_obj_t * obj);
     48 static lv_coord_t get_y_center(lv_obj_t * obj);
     49 
     50 /**********************
     51  *  STATIC VARIABLES
     52  **********************/
     53 
     54 /**********************
     55  *      MACROS
     56  **********************/
     57 
     58 /**********************
     59  *   GLOBAL FUNCTIONS
     60  **********************/
     61 
     62 void lv_gridnav_add(lv_obj_t * obj, lv_gridnav_ctrl_t ctrl)
     63 {
     64     lv_gridnav_remove(obj); /*Be sure to not add gridnav twice*/
     65 
     66     lv_gridnav_dsc_t * dsc = lv_mem_alloc(sizeof(lv_gridnav_dsc_t));
     67     LV_ASSERT_MALLOC(dsc);
     68     dsc->ctrl = ctrl;
     69     dsc->focused_obj = NULL;
     70     lv_obj_add_event_cb(obj, gridnav_event_cb, LV_EVENT_ALL, dsc);
     71 
     72     lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLL_WITH_ARROW);
     73 }
     74 
     75 void lv_gridnav_remove(lv_obj_t * obj)
     76 {
     77     lv_gridnav_dsc_t * dsc = lv_obj_get_event_user_data(obj, gridnav_event_cb);
     78     if(dsc == NULL) return; /* no gridnav on this object */
     79 
     80     lv_mem_free(dsc);
     81     lv_obj_remove_event_cb(obj, gridnav_event_cb);
     82 }
     83 
     84 void lv_gridnav_set_focused(lv_obj_t * cont, lv_obj_t * to_focus, lv_anim_enable_t anim_en)
     85 {
     86     LV_ASSERT_NULL(to_focus);
     87     lv_gridnav_dsc_t * dsc = lv_obj_get_event_user_data(cont, gridnav_event_cb);
     88     if(dsc == NULL) {
     89         LV_LOG_WARN("`cont` is not a gridnav container");
     90         return;
     91     }
     92 
     93     if(obj_is_focuable(to_focus) == false) {
     94         LV_LOG_WARN("The object to focus is not focusable");
     95         return;
     96     }
     97 
     98     lv_obj_clear_state(dsc->focused_obj, LV_STATE_FOCUSED | LV_STATE_FOCUS_KEY);
     99     lv_obj_add_state(to_focus, LV_STATE_FOCUSED | LV_STATE_FOCUS_KEY);
    100     lv_obj_scroll_to_view(to_focus, anim_en);
    101     dsc->focused_obj = to_focus;
    102 
    103 }
    104 
    105 /**********************
    106  *   STATIC FUNCTIONS
    107  **********************/
    108 
    109 static void gridnav_event_cb(lv_event_t * e)
    110 {
    111     lv_obj_t * obj = lv_event_get_current_target(e);
    112     lv_gridnav_dsc_t * dsc = lv_event_get_user_data(e);
    113     lv_event_code_t code = lv_event_get_code(e);
    114 
    115     if(code == LV_EVENT_KEY) {
    116         uint32_t child_cnt = lv_obj_get_child_cnt(obj);
    117         if(child_cnt == 0) return;
    118 
    119         if(dsc->focused_obj == NULL) dsc->focused_obj = find_first_focusable(obj);
    120         if(dsc->focused_obj == NULL) return;
    121 
    122         uint32_t key = lv_event_get_key(e);
    123         lv_obj_t * guess = NULL;
    124 
    125         if(key == LV_KEY_RIGHT) {
    126             if((dsc->ctrl & LV_GRIDNAV_CTRL_SCROLL_FIRST) && lv_obj_has_flag(dsc->focused_obj, LV_OBJ_FLAG_SCROLLABLE) &&
    127                lv_obj_get_scroll_right(dsc->focused_obj) > 0) {
    128                 lv_coord_t d = lv_obj_get_width(dsc->focused_obj) / 4;
    129                 if(d <= 0) d = 1;
    130                 lv_obj_scroll_by_bounded(dsc->focused_obj, -d, 0, LV_ANIM_ON);
    131             }
    132             else {
    133                 guess = find_chid(obj, dsc->focused_obj, FIND_RIGHT);
    134                 if(guess == NULL) {
    135                     if(dsc->ctrl & LV_GRIDNAV_CTRL_ROLLOVER) {
    136                         guess = find_chid(obj, dsc->focused_obj, FIND_NEXT_ROW_FIRST_ITEM);
    137                         if(guess == NULL) guess = find_first_focusable(obj);
    138                     }
    139                     else {
    140                         lv_group_focus_next(lv_obj_get_group(obj));
    141                     }
    142                 }
    143             }
    144         }
    145         else if(key == LV_KEY_LEFT) {
    146             if((dsc->ctrl & LV_GRIDNAV_CTRL_SCROLL_FIRST) && lv_obj_has_flag(dsc->focused_obj, LV_OBJ_FLAG_SCROLLABLE) &&
    147                lv_obj_get_scroll_left(dsc->focused_obj) > 0) {
    148                 lv_coord_t d = lv_obj_get_width(dsc->focused_obj) / 4;
    149                 if(d <= 0) d = 1;
    150                 lv_obj_scroll_by_bounded(dsc->focused_obj, d, 0, LV_ANIM_ON);
    151             }
    152             else {
    153                 guess = find_chid(obj, dsc->focused_obj, FIND_LEFT);
    154                 if(guess == NULL) {
    155                     if(dsc->ctrl & LV_GRIDNAV_CTRL_ROLLOVER) {
    156                         guess = find_chid(obj, dsc->focused_obj, FIND_PREV_ROW_LAST_ITEM);
    157                         if(guess == NULL) guess = find_last_focusable(obj);
    158                     }
    159                     else {
    160                         lv_group_focus_prev(lv_obj_get_group(obj));
    161                     }
    162                 }
    163             }
    164         }
    165         else if(key == LV_KEY_DOWN) {
    166             if((dsc->ctrl & LV_GRIDNAV_CTRL_SCROLL_FIRST) && lv_obj_has_flag(dsc->focused_obj, LV_OBJ_FLAG_SCROLLABLE) &&
    167                lv_obj_get_scroll_bottom(dsc->focused_obj) > 0) {
    168                 lv_coord_t d = lv_obj_get_height(dsc->focused_obj) / 4;
    169                 if(d <= 0) d = 1;
    170                 lv_obj_scroll_by_bounded(dsc->focused_obj, 0, -d, LV_ANIM_ON);
    171             }
    172             else {
    173                 guess = find_chid(obj, dsc->focused_obj, FIND_BOTTOM);
    174                 if(guess == NULL) {
    175                     if(dsc->ctrl & LV_GRIDNAV_CTRL_ROLLOVER) {
    176                         guess = find_chid(obj, dsc->focused_obj, FIND_FIRST_ROW);
    177                     }
    178                     else {
    179                         lv_group_focus_next(lv_obj_get_group(obj));
    180                     }
    181                 }
    182             }
    183         }
    184         else if(key == LV_KEY_UP) {
    185             if((dsc->ctrl & LV_GRIDNAV_CTRL_SCROLL_FIRST) && lv_obj_has_flag(dsc->focused_obj, LV_OBJ_FLAG_SCROLLABLE) &&
    186                lv_obj_get_scroll_top(dsc->focused_obj) > 0) {
    187                 lv_coord_t d = lv_obj_get_height(dsc->focused_obj) / 4;
    188                 if(d <= 0) d = 1;
    189                 lv_obj_scroll_by_bounded(dsc->focused_obj, 0, d, LV_ANIM_ON);
    190             }
    191             else {
    192                 guess = find_chid(obj, dsc->focused_obj, FIND_TOP);
    193                 if(guess == NULL) {
    194                     if(dsc->ctrl & LV_GRIDNAV_CTRL_ROLLOVER) {
    195                         guess = find_chid(obj, dsc->focused_obj, FIND_LAST_ROW);
    196                     }
    197                     else {
    198                         lv_group_focus_prev(lv_obj_get_group(obj));
    199                     }
    200                 }
    201             }
    202         }
    203         else {
    204             if(lv_group_get_focused(lv_obj_get_group(obj)) == obj) {
    205                 lv_event_send(dsc->focused_obj, LV_EVENT_KEY, &key);
    206             }
    207         }
    208 
    209         if(guess && guess != dsc->focused_obj) {
    210             lv_obj_clear_state(dsc->focused_obj, LV_STATE_FOCUSED | LV_STATE_FOCUS_KEY);
    211             lv_obj_add_state(guess, LV_STATE_FOCUSED | LV_STATE_FOCUS_KEY);
    212             lv_obj_scroll_to_view(guess, LV_ANIM_ON);
    213             dsc->focused_obj = guess;
    214         }
    215     }
    216     else if(code == LV_EVENT_FOCUSED) {
    217         if(dsc->focused_obj == NULL)  dsc->focused_obj = find_first_focusable(obj);
    218         if(dsc->focused_obj) {
    219             lv_obj_add_state(dsc->focused_obj, LV_STATE_FOCUSED | LV_STATE_FOCUS_KEY);
    220             lv_obj_scroll_to_view(dsc->focused_obj, LV_ANIM_OFF);
    221         }
    222     }
    223     else if(code == LV_EVENT_DEFOCUSED) {
    224         if(dsc->focused_obj) {
    225             lv_obj_clear_state(dsc->focused_obj, LV_STATE_FOCUSED | LV_STATE_FOCUS_KEY);
    226         }
    227     }
    228     else if(code == LV_EVENT_CHILD_CREATED) {
    229         lv_obj_t * child = lv_event_get_target(e);
    230         if(lv_obj_get_parent(child) == obj) {
    231             if(dsc->focused_obj == NULL) {
    232                 dsc->focused_obj = child;
    233                 if(lv_obj_has_state(obj, LV_STATE_FOCUSED)) {
    234                     lv_obj_add_state(child, LV_STATE_FOCUSED | LV_STATE_FOCUS_KEY);
    235                     lv_obj_scroll_to_view(child, LV_ANIM_OFF);
    236                 }
    237             }
    238         }
    239     }
    240     else if(code == LV_EVENT_CHILD_DELETED) {
    241         /*This event bubble, so be sure this object's child was deleted.
    242          *As we don't know which object was deleted we can't make the next focused.
    243          *So make the first object focused*/
    244         lv_obj_t * target = lv_event_get_target(e);
    245         if(target == obj) {
    246             dsc->focused_obj = find_first_focusable(obj);
    247         }
    248     }
    249     else if(code == LV_EVENT_DELETE) {
    250         lv_gridnav_remove(obj);
    251     }
    252     else if(code == LV_EVENT_PRESSED || code == LV_EVENT_PRESSING || code == LV_EVENT_PRESS_LOST ||
    253             code == LV_EVENT_LONG_PRESSED || code == LV_EVENT_LONG_PRESSED_REPEAT ||
    254             code == LV_EVENT_CLICKED || code == LV_EVENT_RELEASED) {
    255         if(lv_group_get_focused(lv_obj_get_group(obj)) == obj) {
    256             /*Forward press/release related event too*/
    257             lv_indev_type_t t = lv_indev_get_type(lv_indev_get_act());
    258             if(t == LV_INDEV_TYPE_ENCODER || t == LV_INDEV_TYPE_KEYPAD) {
    259                 lv_event_send(dsc->focused_obj, code, lv_indev_get_act());
    260             }
    261         }
    262     }
    263 }
    264 
    265 static lv_obj_t * find_chid(lv_obj_t * obj, lv_obj_t * start_child, find_mode_t mode)
    266 {
    267     lv_coord_t x_start = get_x_center(start_child);
    268     lv_coord_t y_start = get_y_center(start_child);
    269     uint32_t child_cnt = lv_obj_get_child_cnt(obj);
    270     lv_obj_t * guess = NULL;
    271     lv_coord_t x_err_guess = LV_COORD_MAX;
    272     lv_coord_t y_err_guess = LV_COORD_MAX;
    273     lv_coord_t h_half = lv_obj_get_height(start_child) / 2;
    274     lv_coord_t h_max = lv_obj_get_height(obj) + lv_obj_get_scroll_top(obj) + lv_obj_get_scroll_bottom(obj);
    275     uint32_t i;
    276     for(i = 0; i < child_cnt; i++) {
    277         lv_obj_t * child = lv_obj_get_child(obj, i);
    278         if(child == start_child) continue;
    279         if(obj_is_focuable(child) == false) continue;
    280 
    281         lv_coord_t x_err = 0;
    282         lv_coord_t y_err = 0;
    283         switch(mode) {
    284             case FIND_LEFT:
    285                 x_err = get_x_center(child) - x_start;
    286                 y_err = get_y_center(child) - y_start;
    287                 if(x_err >= 0) continue;    /*It's on the right*/
    288                 if(LV_ABS(y_err) > h_half) continue;    /*Too far*/
    289                 break;
    290             case FIND_RIGHT:
    291                 x_err = get_x_center(child) - x_start;
    292                 y_err = get_y_center(child) - y_start;
    293                 if(x_err <= 0) continue;    /*It's on the left*/
    294                 if(LV_ABS(y_err) > h_half) continue;    /*Too far*/
    295                 break;
    296             case FIND_TOP:
    297                 x_err = get_x_center(child) - x_start;
    298                 y_err = get_y_center(child) - y_start;
    299                 if(y_err >= 0) continue;    /*It's on the bottom*/
    300                 break;
    301             case FIND_BOTTOM:
    302                 x_err = get_x_center(child) - x_start;
    303                 y_err = get_y_center(child) - y_start;
    304                 if(y_err <= 0) continue;    /*It's on the top*/
    305                 break;
    306             case FIND_NEXT_ROW_FIRST_ITEM:
    307                 y_err = get_y_center(child) - y_start;
    308                 if(y_err <= 0) continue;    /*It's on the top*/
    309                 x_err = lv_obj_get_x(child);
    310                 break;
    311             case FIND_PREV_ROW_LAST_ITEM:
    312                 y_err = get_y_center(child) - y_start;
    313                 if(y_err >= 0) continue;    /*It's on the bottom*/
    314                 x_err = obj->coords.x2 - child->coords.x2;
    315                 break;
    316             case FIND_FIRST_ROW:
    317                 x_err = get_x_center(child) - x_start;
    318                 y_err = lv_obj_get_y(child);
    319                 break;
    320             case FIND_LAST_ROW:
    321                 x_err = get_x_center(child) - x_start;
    322                 y_err = h_max - lv_obj_get_y(child);
    323         }
    324 
    325         if(guess == NULL ||
    326            (y_err * y_err + x_err * x_err < y_err_guess * y_err_guess + x_err_guess * x_err_guess)) {
    327             guess = child;
    328             x_err_guess  = x_err;
    329             y_err_guess  = y_err;
    330         }
    331     }
    332     return guess;
    333 }
    334 
    335 static lv_obj_t * find_first_focusable(lv_obj_t * obj)
    336 {
    337     uint32_t child_cnt = lv_obj_get_child_cnt(obj);
    338     uint32_t i;
    339     for(i = 0; i < child_cnt; i++) {
    340         lv_obj_t * child = lv_obj_get_child(obj, i);
    341         if(obj_is_focuable(child)) return child;
    342 
    343     }
    344     return NULL;
    345 }
    346 
    347 static lv_obj_t * find_last_focusable(lv_obj_t * obj)
    348 {
    349     uint32_t child_cnt = lv_obj_get_child_cnt(obj);
    350     int32_t i;
    351     for(i = child_cnt - 1; i >= 0; i++) {
    352         lv_obj_t * child = lv_obj_get_child(obj, i);
    353         if(obj_is_focuable(child)) return child;
    354     }
    355     return NULL;
    356 }
    357 
    358 static bool obj_is_focuable(lv_obj_t * obj)
    359 {
    360     if(lv_obj_has_flag(obj, LV_OBJ_FLAG_HIDDEN)) return false;
    361     if(lv_obj_has_flag(obj, LV_OBJ_FLAG_CLICKABLE | LV_OBJ_FLAG_CLICK_FOCUSABLE)) return true;
    362     else return false;
    363 }
    364 
    365 static lv_coord_t get_x_center(lv_obj_t * obj)
    366 {
    367     return obj->coords.x1 + lv_area_get_width(&obj->coords) / 2;
    368 }
    369 
    370 static lv_coord_t get_y_center(lv_obj_t * obj)
    371 {
    372     return obj->coords.y1 + lv_area_get_height(&obj->coords) / 2;
    373 }
    374 
    375 #endif /*LV_USE_GRIDNAV*/