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_span.c (34209B)
1 /** 2 * @file lv_span.c 3 * 4 */ 5 6 /********************* 7 * INCLUDES 8 *********************/ 9 #include "lv_span.h" 10 11 #if LV_USE_SPAN != 0 12 13 #include "../../../misc/lv_assert.h" 14 15 /********************* 16 * DEFINES 17 *********************/ 18 #define MY_CLASS &lv_spangroup_class 19 20 /********************** 21 * TYPEDEFS 22 **********************/ 23 typedef struct { 24 lv_span_t * span; 25 const char * txt; 26 const lv_font_t * font; 27 uint16_t bytes; 28 lv_coord_t txt_w; 29 lv_coord_t line_h; 30 lv_coord_t letter_space; 31 } lv_snippet_t; 32 33 struct _snippet_stack { 34 lv_snippet_t stack[LV_SPAN_SNIPPET_STACK_SIZE]; 35 uint16_t index; 36 }; 37 38 /********************** 39 * STATIC PROTOTYPES 40 **********************/ 41 static void lv_spangroup_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj); 42 static void lv_spangroup_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj); 43 static void lv_spangroup_event(const lv_obj_class_t * class_p, lv_event_t * e); 44 static void draw_main(lv_event_t * e); 45 static void refresh_self_size(lv_obj_t * obj); 46 47 static const lv_font_t * lv_span_get_style_text_font(lv_obj_t * par, lv_span_t * span); 48 static lv_coord_t lv_span_get_style_text_letter_space(lv_obj_t * par, lv_span_t * span); 49 static lv_color_t lv_span_get_style_text_color(lv_obj_t * par, lv_span_t * span); 50 static lv_opa_t lv_span_get_style_text_opa(lv_obj_t * par, lv_span_t * span); 51 static lv_opa_t lv_span_get_style_text_blend_mode(lv_obj_t * par, lv_span_t * span); 52 static int32_t lv_span_get_style_text_decor(lv_obj_t * par, lv_span_t * span); 53 54 static inline void span_text_check(const char ** text); 55 static void lv_draw_span(lv_obj_t * obj, lv_draw_ctx_t * draw_ctx); 56 static bool lv_txt_get_snippet(const char * txt, const lv_font_t * font, lv_coord_t letter_space, 57 lv_coord_t max_width, lv_text_flag_t flag, lv_coord_t * use_width, 58 uint32_t * end_ofs); 59 60 static void lv_snippet_clear(void); 61 static uint16_t lv_get_snippet_cnt(void); 62 static void lv_snippet_push(lv_snippet_t * item); 63 static lv_snippet_t * lv_get_snippet(uint16_t index); 64 static lv_coord_t convert_indent_pct(lv_obj_t * spans, lv_coord_t width); 65 66 /********************** 67 * STATIC VARIABLES 68 **********************/ 69 static struct _snippet_stack snippet_stack; 70 71 const lv_obj_class_t lv_spangroup_class = { 72 .base_class = &lv_obj_class, 73 .constructor_cb = lv_spangroup_constructor, 74 .destructor_cb = lv_spangroup_destructor, 75 .event_cb = lv_spangroup_event, 76 .instance_size = sizeof(lv_spangroup_t), 77 .width_def = LV_SIZE_CONTENT, 78 .height_def = LV_SIZE_CONTENT, 79 }; 80 81 /********************** 82 * MACROS 83 **********************/ 84 85 /********************** 86 * GLOBAL FUNCTIONS 87 **********************/ 88 89 lv_obj_t * lv_spangroup_create(lv_obj_t * par) 90 { 91 lv_obj_t * obj = lv_obj_class_create_obj(&lv_spangroup_class, par); 92 lv_obj_class_init_obj(obj); 93 return obj; 94 } 95 96 lv_span_t * lv_spangroup_new_span(lv_obj_t * obj) 97 { 98 if(obj == NULL) { 99 return NULL; 100 } 101 102 LV_ASSERT_OBJ(obj, MY_CLASS); 103 lv_spangroup_t * spans = (lv_spangroup_t *)obj; 104 lv_span_t * span = _lv_ll_ins_tail(&spans->child_ll); 105 LV_ASSERT_MALLOC(span); 106 107 lv_style_init(&span->style); 108 span->txt = (char *)""; 109 span->static_flag = 1; 110 span->spangroup = obj; 111 112 refresh_self_size(obj); 113 114 return span; 115 } 116 117 void lv_spangroup_del_span(lv_obj_t * obj, lv_span_t * span) 118 { 119 if(obj == NULL || span == NULL) { 120 return; 121 } 122 123 LV_ASSERT_OBJ(obj, MY_CLASS); 124 lv_spangroup_t * spans = (lv_spangroup_t *)obj; 125 lv_span_t * cur_span; 126 _LV_LL_READ(&spans->child_ll, cur_span) { 127 if(cur_span == span) { 128 _lv_ll_remove(&spans->child_ll, cur_span); 129 if(cur_span->txt && cur_span->static_flag == 0) { 130 lv_mem_free(cur_span->txt); 131 } 132 lv_style_reset(&cur_span->style); 133 lv_mem_free(cur_span); 134 break; 135 } 136 } 137 138 refresh_self_size(obj); 139 } 140 141 /*===================== 142 * Setter functions 143 *====================*/ 144 145 void lv_span_set_text(lv_span_t * span, const char * text) 146 { 147 if(span == NULL || text == NULL) { 148 return; 149 } 150 151 if(span->txt == NULL || span->static_flag == 1) { 152 span->txt = lv_mem_alloc(strlen(text) + 1); 153 } 154 else { 155 span->txt = lv_mem_realloc(span->txt, strlen(text) + 1); 156 } 157 span->static_flag = 0; 158 strcpy(span->txt, text); 159 160 refresh_self_size(span->spangroup); 161 } 162 163 void lv_span_set_text_static(lv_span_t * span, const char * text) 164 { 165 if(span == NULL || text == NULL) { 166 return; 167 } 168 169 if(span->txt && span->static_flag == 0) { 170 lv_mem_free(span->txt); 171 } 172 span->static_flag = 1; 173 span->txt = (char *)text; 174 175 refresh_self_size(span->spangroup); 176 } 177 178 void lv_spangroup_set_align(lv_obj_t * obj, lv_text_align_t align) 179 { 180 lv_obj_set_style_text_align(obj, align, LV_PART_MAIN); 181 } 182 183 void lv_spangroup_set_overflow(lv_obj_t * obj, lv_span_overflow_t overflow) 184 { 185 LV_ASSERT_OBJ(obj, MY_CLASS); 186 lv_spangroup_t * spans = (lv_spangroup_t *)obj; 187 if(spans->overflow == overflow) return; 188 189 spans->overflow = overflow; 190 lv_obj_invalidate(obj); 191 } 192 193 void lv_spangroup_set_indent(lv_obj_t * obj, lv_coord_t indent) 194 { 195 LV_ASSERT_OBJ(obj, MY_CLASS); 196 lv_spangroup_t * spans = (lv_spangroup_t *)obj; 197 if(spans->indent == indent) return; 198 199 spans->indent = indent; 200 201 refresh_self_size(obj); 202 } 203 204 void lv_spangroup_set_mode(lv_obj_t * obj, lv_span_mode_t mode) 205 { 206 LV_ASSERT_OBJ(obj, MY_CLASS); 207 lv_spangroup_t * spans = (lv_spangroup_t *)obj; 208 spans->mode = mode; 209 lv_spangroup_refr_mode(obj); 210 } 211 212 void lv_spangroup_set_lines(lv_obj_t * obj, int32_t lines) 213 { 214 LV_ASSERT_OBJ(obj, MY_CLASS); 215 lv_spangroup_t * spans = (lv_spangroup_t *)obj; 216 spans->lines = lines; 217 lv_spangroup_refr_mode(obj); 218 } 219 220 /*===================== 221 * Getter functions 222 *====================*/ 223 224 lv_span_t * lv_spangroup_get_child(const lv_obj_t * obj, int32_t id) 225 { 226 if(obj == NULL) { 227 return NULL; 228 } 229 230 LV_ASSERT_OBJ(obj, MY_CLASS); 231 lv_spangroup_t * spans = (lv_spangroup_t *)obj; 232 lv_ll_t * linked_list = &spans->child_ll; 233 234 bool traverse_forwards = (id >= 0); 235 int32_t cur_idx = 0; 236 lv_ll_node_t * cur_node = linked_list->head; 237 238 /*If using a negative index, start from the tail and use cur -1 to indicate the end*/ 239 if(!traverse_forwards) { 240 cur_idx = -1; 241 cur_node = linked_list->tail; 242 } 243 244 while(cur_node != NULL) { 245 if(cur_idx == id) { 246 return (lv_span_t *) cur_node; 247 } 248 if(traverse_forwards) { 249 cur_node = (lv_ll_node_t *) _lv_ll_get_next(linked_list, cur_node); 250 cur_idx++; 251 } 252 else { 253 cur_node = (lv_ll_node_t *) _lv_ll_get_prev(linked_list, cur_node); 254 cur_idx--; 255 } 256 } 257 258 return NULL; 259 } 260 261 uint32_t lv_spangroup_get_child_cnt(const lv_obj_t * obj) 262 { 263 LV_ASSERT_OBJ(obj, MY_CLASS); 264 265 if(obj == NULL) { 266 return 0; 267 } 268 269 LV_ASSERT_OBJ(obj, MY_CLASS); 270 lv_spangroup_t * spans = (lv_spangroup_t *)obj; 271 return _lv_ll_get_len(&(spans->child_ll)); 272 } 273 274 lv_text_align_t lv_spangroup_get_align(lv_obj_t * obj) 275 { 276 return lv_obj_get_style_text_align(obj, LV_PART_MAIN); 277 } 278 279 lv_span_overflow_t lv_spangroup_get_overflow(lv_obj_t * obj) 280 { 281 LV_ASSERT_OBJ(obj, MY_CLASS); 282 lv_spangroup_t * spans = (lv_spangroup_t *)obj; 283 return spans->overflow; 284 } 285 286 lv_coord_t lv_spangroup_get_indent(lv_obj_t * obj) 287 { 288 LV_ASSERT_OBJ(obj, MY_CLASS); 289 lv_spangroup_t * spans = (lv_spangroup_t *)obj; 290 return spans->indent; 291 } 292 293 lv_span_mode_t lv_spangroup_get_mode(lv_obj_t * obj) 294 { 295 LV_ASSERT_OBJ(obj, MY_CLASS); 296 lv_spangroup_t * spans = (lv_spangroup_t *)obj; 297 return spans->mode; 298 } 299 300 int32_t lv_spangroup_get_lines(lv_obj_t * obj) 301 { 302 LV_ASSERT_OBJ(obj, MY_CLASS); 303 lv_spangroup_t * spans = (lv_spangroup_t *)obj; 304 return spans->lines; 305 } 306 307 void lv_spangroup_refr_mode(lv_obj_t * obj) 308 { 309 LV_ASSERT_OBJ(obj, MY_CLASS); 310 lv_spangroup_t * spans = (lv_spangroup_t *)obj; 311 312 if(spans->mode == LV_SPAN_MODE_EXPAND) { 313 lv_obj_set_width(obj, LV_SIZE_CONTENT); 314 lv_obj_set_height(obj, LV_SIZE_CONTENT); 315 } 316 else if(spans->mode == LV_SPAN_MODE_BREAK) { 317 if(lv_obj_get_style_width(obj, LV_PART_MAIN) == LV_SIZE_CONTENT) { 318 lv_obj_set_width(obj, 100); 319 } 320 lv_obj_set_height(obj, LV_SIZE_CONTENT); 321 } 322 else if(spans->mode == LV_SPAN_MODE_FIXED) { 323 /* use this mode, The user needs to set the size. */ 324 /* This is just to prevent an infinite loop. */ 325 if(lv_obj_get_style_width(obj, LV_PART_MAIN) == LV_SIZE_CONTENT) { 326 lv_obj_set_width(obj, 100); 327 } 328 if(lv_obj_get_style_height(obj, LV_PART_MAIN) == LV_SIZE_CONTENT) { 329 lv_coord_t width = lv_obj_get_style_width(obj, LV_PART_MAIN); 330 if(LV_COORD_IS_PCT(width)) { 331 width = 100; 332 } 333 lv_coord_t height = lv_spangroup_get_expand_height(obj, width); 334 lv_obj_set_content_height(obj, height); 335 } 336 } 337 338 refresh_self_size(obj); 339 } 340 341 lv_coord_t lv_spangroup_get_max_line_h(lv_obj_t * obj) 342 { 343 LV_ASSERT_OBJ(obj, MY_CLASS); 344 lv_spangroup_t * spans = (lv_spangroup_t *)obj; 345 346 lv_coord_t max_line_h = 0; 347 lv_span_t * cur_span; 348 _LV_LL_READ(&spans->child_ll, cur_span) { 349 const lv_font_t * font = lv_span_get_style_text_font(obj, cur_span); 350 lv_coord_t line_h = lv_font_get_line_height(font); 351 if(line_h > max_line_h) { 352 max_line_h = line_h; 353 } 354 } 355 356 return max_line_h; 357 } 358 359 uint32_t lv_spangroup_get_expand_width(lv_obj_t * obj, uint32_t max_width) 360 { 361 LV_ASSERT_OBJ(obj, MY_CLASS); 362 lv_spangroup_t * spans = (lv_spangroup_t *)obj; 363 364 if(_lv_ll_get_head(&spans->child_ll) == NULL) { 365 return 0; 366 } 367 368 uint32_t width = LV_COORD_IS_PCT(spans->indent) ? 0 : spans->indent; 369 lv_span_t * cur_span; 370 lv_coord_t letter_space = 0; 371 _LV_LL_READ(&spans->child_ll, cur_span) { 372 const lv_font_t * font = lv_span_get_style_text_font(obj, cur_span); 373 letter_space = lv_span_get_style_text_letter_space(obj, cur_span); 374 uint32_t j = 0; 375 const char * cur_txt = cur_span->txt; 376 span_text_check(&cur_txt); 377 while(cur_txt[j] != '\0') { 378 if(max_width > 0 && width >= max_width) { 379 return max_width; 380 } 381 uint32_t letter = _lv_txt_encoded_next(cur_txt, &j); 382 uint32_t letter_next = _lv_txt_encoded_next(&cur_txt[j], NULL); 383 uint16_t letter_w = lv_font_get_glyph_width(font, letter, letter_next); 384 width = width + letter_w + letter_space; 385 } 386 } 387 388 return width - letter_space; 389 } 390 391 lv_coord_t lv_spangroup_get_expand_height(lv_obj_t * obj, lv_coord_t width) 392 { 393 LV_ASSERT_OBJ(obj, MY_CLASS); 394 lv_spangroup_t * spans = (lv_spangroup_t *)obj; 395 if(_lv_ll_get_head(&spans->child_ll) == NULL || width <= 0) { 396 return 0; 397 } 398 399 /* init draw variable */ 400 lv_text_flag_t txt_flag = LV_TEXT_FLAG_NONE; 401 lv_coord_t line_space = lv_obj_get_style_text_line_space(obj, LV_PART_MAIN); 402 lv_coord_t max_width = width; 403 lv_coord_t indent = convert_indent_pct(obj, max_width); 404 lv_coord_t max_w = max_width - indent; /* first line need minus indent */ 405 406 /* coords of draw span-txt */ 407 lv_point_t txt_pos; 408 txt_pos.y = 0; 409 txt_pos.x = 0 + indent; /* first line need add indent */ 410 411 lv_span_t * cur_span = _lv_ll_get_head(&spans->child_ll); 412 const char * cur_txt = cur_span->txt; 413 span_text_check(&cur_txt); 414 uint32_t cur_txt_ofs = 0; 415 lv_snippet_t snippet; /* use to save cur_span info and push it to stack */ 416 memset(&snippet, 0, sizeof(snippet)); 417 418 int32_t line_cnt = 0; 419 int32_t lines = spans->lines < 0 ? INT32_MAX : spans->lines; 420 /* the loop control how many lines need to draw */ 421 while(cur_span) { 422 int snippet_cnt = 0; 423 lv_coord_t max_line_h = 0; /* the max height of span-font when a line have a lot of span */ 424 425 /* the loop control to find a line and push the relevant span info into stack */ 426 while(1) { 427 /* switch to the next span when current is end */ 428 if(cur_txt[cur_txt_ofs] == '\0') { 429 cur_span = _lv_ll_get_next(&spans->child_ll, cur_span); 430 if(cur_span == NULL) break; 431 cur_txt = cur_span->txt; 432 span_text_check(&cur_txt); 433 cur_txt_ofs = 0; 434 /* maybe also cur_txt[cur_txt_ofs] == '\0' */ 435 continue; 436 } 437 438 /* init span info to snippet. */ 439 if(cur_txt_ofs == 0) { 440 snippet.span = cur_span; 441 snippet.font = lv_span_get_style_text_font(obj, cur_span); 442 snippet.letter_space = lv_span_get_style_text_letter_space(obj, cur_span); 443 snippet.line_h = lv_font_get_line_height(snippet.font) + line_space; 444 } 445 446 /* get current span text line info */ 447 uint32_t next_ofs = 0; 448 lv_coord_t use_width = 0; 449 bool isfill = lv_txt_get_snippet(&cur_txt[cur_txt_ofs], snippet.font, snippet.letter_space, 450 max_w, txt_flag, &use_width, &next_ofs); 451 452 /* break word deal width */ 453 if(isfill && next_ofs > 0 && snippet_cnt > 0) { 454 if(max_w < use_width) { 455 break; 456 } 457 458 uint32_t tmp_ofs = next_ofs; 459 uint32_t letter = _lv_txt_encoded_prev(&cur_txt[cur_txt_ofs], &tmp_ofs); 460 if(!(letter == '\0' || letter == '\n' || letter == '\r' || _lv_txt_is_break_char(letter))) { 461 tmp_ofs = 0; 462 letter = _lv_txt_encoded_next(&cur_txt[cur_txt_ofs + next_ofs], &tmp_ofs); 463 if(!(letter == '\0' || letter == '\n' || letter == '\r' || _lv_txt_is_break_char(letter))) { 464 break; 465 } 466 } 467 } 468 469 snippet.txt = &cur_txt[cur_txt_ofs]; 470 snippet.bytes = next_ofs; 471 snippet.txt_w = use_width; 472 cur_txt_ofs += next_ofs; 473 if(max_line_h < snippet.line_h) { 474 max_line_h = snippet.line_h; 475 } 476 snippet_cnt ++; 477 max_w = max_w - use_width - snippet.letter_space; 478 if(isfill || max_w <= 0) { 479 break; 480 } 481 } 482 483 /* next line init */ 484 txt_pos.x = 0; 485 txt_pos.y += max_line_h; 486 max_w = max_width; 487 line_cnt += 1; 488 if(line_cnt >= lines) { 489 break; 490 } 491 } 492 txt_pos.y -= line_space; 493 494 return txt_pos.y; 495 } 496 497 /********************** 498 * STATIC FUNCTIONS 499 **********************/ 500 501 static void lv_spangroup_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj) 502 { 503 LV_UNUSED(class_p); 504 lv_spangroup_t * spans = (lv_spangroup_t *)obj; 505 _lv_ll_init(&spans->child_ll, sizeof(lv_span_t)); 506 spans->indent = 0; 507 spans->lines = -1; 508 spans->mode = LV_SPAN_MODE_EXPAND; 509 spans->overflow = LV_SPAN_OVERFLOW_CLIP; 510 spans->cache_w = 0; 511 spans->cache_h = 0; 512 spans->refresh = 1; 513 } 514 515 static void lv_spangroup_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj) 516 { 517 LV_UNUSED(class_p); 518 lv_spangroup_t * spans = (lv_spangroup_t *)obj; 519 lv_span_t * cur_span = _lv_ll_get_head(&spans->child_ll); 520 while(cur_span) { 521 _lv_ll_remove(&spans->child_ll, cur_span); 522 if(cur_span->txt && cur_span->static_flag == 0) { 523 lv_mem_free(cur_span->txt); 524 } 525 lv_style_reset(&cur_span->style); 526 lv_mem_free(cur_span); 527 cur_span = _lv_ll_get_head(&spans->child_ll); 528 } 529 } 530 531 static void lv_spangroup_event(const lv_obj_class_t * class_p, lv_event_t * e) 532 { 533 LV_UNUSED(class_p); 534 535 /* Call the ancestor's event handler */ 536 if(lv_obj_event_base(MY_CLASS, e) != LV_RES_OK) return; 537 538 lv_event_code_t code = lv_event_get_code(e); 539 lv_obj_t * obj = lv_event_get_target(e); 540 lv_spangroup_t * spans = (lv_spangroup_t *)obj; 541 542 if(code == LV_EVENT_DRAW_MAIN) { 543 draw_main(e); 544 } 545 else if(code == LV_EVENT_STYLE_CHANGED) { 546 refresh_self_size(obj); 547 } 548 else if(code == LV_EVENT_SIZE_CHANGED) { 549 refresh_self_size(obj); 550 } 551 else if(code == LV_EVENT_GET_SELF_SIZE) { 552 lv_coord_t width = 0; 553 lv_coord_t height = 0; 554 lv_point_t * self_size = lv_event_get_param(e); 555 556 if(spans->mode == LV_SPAN_MODE_EXPAND) { 557 if(spans->refresh) { 558 spans->cache_w = (lv_coord_t)lv_spangroup_get_expand_width(obj, 0); 559 spans->cache_h = lv_spangroup_get_max_line_h(obj); 560 spans->refresh = 0; 561 } 562 width = spans->cache_w; 563 height = spans->cache_h; 564 } 565 else if(spans->mode == LV_SPAN_MODE_BREAK) { 566 width = lv_obj_get_content_width(obj); 567 if(self_size->y >= 0) { 568 if(width != spans->cache_w || spans->refresh) { 569 height = lv_spangroup_get_expand_height(obj, width); 570 spans->cache_w = width; 571 spans->cache_h = height; 572 spans->refresh = 0; 573 } 574 else { 575 height = spans->cache_h; 576 } 577 } 578 } 579 else if(spans->mode == LV_SPAN_MODE_FIXED) { 580 width = self_size->x >= 0 ? lv_obj_get_content_width(obj) : 0; 581 height = self_size->y >= 0 ? lv_obj_get_content_height(obj) : 0; 582 } 583 self_size->x = LV_MAX(self_size->x, width); 584 self_size->y = LV_MAX(self_size->y, height); 585 } 586 } 587 588 static void draw_main(lv_event_t * e) 589 { 590 lv_obj_t * obj = lv_event_get_target(e); 591 lv_draw_ctx_t * draw_ctx = lv_event_get_draw_ctx(e); 592 593 lv_draw_span(obj, draw_ctx); 594 } 595 596 /** 597 * @return true for txt fill the max_width. 598 */ 599 static bool lv_txt_get_snippet(const char * txt, const lv_font_t * font, 600 lv_coord_t letter_space, lv_coord_t max_width, lv_text_flag_t flag, 601 lv_coord_t * use_width, uint32_t * end_ofs) 602 { 603 if(txt == NULL || txt[0] == '\0') { 604 *end_ofs = 0; 605 *use_width = 0; 606 return false; 607 } 608 609 uint32_t ofs = _lv_txt_get_next_line(txt, font, letter_space, max_width, use_width, flag); 610 *end_ofs = ofs; 611 612 if(txt[ofs] == '\0' && *use_width < max_width) { 613 return false; 614 } 615 else { 616 return true; 617 } 618 } 619 620 static void lv_snippet_push(lv_snippet_t * item) 621 { 622 if(snippet_stack.index < LV_SPAN_SNIPPET_STACK_SIZE) { 623 memcpy(&snippet_stack.stack[snippet_stack.index], item, sizeof(lv_snippet_t)); 624 snippet_stack.index++; 625 } 626 else { 627 LV_LOG_ERROR("span draw stack overflow, please set LV_SPAN_SNIPPET_STACK_SIZE too larger"); 628 } 629 } 630 631 static uint16_t lv_get_snippet_cnt(void) 632 { 633 return snippet_stack.index; 634 } 635 636 static lv_snippet_t * lv_get_snippet(uint16_t index) 637 { 638 return &snippet_stack.stack[index]; 639 } 640 641 static void lv_snippet_clear(void) 642 { 643 snippet_stack.index = 0; 644 } 645 646 static const lv_font_t * lv_span_get_style_text_font(lv_obj_t * par, lv_span_t * span) 647 { 648 const lv_font_t * font; 649 lv_style_value_t value; 650 lv_res_t res = lv_style_get_prop(&span->style, LV_STYLE_TEXT_FONT, &value); 651 if(res != LV_RES_OK) { 652 font = lv_obj_get_style_text_font(par, LV_PART_MAIN); 653 } 654 else { 655 font = (const lv_font_t *)value.ptr; 656 } 657 return font; 658 } 659 660 static lv_coord_t lv_span_get_style_text_letter_space(lv_obj_t * par, lv_span_t * span) 661 { 662 lv_coord_t letter_space; 663 lv_style_value_t value; 664 lv_res_t res = lv_style_get_prop(&span->style, LV_STYLE_TEXT_LETTER_SPACE, &value); 665 if(res != LV_RES_OK) { 666 letter_space = lv_obj_get_style_text_letter_space(par, LV_PART_MAIN); 667 } 668 else { 669 letter_space = (lv_coord_t)value.num; 670 } 671 return letter_space; 672 } 673 674 static lv_color_t lv_span_get_style_text_color(lv_obj_t * par, lv_span_t * span) 675 { 676 lv_style_value_t value; 677 lv_res_t res = lv_style_get_prop(&span->style, LV_STYLE_TEXT_COLOR, &value); 678 if(res != LV_RES_OK) { 679 value.color = lv_obj_get_style_text_color(par, LV_PART_MAIN); 680 } 681 return value.color; 682 } 683 684 static lv_opa_t lv_span_get_style_text_opa(lv_obj_t * par, lv_span_t * span) 685 { 686 lv_opa_t opa; 687 lv_style_value_t value; 688 lv_res_t res = lv_style_get_prop(&span->style, LV_STYLE_TEXT_OPA, &value); 689 if(res != LV_RES_OK) { 690 opa = (lv_opa_t)lv_obj_get_style_text_opa(par, LV_PART_MAIN); 691 } 692 else { 693 opa = (lv_opa_t)value.num; 694 } 695 return opa; 696 } 697 698 static lv_blend_mode_t lv_span_get_style_text_blend_mode(lv_obj_t * par, lv_span_t * span) 699 { 700 lv_blend_mode_t mode; 701 lv_style_value_t value; 702 lv_res_t res = lv_style_get_prop(&span->style, LV_STYLE_BLEND_MODE, &value); 703 if(res != LV_RES_OK) { 704 mode = (lv_blend_mode_t)lv_obj_get_style_blend_mode(par, LV_PART_MAIN); 705 } 706 else { 707 mode = (lv_blend_mode_t)value.num; 708 } 709 return mode; 710 } 711 712 static int32_t lv_span_get_style_text_decor(lv_obj_t * par, lv_span_t * span) 713 { 714 int32_t decor; 715 lv_style_value_t value; 716 lv_res_t res = lv_style_get_prop(&span->style, LV_STYLE_TEXT_DECOR, &value); 717 if(res != LV_RES_OK) { 718 decor = (lv_text_decor_t)lv_obj_get_style_text_decor(par, LV_PART_MAIN);; 719 } 720 else { 721 decor = (int32_t)value.num; 722 } 723 return decor; 724 } 725 726 static inline void span_text_check(const char ** text) 727 { 728 if(*text == NULL) { 729 *text = ""; 730 LV_LOG_ERROR("occur an error that span text == NULL"); 731 } 732 } 733 734 static lv_coord_t convert_indent_pct(lv_obj_t * obj, lv_coord_t width) 735 { 736 lv_spangroup_t * spans = (lv_spangroup_t *)obj; 737 738 lv_coord_t indent = spans->indent; 739 if(LV_COORD_IS_PCT(spans->indent)) { 740 if(spans->mode == LV_SPAN_MODE_EXPAND) { 741 indent = 0; 742 } 743 else { 744 indent = (width * LV_COORD_GET_PCT(spans->indent)) / 100; 745 } 746 } 747 748 return indent; 749 } 750 751 /** 752 * draw span group 753 * @param spans obj handle 754 * @param coords coordinates of the label 755 * @param mask the label will be drawn only in this area 756 */ 757 static void lv_draw_span(lv_obj_t * obj, lv_draw_ctx_t * draw_ctx) 758 { 759 760 lv_area_t coords; 761 lv_obj_get_content_coords(obj, &coords); 762 763 lv_spangroup_t * spans = (lv_spangroup_t *)obj; 764 765 /* return if not span */ 766 if(_lv_ll_get_head(&spans->child_ll) == NULL) { 767 return; 768 } 769 770 /* return if no draw area */ 771 lv_area_t clip_area; 772 if(!_lv_area_intersect(&clip_area, &coords, draw_ctx->clip_area)) return; 773 const lv_area_t * clip_area_ori = draw_ctx->clip_area; 774 draw_ctx->clip_area = &clip_area; 775 776 /* init draw variable */ 777 lv_text_flag_t txt_flag = LV_TEXT_FLAG_NONE; 778 lv_coord_t line_space = lv_obj_get_style_text_line_space(obj, LV_PART_MAIN);; 779 lv_coord_t max_width = lv_area_get_width(&coords); 780 lv_coord_t indent = convert_indent_pct(obj, max_width); 781 lv_coord_t max_w = max_width - indent; /* first line need minus indent */ 782 lv_opa_t obj_opa = lv_obj_get_style_opa(obj, LV_PART_MAIN); 783 784 /* coords of draw span-txt */ 785 lv_point_t txt_pos; 786 txt_pos.y = coords.y1; 787 txt_pos.x = coords.x1 + indent; /* first line need add indent */ 788 789 lv_span_t * cur_span = _lv_ll_get_head(&spans->child_ll); 790 const char * cur_txt = cur_span->txt; 791 span_text_check(&cur_txt); 792 uint32_t cur_txt_ofs = 0; 793 lv_snippet_t snippet; /* use to save cur_span info and push it to stack */ 794 lv_memset_00(&snippet, sizeof(snippet)); 795 796 lv_draw_label_dsc_t label_draw_dsc; 797 lv_draw_label_dsc_init(&label_draw_dsc); 798 799 bool is_first_line = true; 800 /* the loop control how many lines need to draw */ 801 while(cur_span) { 802 bool is_end_line = false; 803 bool ellipsis_valid = false; 804 lv_coord_t max_line_h = 0; /* the max height of span-font when a line have a lot of span */ 805 lv_coord_t max_baseline = 0; /*baseline of the highest span*/ 806 lv_snippet_clear(); 807 808 /* the loop control to find a line and push the relevant span info into stack */ 809 while(1) { 810 /* switch to the next span when current is end */ 811 if(cur_txt[cur_txt_ofs] == '\0') { 812 cur_span = _lv_ll_get_next(&spans->child_ll, cur_span); 813 if(cur_span == NULL) break; 814 cur_txt = cur_span->txt; 815 span_text_check(&cur_txt); 816 cur_txt_ofs = 0; 817 /* maybe also cur_txt[cur_txt_ofs] == '\0' */ 818 continue; 819 } 820 821 /* init span info to snippet. */ 822 if(cur_txt_ofs == 0) { 823 snippet.span = cur_span; 824 snippet.font = lv_span_get_style_text_font(obj, cur_span); 825 snippet.letter_space = lv_span_get_style_text_letter_space(obj, cur_span); 826 snippet.line_h = lv_font_get_line_height(snippet.font) + line_space; 827 } 828 829 /* get current span text line info */ 830 uint32_t next_ofs = 0; 831 lv_coord_t use_width = 0; 832 bool isfill = lv_txt_get_snippet(&cur_txt[cur_txt_ofs], snippet.font, snippet.letter_space, 833 max_w, txt_flag, &use_width, &next_ofs); 834 835 if(isfill) { 836 if(next_ofs > 0 && lv_get_snippet_cnt() > 0) { 837 /* To prevent infinite loops, the _lv_txt_get_next_line() may return incomplete words, */ 838 /* This phenomenon should be avoided when lv_get_snippet_cnt() > 0 */ 839 if(max_w < use_width) { 840 break; 841 } 842 uint32_t tmp_ofs = next_ofs; 843 uint32_t letter = _lv_txt_encoded_prev(&cur_txt[cur_txt_ofs], &tmp_ofs); 844 if(!(letter == '\0' || letter == '\n' || letter == '\r' || _lv_txt_is_break_char(letter))) { 845 tmp_ofs = 0; 846 letter = _lv_txt_encoded_next(&cur_txt[cur_txt_ofs + next_ofs], &tmp_ofs); 847 if(!(letter == '\0' || letter == '\n' || letter == '\r' || _lv_txt_is_break_char(letter))) { 848 break; 849 } 850 } 851 } 852 } 853 854 snippet.txt = &cur_txt[cur_txt_ofs]; 855 snippet.bytes = next_ofs; 856 snippet.txt_w = use_width; 857 cur_txt_ofs += next_ofs; 858 if(max_line_h < snippet.line_h) { 859 max_line_h = snippet.line_h; 860 max_baseline = snippet.font->base_line; 861 } 862 863 lv_snippet_push(&snippet); 864 max_w = max_w - use_width - snippet.letter_space; 865 if(isfill || max_w <= 0) { 866 break; 867 } 868 } 869 870 /* start current line deal with */ 871 872 uint16_t item_cnt = lv_get_snippet_cnt(); 873 if(item_cnt == 0) { /* break if stack is empty */ 874 break; 875 } 876 877 /* Whether the current line is the end line and does overflow processing */ 878 { 879 lv_snippet_t * last_snippet = lv_get_snippet(item_cnt - 1); 880 lv_coord_t next_line_h = last_snippet->line_h; 881 if(last_snippet->txt[last_snippet->bytes] == '\0') { 882 next_line_h = 0; 883 lv_span_t * next_span = _lv_ll_get_next(&spans->child_ll, last_snippet->span); 884 if(next_span) { /* have the next line */ 885 next_line_h = lv_font_get_line_height(lv_span_get_style_text_font(obj, next_span)) + line_space; 886 } 887 } 888 if(txt_pos.y + max_line_h + next_line_h - line_space > coords.y2 + 1) { /* for overflow if is end line. */ 889 if(last_snippet->txt[last_snippet->bytes] != '\0') { 890 last_snippet->bytes = strlen(last_snippet->txt); 891 last_snippet->txt_w = lv_txt_get_width(last_snippet->txt, last_snippet->bytes, last_snippet->font, 892 last_snippet->letter_space, txt_flag); 893 } 894 ellipsis_valid = spans->overflow == LV_SPAN_OVERFLOW_ELLIPSIS ? true : false; 895 is_end_line = true; 896 } 897 } 898 899 /*Go the first visible line*/ 900 if(txt_pos.y + max_line_h < clip_area.y1) { 901 goto Next_line_init; 902 } 903 904 /* align deal with */ 905 lv_text_align_t align = lv_obj_get_style_text_align(obj, LV_PART_MAIN); 906 if(align == LV_TEXT_ALIGN_CENTER || align == LV_TEXT_ALIGN_RIGHT) { 907 lv_coord_t align_ofs = 0; 908 lv_coord_t txts_w = is_first_line ? indent : 0; 909 for(int i = 0; i < item_cnt; i++) { 910 lv_snippet_t * pinfo = lv_get_snippet(i); 911 txts_w = txts_w + pinfo->txt_w + pinfo->letter_space; 912 } 913 txts_w -= lv_get_snippet(item_cnt - 1)->letter_space; 914 align_ofs = max_width > txts_w ? max_width - txts_w : 0; 915 if(align == LV_TEXT_ALIGN_CENTER) { 916 align_ofs = align_ofs >> 1; 917 } 918 txt_pos.x += align_ofs; 919 } 920 921 /* draw line letters */ 922 int i; 923 for(i = 0; i < item_cnt; i++) { 924 lv_snippet_t * pinfo = lv_get_snippet(i); 925 926 /* bidi deal with:todo */ 927 const char * bidi_txt = pinfo->txt; 928 929 lv_point_t pos; 930 pos.x = txt_pos.x; 931 pos.y = txt_pos.y + max_line_h - pinfo->line_h - (max_baseline - pinfo->font->base_line); 932 label_draw_dsc.color = lv_span_get_style_text_color(obj, pinfo->span); 933 label_draw_dsc.opa = lv_span_get_style_text_opa(obj, pinfo->span); 934 label_draw_dsc.font = lv_span_get_style_text_font(obj, pinfo->span); 935 label_draw_dsc.blend_mode = lv_span_get_style_text_blend_mode(obj, pinfo->span); 936 if(obj_opa < LV_OPA_MAX) { 937 label_draw_dsc.opa = (uint16_t)((uint16_t)label_draw_dsc.opa * obj_opa) >> 8; 938 } 939 uint32_t txt_bytes = pinfo->bytes; 940 941 /* overflow */ 942 uint16_t dot_letter_w = 0; 943 uint16_t dot_width = 0; 944 if(ellipsis_valid) { 945 dot_letter_w = lv_font_get_glyph_width(pinfo->font, '.', '.'); 946 dot_width = dot_letter_w * 3; 947 } 948 lv_coord_t ellipsis_width = coords.x1 + max_width - dot_width; 949 950 uint32_t j = 0; 951 while(j < txt_bytes) { 952 /* skip invalid fields */ 953 if(pos.x > clip_area.x2) { 954 break; 955 } 956 uint32_t letter = _lv_txt_encoded_next(bidi_txt, &j); 957 uint32_t letter_next = _lv_txt_encoded_next(&bidi_txt[j], NULL); 958 int32_t letter_w = lv_font_get_glyph_width(pinfo->font, letter, letter_next); 959 960 /* skip invalid fields */ 961 if(pos.x + letter_w + pinfo->letter_space < clip_area.x1) { 962 if(letter_w > 0) { 963 pos.x = pos.x + letter_w + pinfo->letter_space; 964 } 965 continue; 966 } 967 968 if(ellipsis_valid && pos.x + letter_w + pinfo->letter_space > ellipsis_width) { 969 for(int ell = 0; ell < 3; ell++) { 970 lv_draw_letter(draw_ctx, &label_draw_dsc, &pos, '.'); 971 pos.x = pos.x + dot_letter_w + pinfo->letter_space; 972 } 973 if(pos.x <= ellipsis_width) { 974 pos.x = ellipsis_width + 1; 975 } 976 break; 977 } 978 else { 979 lv_draw_letter(draw_ctx, &label_draw_dsc, &pos, letter); 980 if(letter_w > 0) { 981 pos.x = pos.x + letter_w + pinfo->letter_space; 982 } 983 } 984 } 985 986 /* draw decor */ 987 lv_text_decor_t decor = lv_span_get_style_text_decor(obj, pinfo->span); 988 if(decor != LV_TEXT_DECOR_NONE) { 989 lv_draw_line_dsc_t line_dsc; 990 lv_draw_line_dsc_init(&line_dsc); 991 line_dsc.color = label_draw_dsc.color; 992 line_dsc.width = label_draw_dsc.font->underline_thickness ? pinfo->font->underline_thickness : 1; 993 line_dsc.opa = label_draw_dsc.opa; 994 line_dsc.blend_mode = label_draw_dsc.blend_mode; 995 996 if(decor & LV_TEXT_DECOR_STRIKETHROUGH) { 997 lv_point_t p1; 998 lv_point_t p2; 999 p1.x = txt_pos.x; 1000 p1.y = pos.y + ((pinfo->line_h - line_space) >> 1) + (line_dsc.width >> 1); 1001 p2.x = pos.x; 1002 p2.y = p1.y; 1003 lv_draw_line(draw_ctx, &line_dsc, &p1, &p2); 1004 } 1005 1006 if(decor & LV_TEXT_DECOR_UNDERLINE) { 1007 lv_point_t p1; 1008 lv_point_t p2; 1009 p1.x = txt_pos.x; 1010 p1.y = pos.y + pinfo->line_h - line_space - pinfo->font->base_line - pinfo->font->underline_position; 1011 p2.x = pos.x; 1012 p2.y = p1.y; 1013 lv_draw_line(draw_ctx, &line_dsc, &p1, &p2); 1014 } 1015 } 1016 txt_pos.x = pos.x; 1017 } 1018 1019 Next_line_init: 1020 /* next line init */ 1021 is_first_line = false; 1022 txt_pos.x = coords.x1; 1023 txt_pos.y += max_line_h; 1024 if(is_end_line || txt_pos.y > clip_area.y2 + 1) { 1025 draw_ctx->clip_area = clip_area_ori; 1026 return; 1027 } 1028 max_w = max_width; 1029 } 1030 draw_ctx->clip_area = clip_area_ori; 1031 } 1032 1033 static void refresh_self_size(lv_obj_t * obj) 1034 { 1035 lv_spangroup_t * spans = (lv_spangroup_t *)obj; 1036 spans->refresh = 1; 1037 lv_obj_invalidate(obj); 1038 lv_obj_refresh_self_size(obj); 1039 } 1040 1041 #endif