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