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