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_roller.c (27972B)
1 /** 2 * @file lv_roller.c 3 * 4 */ 5 6 /********************* 7 * INCLUDES 8 *********************/ 9 #include "lv_roller.h" 10 #if LV_USE_ROLLER != 0 11 12 #include "../misc/lv_assert.h" 13 #include "../draw/lv_draw.h" 14 #include "../core/lv_group.h" 15 #include "../core/lv_indev.h" 16 #include "../core/lv_indev_scroll.h" 17 18 /********************* 19 * DEFINES 20 *********************/ 21 #define MY_CLASS &lv_roller_class 22 #define MY_CLASS_LABEL &lv_roller_label_class 23 24 /********************** 25 * TYPEDEFS 26 **********************/ 27 28 /********************** 29 * STATIC PROTOTYPES 30 **********************/ 31 static void lv_roller_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj); 32 static void lv_roller_event(const lv_obj_class_t * class_p, lv_event_t * e); 33 static void lv_roller_label_event(const lv_obj_class_t * class_p, lv_event_t * e); 34 static void draw_main(lv_event_t * e); 35 static void draw_label(lv_event_t * e); 36 static void get_sel_area(lv_obj_t * obj, lv_area_t * sel_area); 37 static void refr_position(lv_obj_t * obj, lv_anim_enable_t animen); 38 static lv_res_t release_handler(lv_obj_t * obj); 39 static void inf_normalize(lv_obj_t * obj_scrl); 40 static lv_obj_t * get_label(const lv_obj_t * obj); 41 static lv_coord_t get_selected_label_width(const lv_obj_t * obj); 42 static void scroll_anim_ready_cb(lv_anim_t * a); 43 static void set_y_anim(void * obj, int32_t v); 44 45 /********************** 46 * STATIC VARIABLES 47 **********************/ 48 const lv_obj_class_t lv_roller_class = { 49 .constructor_cb = lv_roller_constructor, 50 .event_cb = lv_roller_event, 51 .width_def = LV_SIZE_CONTENT, 52 .height_def = LV_DPI_DEF, 53 .instance_size = sizeof(lv_roller_t), 54 .editable = LV_OBJ_CLASS_EDITABLE_TRUE, 55 .group_def = LV_OBJ_CLASS_GROUP_DEF_TRUE, 56 .base_class = &lv_obj_class 57 }; 58 59 const lv_obj_class_t lv_roller_label_class = { 60 .event_cb = lv_roller_label_event, 61 .instance_size = sizeof(lv_label_t), 62 .base_class = &lv_label_class 63 }; 64 65 /********************** 66 * MACROS 67 **********************/ 68 69 /********************** 70 * GLOBAL FUNCTIONS 71 **********************/ 72 73 /** 74 * Create a roller object 75 * @param parent pointer to an object, it will be the parent of the new roller 76 * @return pointer to the created roller 77 */ 78 lv_obj_t * lv_roller_create(lv_obj_t * parent) 79 { 80 LV_LOG_INFO("begin"); 81 lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent); 82 lv_obj_class_init_obj(obj); 83 return obj; 84 } 85 86 /*===================== 87 * Setter functions 88 *====================*/ 89 90 /** 91 * Set the options on a roller 92 * @param roller pointer to roller object 93 * @param options a string with '\n' separated options. E.g. "One\nTwo\nThree" 94 * @param mode `LV_ROLLER_MODE_NORMAL` or `LV_ROLLER_MODE_INFINITE` 95 */ 96 void lv_roller_set_options(lv_obj_t * obj, const char * options, lv_roller_mode_t mode) 97 { 98 LV_ASSERT_OBJ(obj, MY_CLASS); 99 LV_ASSERT_NULL(options); 100 101 lv_roller_t * roller = (lv_roller_t *)obj; 102 lv_obj_t * label = get_label(obj); 103 104 roller->sel_opt_id = 0; 105 roller->sel_opt_id_ori = 0; 106 107 /*Count the '\n'-s to determine the number of options*/ 108 roller->option_cnt = 0; 109 uint32_t cnt; 110 for(cnt = 0; options[cnt] != '\0'; cnt++) { 111 if(options[cnt] == '\n') roller->option_cnt++; 112 } 113 roller->option_cnt++; /*Last option has no `\n`*/ 114 115 if(mode == LV_ROLLER_MODE_NORMAL) { 116 roller->mode = LV_ROLLER_MODE_NORMAL; 117 lv_label_set_text(label, options); 118 } 119 else { 120 roller->mode = LV_ROLLER_MODE_INFINITE; 121 122 size_t opt_len = strlen(options) + 1; /*+1 to add '\n' after option lists*/ 123 char * opt_extra = lv_mem_buf_get(opt_len * LV_ROLLER_INF_PAGES); 124 uint8_t i; 125 for(i = 0; i < LV_ROLLER_INF_PAGES; i++) { 126 strcpy(&opt_extra[opt_len * i], options); 127 opt_extra[opt_len * (i + 1) - 1] = '\n'; 128 } 129 opt_extra[opt_len * LV_ROLLER_INF_PAGES - 1] = '\0'; 130 lv_label_set_text(label, opt_extra); 131 lv_mem_buf_release(opt_extra); 132 133 roller->sel_opt_id = ((LV_ROLLER_INF_PAGES / 2) + 0) * roller->option_cnt; 134 135 roller->option_cnt = roller->option_cnt * LV_ROLLER_INF_PAGES; 136 inf_normalize(obj); 137 } 138 139 roller->sel_opt_id_ori = roller->sel_opt_id; 140 141 /*If the selected text has larger font the label needs some extra draw padding to draw it.*/ 142 lv_obj_refresh_ext_draw_size(label); 143 144 } 145 146 /** 147 * Set the selected option 148 * @param roller pointer to a roller object 149 * @param sel_opt id of the selected option (0 ... number of option - 1); 150 * @param anim_en LV_ANIM_ON: set with animation; LV_ANOM_OFF set immediately 151 */ 152 void lv_roller_set_selected(lv_obj_t * obj, uint16_t sel_opt, lv_anim_enable_t anim) 153 { 154 LV_ASSERT_OBJ(obj, MY_CLASS); 155 156 /*Set the value even if it's the same as the current value because 157 *if moving to the next option with an animation which was just deleted in the PRESS Call the ancestor's event handler 158 *nothing will continue the animation.*/ 159 160 lv_roller_t * roller = (lv_roller_t *)obj; 161 162 /*In infinite mode interpret the new ID relative to the currently visible "page"*/ 163 if(roller->mode == LV_ROLLER_MODE_INFINITE) { 164 uint32_t real_option_cnt = roller->option_cnt / LV_ROLLER_INF_PAGES; 165 uint16_t current_page = roller->sel_opt_id / real_option_cnt; 166 /*Set by the user to e.g. 0, 1, 2, 3... 167 *Upscale the value to the current page*/ 168 if(sel_opt < real_option_cnt) { 169 uint16_t act_opt = roller->sel_opt_id - current_page * real_option_cnt; 170 int32_t sel_opt_signed = sel_opt; 171 /*Huge jump? Probably from last to first or first to last option.*/ 172 if(LV_ABS((int16_t)act_opt - sel_opt) > real_option_cnt / 2) { 173 if(act_opt > sel_opt) sel_opt_signed += real_option_cnt; 174 else sel_opt_signed -= real_option_cnt; 175 } 176 sel_opt = sel_opt_signed + real_option_cnt * current_page; 177 } 178 } 179 180 roller->sel_opt_id = sel_opt < roller->option_cnt ? sel_opt : roller->option_cnt - 1; 181 roller->sel_opt_id_ori = roller->sel_opt_id; 182 183 refr_position(obj, anim); 184 } 185 186 /** 187 * Set the height to show the given number of rows (options) 188 * @param roller pointer to a roller object 189 * @param row_cnt number of desired visible rows 190 */ 191 void lv_roller_set_visible_row_count(lv_obj_t * obj, uint8_t row_cnt) 192 { 193 LV_ASSERT_OBJ(obj, MY_CLASS); 194 195 const lv_font_t * font = lv_obj_get_style_text_font(obj, LV_PART_MAIN); 196 lv_coord_t line_space = lv_obj_get_style_text_line_space(obj, LV_PART_MAIN); 197 lv_coord_t border_width = lv_obj_get_style_border_width(obj, LV_PART_MAIN); 198 lv_obj_set_height(obj, (lv_font_get_line_height(font) + line_space) * row_cnt + 2 * border_width); 199 } 200 201 /*===================== 202 * Getter functions 203 *====================*/ 204 205 /** 206 * Get the id of the selected option 207 * @param roller pointer to a roller object 208 * @return id of the selected option (0 ... number of option - 1); 209 */ 210 uint16_t lv_roller_get_selected(const lv_obj_t * obj) 211 { 212 LV_ASSERT_OBJ(obj, MY_CLASS); 213 214 lv_roller_t * roller = (lv_roller_t *)obj; 215 if(roller->mode == LV_ROLLER_MODE_INFINITE) { 216 uint16_t real_id_cnt = roller->option_cnt / LV_ROLLER_INF_PAGES; 217 return roller->sel_opt_id % real_id_cnt; 218 } 219 else { 220 return roller->sel_opt_id; 221 } 222 } 223 224 /** 225 * Get the current selected option as a string 226 * @param ddlist pointer to ddlist object 227 * @param buf pointer to an array to store the string 228 * @param buf_size size of `buf` in bytes. 0: to ignore it. 229 */ 230 void lv_roller_get_selected_str(const lv_obj_t * obj, char * buf, uint32_t buf_size) 231 { 232 LV_ASSERT_OBJ(obj, MY_CLASS); 233 234 lv_roller_t * roller = (lv_roller_t *)obj; 235 lv_obj_t * label = get_label(obj); 236 uint32_t i; 237 uint16_t line = 0; 238 const char * opt_txt = lv_label_get_text(label); 239 size_t txt_len = strlen(opt_txt); 240 241 for(i = 0; i < txt_len && line != roller->sel_opt_id; i++) { 242 if(opt_txt[i] == '\n') line++; 243 } 244 245 uint32_t c; 246 for(c = 0; i < txt_len && opt_txt[i] != '\n'; c++, i++) { 247 if(buf_size && c >= buf_size - 1) { 248 LV_LOG_WARN("lv_dropdown_get_selected_str: the buffer was too small"); 249 break; 250 } 251 buf[c] = opt_txt[i]; 252 } 253 254 buf[c] = '\0'; 255 } 256 257 258 /** 259 * Get the options of a roller 260 * @param roller pointer to roller object 261 * @return the options separated by '\n'-s (E.g. "Option1\nOption2\nOption3") 262 */ 263 const char * lv_roller_get_options(const lv_obj_t * obj) 264 { 265 LV_ASSERT_OBJ(obj, MY_CLASS); 266 267 return lv_label_get_text(get_label(obj)); 268 } 269 270 271 /** 272 * Get the total number of options 273 * @param roller pointer to a roller object 274 * @return the total number of options 275 */ 276 uint16_t lv_roller_get_option_cnt(const lv_obj_t * obj) 277 { 278 LV_ASSERT_OBJ(obj, MY_CLASS); 279 280 lv_roller_t * roller = (lv_roller_t *)obj; 281 if(roller->mode == LV_ROLLER_MODE_INFINITE) { 282 return roller->option_cnt / LV_ROLLER_INF_PAGES; 283 } 284 else { 285 return roller->option_cnt; 286 } 287 } 288 289 /********************** 290 * STATIC FUNCTIONS 291 **********************/ 292 293 294 static void lv_roller_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj) 295 { 296 LV_UNUSED(class_p); 297 lv_roller_t * roller = (lv_roller_t *)obj; 298 299 roller->mode = LV_ROLLER_MODE_NORMAL; 300 roller->option_cnt = 0; 301 roller->sel_opt_id = 0; 302 roller->sel_opt_id_ori = 0; 303 304 lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE); 305 lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLL_CHAIN_VER); 306 307 LV_LOG_INFO("begin"); 308 lv_obj_t * label = lv_obj_class_create_obj(&lv_roller_label_class, obj); 309 lv_obj_class_init_obj(label); 310 lv_roller_set_options(obj, "Option 1\nOption 2\nOption 3\nOption 4\nOption 5", LV_ROLLER_MODE_NORMAL); 311 312 LV_LOG_TRACE("finshed"); 313 } 314 315 static void lv_roller_event(const lv_obj_class_t * class_p, lv_event_t * e) 316 { 317 LV_UNUSED(class_p); 318 319 lv_res_t res; 320 321 /*Call the ancestor's event handler*/ 322 res = lv_obj_event_base(MY_CLASS, e); 323 if(res != LV_RES_OK) return; 324 325 lv_event_code_t code = lv_event_get_code(e); 326 lv_obj_t * obj = lv_event_get_target(e); 327 lv_roller_t * roller = (lv_roller_t *)obj; 328 329 if(code == LV_EVENT_GET_SELF_SIZE) { 330 lv_point_t * p = lv_event_get_param(e); 331 p->x = get_selected_label_width(obj); 332 } 333 else if(code == LV_EVENT_STYLE_CHANGED) { 334 lv_obj_t * label = get_label(obj); 335 /*Be sure the label's style is updated before processing the roller*/ 336 if(label) lv_event_send(label, LV_EVENT_STYLE_CHANGED, NULL); 337 lv_obj_refresh_self_size(obj); 338 refr_position(obj, LV_ANIM_OFF); 339 } 340 else if(code == LV_EVENT_SIZE_CHANGED) { 341 refr_position(obj, LV_ANIM_OFF); 342 } 343 else if(code == LV_EVENT_PRESSED) { 344 roller->moved = 0; 345 lv_anim_del(get_label(obj), set_y_anim); 346 } 347 else if(code == LV_EVENT_PRESSING) { 348 lv_indev_t * indev = lv_indev_get_act(); 349 lv_point_t p; 350 lv_indev_get_vect(indev, &p); 351 if(p.y) { 352 lv_obj_t * label = get_label(obj); 353 lv_obj_set_y(label, lv_obj_get_y(label) + p.y); 354 roller->moved = 1; 355 } 356 } 357 else if(code == LV_EVENT_RELEASED || code == LV_EVENT_PRESS_LOST) { 358 release_handler(obj); 359 } 360 else if(code == LV_EVENT_FOCUSED) { 361 lv_group_t * g = lv_obj_get_group(obj); 362 bool editing = lv_group_get_editing(g); 363 lv_indev_type_t indev_type = lv_indev_get_type(lv_indev_get_act()); 364 365 /*Encoders need special handling*/ 366 if(indev_type == LV_INDEV_TYPE_ENCODER) { 367 /*In navigate mode revert the original value*/ 368 if(!editing) { 369 if(roller->sel_opt_id != roller->sel_opt_id_ori) { 370 roller->sel_opt_id = roller->sel_opt_id_ori; 371 refr_position(obj, LV_ANIM_ON); 372 } 373 } 374 /*Save the current state when entered to edit mode*/ 375 else { 376 roller->sel_opt_id_ori = roller->sel_opt_id; 377 } 378 } 379 else { 380 roller->sel_opt_id_ori = roller->sel_opt_id; /*Save the current value. Used to revert this state if 381 ENTER won't be pressed*/ 382 } 383 } 384 else if(code == LV_EVENT_DEFOCUSED) { 385 /*Revert the original state*/ 386 if(roller->sel_opt_id != roller->sel_opt_id_ori) { 387 roller->sel_opt_id = roller->sel_opt_id_ori; 388 refr_position(obj, LV_ANIM_ON); 389 } 390 } 391 else if(code == LV_EVENT_KEY) { 392 char c = *((char *)lv_event_get_param(e)); 393 if(c == LV_KEY_RIGHT || c == LV_KEY_DOWN) { 394 if(roller->sel_opt_id + 1 < roller->option_cnt) { 395 uint16_t ori_id = roller->sel_opt_id_ori; /*lv_roller_set_selected will overwrite this*/ 396 lv_roller_set_selected(obj, roller->sel_opt_id + 1, LV_ANIM_ON); 397 roller->sel_opt_id_ori = ori_id; 398 } 399 } 400 else if(c == LV_KEY_LEFT || c == LV_KEY_UP) { 401 if(roller->sel_opt_id > 0) { 402 uint16_t ori_id = roller->sel_opt_id_ori; /*lv_roller_set_selected will overwrite this*/ 403 404 lv_roller_set_selected(obj, roller->sel_opt_id - 1, LV_ANIM_ON); 405 roller->sel_opt_id_ori = ori_id; 406 } 407 } 408 } 409 else if(code == LV_EVENT_REFR_EXT_DRAW_SIZE) { 410 lv_obj_t * label = get_label(obj); 411 lv_obj_refresh_ext_draw_size(label); 412 } 413 else if(code == LV_EVENT_DRAW_MAIN || code == LV_EVENT_DRAW_POST) { 414 draw_main(e); 415 } 416 } 417 418 static void lv_roller_label_event(const lv_obj_class_t * class_p, lv_event_t * e) 419 { 420 LV_UNUSED(class_p); 421 422 lv_res_t res; 423 424 lv_event_code_t code = lv_event_get_code(e); 425 /*LV_EVENT_DRAW_MAIN will be called in the draw function*/ 426 if(code != LV_EVENT_DRAW_MAIN) { 427 /* Call the ancestor's event handler */ 428 res = lv_obj_event_base(MY_CLASS_LABEL, e); 429 if(res != LV_RES_OK) return; 430 } 431 432 lv_obj_t * label = lv_event_get_target(e); 433 if(code == LV_EVENT_REFR_EXT_DRAW_SIZE) { 434 /*If the selected text has a larger font it needs some extra space to draw it*/ 435 lv_coord_t * s = lv_event_get_param(e); 436 lv_obj_t * obj = lv_obj_get_parent(label); 437 lv_coord_t sel_w = get_selected_label_width(obj); 438 lv_coord_t label_w = lv_obj_get_width(label); 439 *s = LV_MAX(*s, sel_w - label_w); 440 } 441 else if(code == LV_EVENT_SIZE_CHANGED) { 442 refr_position(lv_obj_get_parent(label), LV_ANIM_OFF); 443 } 444 else if(code == LV_EVENT_DRAW_MAIN) { 445 draw_label(e); 446 } 447 } 448 449 450 static void draw_main(lv_event_t * e) 451 { 452 lv_event_code_t code = lv_event_get_code(e); 453 lv_obj_t * obj = lv_event_get_target(e); 454 if(code == LV_EVENT_DRAW_MAIN) { 455 /*Draw the selected rectangle*/ 456 lv_draw_ctx_t * draw_ctx = lv_event_get_draw_ctx(e); 457 lv_area_t sel_area; 458 get_sel_area(obj, &sel_area); 459 lv_draw_rect_dsc_t sel_dsc; 460 lv_draw_rect_dsc_init(&sel_dsc); 461 lv_obj_init_draw_rect_dsc(obj, LV_PART_SELECTED, &sel_dsc); 462 lv_draw_rect(draw_ctx, &sel_dsc, &sel_area); 463 } 464 /*Post draw when the children are drawn*/ 465 else if(code == LV_EVENT_DRAW_POST) { 466 lv_draw_ctx_t * draw_ctx = lv_event_get_draw_ctx(e); 467 468 lv_draw_label_dsc_t label_dsc; 469 lv_draw_label_dsc_init(&label_dsc); 470 lv_obj_init_draw_label_dsc(obj, LV_PART_SELECTED, &label_dsc); 471 472 /*Redraw the text on the selected area*/ 473 lv_area_t sel_area; 474 get_sel_area(obj, &sel_area); 475 lv_area_t mask_sel; 476 bool area_ok; 477 area_ok = _lv_area_intersect(&mask_sel, draw_ctx->clip_area, &sel_area); 478 if(area_ok) { 479 lv_obj_t * label = get_label(obj); 480 481 /*Get the size of the "selected text"*/ 482 lv_point_t res_p; 483 lv_txt_get_size(&res_p, lv_label_get_text(label), label_dsc.font, label_dsc.letter_space, label_dsc.line_space, 484 lv_obj_get_width(obj), LV_TEXT_FLAG_EXPAND); 485 486 /*Move the selected label proportionally with the background label*/ 487 lv_coord_t roller_h = lv_obj_get_height(obj); 488 int32_t label_y_prop = label->coords.y1 - (roller_h / 2 + 489 obj->coords.y1); /*label offset from the middle line of the roller*/ 490 label_y_prop = (label_y_prop * 16384) / lv_obj_get_height( 491 label); /*Proportional position from the middle line (upscaled by << 14)*/ 492 493 /*Apply a correction with different line heights*/ 494 const lv_font_t * normal_label_font = lv_obj_get_style_text_font(obj, LV_PART_MAIN); 495 lv_coord_t corr = (label_dsc.font->line_height - normal_label_font->line_height) / 2; 496 497 /*Apply the proportional position to the selected text*/ 498 res_p.y -= corr; 499 int32_t label_sel_y = roller_h / 2 + obj->coords.y1; 500 label_sel_y += (label_y_prop * res_p.y) >> 14; 501 label_sel_y -= corr; 502 503 lv_coord_t bwidth = lv_obj_get_style_border_width(obj, LV_PART_MAIN); 504 lv_coord_t pleft = lv_obj_get_style_pad_left(obj, LV_PART_MAIN); 505 lv_coord_t pright = lv_obj_get_style_pad_right(obj, LV_PART_MAIN); 506 507 /*Draw the selected text*/ 508 lv_area_t label_sel_area; 509 label_sel_area.x1 = obj->coords.x1 + pleft + bwidth; 510 label_sel_area.y1 = label_sel_y; 511 label_sel_area.x2 = obj->coords.x2 - pright - bwidth; 512 label_sel_area.y2 = label_sel_area.y1 + res_p.y; 513 514 label_dsc.flag |= LV_TEXT_FLAG_EXPAND; 515 const lv_area_t * clip_area_ori = draw_ctx->clip_area; 516 draw_ctx->clip_area = &mask_sel; 517 lv_draw_label(draw_ctx, &label_dsc, &label_sel_area, lv_label_get_text(label), NULL); 518 draw_ctx->clip_area = clip_area_ori; 519 } 520 } 521 } 522 523 static void draw_label(lv_event_t * e) 524 { 525 /* Split the drawing of the label into an upper (above the selected area) 526 * and a lower (below the selected area)*/ 527 lv_obj_t * label_obj = lv_event_get_target(e); 528 lv_obj_t * roller = lv_obj_get_parent(label_obj); 529 lv_draw_label_dsc_t label_draw_dsc; 530 lv_draw_label_dsc_init(&label_draw_dsc); 531 lv_obj_init_draw_label_dsc(roller, LV_PART_MAIN, &label_draw_dsc); 532 lv_draw_ctx_t * draw_ctx = lv_event_get_draw_ctx(e); 533 534 /*If the roller has shadow or outline it has some ext. draw size 535 *therefore the label can overflow the roller's boundaries. 536 *To solve this limit the clip area to the "plain" roller.*/ 537 const lv_area_t * clip_area_ori = draw_ctx->clip_area; 538 lv_area_t roller_clip_area; 539 if(!_lv_area_intersect(&roller_clip_area, draw_ctx->clip_area, &roller->coords)) return; 540 draw_ctx->clip_area = &roller_clip_area; 541 542 lv_area_t sel_area; 543 get_sel_area(roller, &sel_area); 544 545 lv_area_t clip2; 546 clip2.x1 = label_obj->coords.x1; 547 clip2.y1 = label_obj->coords.y1; 548 clip2.x2 = label_obj->coords.x2; 549 clip2.y2 = sel_area.y1; 550 if(_lv_area_intersect(&clip2, draw_ctx->clip_area, &clip2)) { 551 const lv_area_t * clip_area_ori2 = draw_ctx->clip_area; 552 draw_ctx->clip_area = &clip2; 553 lv_draw_label(draw_ctx, &label_draw_dsc, &label_obj->coords, lv_label_get_text(label_obj), NULL); 554 draw_ctx->clip_area = clip_area_ori2; 555 } 556 557 clip2.x1 = label_obj->coords.x1; 558 clip2.y1 = sel_area.y2; 559 clip2.x2 = label_obj->coords.x2; 560 clip2.y2 = label_obj->coords.y2; 561 if(_lv_area_intersect(&clip2, draw_ctx->clip_area, &clip2)) { 562 const lv_area_t * clip_area_ori2 = draw_ctx->clip_area; 563 draw_ctx->clip_area = &clip2; 564 lv_draw_label(draw_ctx, &label_draw_dsc, &label_obj->coords, lv_label_get_text(label_obj), NULL); 565 draw_ctx->clip_area = clip_area_ori2; 566 } 567 568 draw_ctx->clip_area = clip_area_ori; 569 } 570 571 static void get_sel_area(lv_obj_t * obj, lv_area_t * sel_area) 572 { 573 574 const lv_font_t * font_main = lv_obj_get_style_text_font(obj, LV_PART_MAIN); 575 const lv_font_t * font_sel = lv_obj_get_style_text_font(obj, LV_PART_SELECTED); 576 lv_coord_t font_main_h = lv_font_get_line_height(font_main); 577 lv_coord_t font_sel_h = lv_font_get_line_height(font_sel); 578 lv_coord_t line_space = lv_obj_get_style_text_line_space(obj, LV_PART_MAIN); 579 lv_coord_t d = (font_sel_h + font_main_h) / 2 + line_space; 580 sel_area->y1 = obj->coords.y1 + lv_obj_get_height(obj) / 2 - d / 2; 581 sel_area->y2 = sel_area->y1 + d; 582 lv_area_t roller_coords; 583 lv_obj_get_coords(obj, &roller_coords); 584 585 sel_area->x1 = roller_coords.x1; 586 sel_area->x2 = roller_coords.x2; 587 588 } 589 590 /** 591 * Refresh the position of the roller. It uses the id stored in: roller->ddlist.selected_option_id 592 * @param roller pointer to a roller object 593 * @param anim_en LV_ANIM_ON: refresh with animation; LV_ANOM_OFF: without animation 594 */ 595 static void refr_position(lv_obj_t * obj, lv_anim_enable_t anim_en) 596 { 597 lv_obj_t * label = get_label(obj); 598 if(label == NULL) return; 599 600 lv_text_align_t align = lv_obj_calculate_style_text_align(label, LV_PART_MAIN, lv_label_get_text(label)); 601 602 switch(align) { 603 case LV_TEXT_ALIGN_CENTER: 604 lv_obj_set_x(label, (lv_obj_get_content_width(obj) - lv_obj_get_width(label)) / 2); 605 break; 606 case LV_TEXT_ALIGN_RIGHT: 607 lv_obj_set_x(label, lv_obj_get_content_width(obj) - lv_obj_get_width(label)); 608 break; 609 case LV_TEXT_ALIGN_LEFT: 610 lv_obj_set_x(label, 0); 611 break; 612 } 613 614 lv_roller_t * roller = (lv_roller_t *)obj; 615 const lv_font_t * font = lv_obj_get_style_text_font(obj, LV_PART_MAIN); 616 lv_coord_t line_space = lv_obj_get_style_text_line_space(obj, LV_PART_MAIN); 617 lv_coord_t font_h = lv_font_get_line_height(font); 618 lv_coord_t h = lv_obj_get_content_height(obj); 619 uint16_t anim_time = lv_obj_get_style_anim_time(obj, LV_PART_MAIN); 620 621 /*Normally the animation's `end_cb` sets correct position of the roller if infinite. 622 *But without animations do it manually*/ 623 if(anim_en == LV_ANIM_OFF || anim_time == 0) { 624 inf_normalize(obj); 625 } 626 627 int32_t id = roller->sel_opt_id; 628 lv_coord_t sel_y1 = id * (font_h + line_space); 629 lv_coord_t mid_y1 = h / 2 - font_h / 2; 630 631 lv_coord_t new_y = mid_y1 - sel_y1; 632 633 if(anim_en == LV_ANIM_OFF || anim_time == 0) { 634 lv_anim_del(label, set_y_anim); 635 lv_obj_set_y(label, new_y); 636 } 637 else { 638 lv_anim_t a; 639 lv_anim_init(&a); 640 lv_anim_set_var(&a, label); 641 lv_anim_set_exec_cb(&a, set_y_anim); 642 lv_anim_set_values(&a, lv_obj_get_y(label), new_y); 643 lv_anim_set_time(&a, anim_time); 644 lv_anim_set_ready_cb(&a, scroll_anim_ready_cb); 645 lv_anim_set_path_cb(&a, lv_anim_path_ease_out); 646 lv_anim_start(&a); 647 } 648 } 649 650 static lv_res_t release_handler(lv_obj_t * obj) 651 { 652 lv_obj_t * label = get_label(obj); 653 if(label == NULL) return LV_RES_OK; 654 655 lv_indev_t * indev = lv_indev_get_act(); 656 lv_roller_t * roller = (lv_roller_t *)obj; 657 658 /*Leave edit mode once a new option is selected*/ 659 lv_indev_type_t indev_type = lv_indev_get_type(indev); 660 if(indev_type == LV_INDEV_TYPE_ENCODER || indev_type == LV_INDEV_TYPE_KEYPAD) { 661 roller->sel_opt_id_ori = roller->sel_opt_id; 662 663 if(indev_type == LV_INDEV_TYPE_ENCODER) { 664 lv_group_t * g = lv_obj_get_group(obj); 665 if(lv_group_get_editing(g)) { 666 lv_group_set_editing(g, false); 667 } 668 } 669 } 670 671 if(lv_indev_get_type(indev) == LV_INDEV_TYPE_POINTER || lv_indev_get_type(indev) == LV_INDEV_TYPE_BUTTON) { 672 /*Search the clicked option (For KEYPAD and ENCODER the new value should be already set)*/ 673 int16_t new_opt = -1; 674 if(roller->moved == 0) { 675 new_opt = 0; 676 lv_point_t p; 677 lv_indev_get_point(indev, &p); 678 p.y -= label->coords.y1; 679 p.x -= label->coords.x1; 680 uint32_t letter_i; 681 letter_i = lv_label_get_letter_on(label, &p); 682 683 const char * txt = lv_label_get_text(label); 684 uint32_t i = 0; 685 uint32_t i_prev = 0; 686 687 uint32_t letter_cnt = 0; 688 for(letter_cnt = 0; letter_cnt < letter_i; letter_cnt++) { 689 uint32_t letter = _lv_txt_encoded_next(txt, &i); 690 /*Count he lines to reach the clicked letter. But ignore the last '\n' because it 691 * still belongs to the clicked line*/ 692 if(letter == '\n' && i_prev != letter_i) new_opt++; 693 i_prev = i; 694 } 695 } 696 else { 697 /*If dragged then align the list to have an element in the middle*/ 698 const lv_font_t * font = lv_obj_get_style_text_font(obj, LV_PART_MAIN); 699 lv_coord_t line_space = lv_obj_get_style_text_line_space(obj, LV_PART_MAIN); 700 lv_coord_t font_h = lv_font_get_line_height(font); 701 702 lv_coord_t label_unit = font_h + line_space; 703 lv_coord_t mid = obj->coords.y1 + (obj->coords.y2 - obj->coords.y1) / 2; 704 lv_coord_t label_y1 = label->coords.y1 + lv_indev_scroll_throw_predict(indev, LV_DIR_VER); 705 int32_t id = (mid - label_y1) / label_unit; 706 707 if(id < 0) id = 0; 708 if(id >= roller->option_cnt) id = roller->option_cnt - 1; 709 710 new_opt = id; 711 } 712 713 if(new_opt >= 0) { 714 lv_roller_set_selected(obj, new_opt, LV_ANIM_ON); 715 } 716 } 717 718 uint32_t id = roller->sel_opt_id; /*Just to use uint32_t in event data*/ 719 lv_res_t res = lv_event_send(obj, LV_EVENT_VALUE_CHANGED, &id); 720 return res; 721 } 722 723 /** 724 * Set the middle page for the roller if infinite is enabled 725 * @param roller pointer to a roller object 726 */ 727 static void inf_normalize(lv_obj_t * obj) 728 { 729 lv_roller_t * roller = (lv_roller_t *)obj; 730 731 if(roller->mode == LV_ROLLER_MODE_INFINITE) { 732 uint16_t real_id_cnt = roller->option_cnt / LV_ROLLER_INF_PAGES; 733 roller->sel_opt_id = roller->sel_opt_id % real_id_cnt; 734 roller->sel_opt_id += (LV_ROLLER_INF_PAGES / 2) * real_id_cnt; /*Select the middle page*/ 735 736 roller->sel_opt_id_ori = roller->sel_opt_id % real_id_cnt; 737 roller->sel_opt_id_ori += (LV_ROLLER_INF_PAGES / 2) * real_id_cnt; /*Select the middle page*/ 738 739 /*Move to the new id*/ 740 const lv_font_t * font = lv_obj_get_style_text_font(obj, LV_PART_MAIN); 741 lv_coord_t line_space = lv_obj_get_style_text_line_space(obj, LV_PART_MAIN); 742 lv_coord_t font_h = lv_font_get_line_height(font); 743 lv_coord_t h = lv_obj_get_content_height(obj); 744 745 lv_obj_t * label = get_label(obj); 746 747 748 lv_coord_t sel_y1 = roller->sel_opt_id * (font_h + line_space); 749 lv_coord_t mid_y1 = h / 2 - font_h / 2; 750 lv_coord_t new_y = mid_y1 - sel_y1; 751 lv_obj_set_y(label, new_y); 752 } 753 } 754 755 static lv_obj_t * get_label(const lv_obj_t * obj) 756 { 757 return lv_obj_get_child(obj, 0); 758 } 759 760 761 static lv_coord_t get_selected_label_width(const lv_obj_t * obj) 762 { 763 lv_obj_t * label = get_label(obj); 764 if(label == NULL) return 0; 765 766 const lv_font_t * font = lv_obj_get_style_text_font(obj, LV_PART_SELECTED); 767 lv_coord_t letter_space = lv_obj_get_style_text_letter_space(obj, LV_PART_SELECTED); 768 const char * txt = lv_label_get_text(label); 769 lv_point_t size; 770 lv_txt_get_size(&size, txt, font, letter_space, 0, LV_COORD_MAX, LV_TEXT_FLAG_NONE); 771 return size.x; 772 } 773 774 static void scroll_anim_ready_cb(lv_anim_t * a) 775 { 776 lv_obj_t * obj = lv_obj_get_parent(a->var); /*The label is animated*/ 777 inf_normalize(obj); 778 } 779 780 781 static void set_y_anim(void * obj, int32_t v) 782 { 783 lv_obj_set_y(obj, v); 784 } 785 786 #endif