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*/