@@ -87,6 +87,8 @@ int Label::get_line_height(int p_line) const {
87
87
}
88
88
89
89
void Label::_shape () {
90
+ bool font_was_dirty = font_dirty;
91
+
90
92
Ref<StyleBox> style = theme_cache.normal_style ;
91
93
int width = (get_size ().width - style->get_minimum_size ().width );
92
94
@@ -128,7 +130,24 @@ void Label::_shape() {
128
130
TS->free_rid (lines_rid[i]);
129
131
}
130
132
lines_rid.clear ();
133
+ }
134
+
135
+ Size2i prev_minsize = minsize;
136
+ minsize = Size2 ();
137
+
138
+ if (xl_text.length () == 0 ) {
139
+ lines_dirty = false ;
140
+ return ;
141
+ }
131
142
143
+ // Don't compute minimum size until width is stable (two shape requests in a row with the same width.)
144
+ // This avoids situations in which the initial width is very narrow and the label would break text
145
+ // into many very short lines, causing a very tall label that can leave a deformed container.
146
+ bool width_stabilized = get_size ().width == stable_width;
147
+ stable_width = get_size ().width ;
148
+
149
+ // With autowrap off, there's still a point in shaping before width is stable: to contribute a min width.
150
+ if (lines_dirty && (width_stabilized || autowrap_mode == TextServer::AUTOWRAP_OFF)) {
132
151
BitField<TextServer::LineBreakFlag> autowrap_flags = TextServer::BREAK_MANDATORY;
133
152
switch (autowrap_mode) {
134
153
case TextServer::AUTOWRAP_WORD_SMART:
@@ -152,21 +171,15 @@ void Label::_shape() {
152
171
}
153
172
}
154
173
155
- if (xl_text.length () == 0 ) {
156
- minsize = Size2 (1 , get_line_height ());
157
- return ;
158
- }
159
-
160
174
if (autowrap_mode == TextServer::AUTOWRAP_OFF) {
161
- minsize.width = 0 .0f ;
162
175
for (int i = 0 ; i < lines_rid.size (); i++) {
163
176
if (minsize.width < TS->shaped_text_get_size (lines_rid[i]).x ) {
164
177
minsize.width = TS->shaped_text_get_size (lines_rid[i]).x ;
165
178
}
166
179
}
167
180
}
168
181
169
- if (lines_dirty) {
182
+ if (lines_dirty && width_stabilized ) {
170
183
BitField<TextServer::TextOverrunFlag> overrun_flags = TextServer::OVERRUN_NO_TRIM;
171
184
switch (overrun_behavior) {
172
185
case TextServer::OVERRUN_TRIM_WORD_ELLIPSIS:
@@ -191,8 +204,11 @@ void Label::_shape() {
191
204
192
205
// Fill after min_size calculation.
193
206
207
+ int visible_lines = lines_rid.size ();
208
+ if (max_lines_visible >= 0 && visible_lines > max_lines_visible) {
209
+ visible_lines = max_lines_visible;
210
+ }
194
211
if (autowrap_mode != TextServer::AUTOWRAP_OFF) {
195
- int visible_lines = get_visible_line_count ();
196
212
bool lines_hidden = visible_lines > 0 && visible_lines < lines_rid.size ();
197
213
if (lines_hidden) {
198
214
overrun_flags.set_flag (TextServer::OVERRUN_ENFORCE_ELLIPSIS);
@@ -221,33 +237,22 @@ void Label::_shape() {
221
237
}
222
238
}
223
239
}
224
- lines_dirty = false ;
225
- lines_shaped_last_width = get_size ().width ;
226
- }
227
240
228
- _update_visible ();
241
+ int last_line = MIN (lines_rid.size (), visible_lines + lines_skipped);
242
+ int line_spacing = settings.is_valid () ? settings->get_line_spacing () : theme_cache.line_spacing ;
243
+ for (int64_t i = lines_skipped; i < last_line; i++) {
244
+ minsize.height += TS->shaped_text_get_size (lines_rid[i]).y + line_spacing;
245
+ }
229
246
230
- if (autowrap_mode == TextServer::AUTOWRAP_OFF || !clip || overrun_behavior == TextServer::OVERRUN_NO_TRIMMING) {
231
- update_minimum_size ();
247
+ lines_dirty = false ;
232
248
}
233
- }
234
-
235
- void Label::_update_visible () {
236
- int line_spacing = settings.is_valid () ? settings->get_line_spacing () : theme_cache.line_spacing ;
237
- Ref<StyleBox> style = theme_cache.normal_style ;
238
- int lines_visible = lines_rid.size ();
239
249
240
- if (max_lines_visible >= 0 && lines_visible > max_lines_visible ) {
241
- lines_visible = max_lines_visible ;
250
+ if (minsize != prev_minsize || font_was_dirty ) {
251
+ update_minimum_size () ;
242
252
}
243
253
244
- minsize.height = 0 ;
245
- int last_line = MIN (lines_rid.size (), lines_visible + lines_skipped);
246
- for (int64_t i = lines_skipped; i < last_line; i++) {
247
- minsize.height += TS->shaped_text_get_size (lines_rid[i]).y + line_spacing;
248
- if (minsize.height > (get_size ().height - style->get_minimum_size ().height + line_spacing)) {
249
- break ;
250
- }
254
+ if (!width_stabilized) {
255
+ callable_mp (this , &Label::_shape).call_deferred ();
251
256
}
252
257
}
253
258
@@ -347,6 +352,11 @@ void Label::_notification(int p_what) {
347
352
348
353
if (dirty || font_dirty || lines_dirty) {
349
354
_shape ();
355
+ if (lines_dirty) {
356
+ // There will be another pass.
357
+ queue_redraw ();
358
+ break ;
359
+ }
350
360
}
351
361
352
362
RID ci = get_canvas_item ();
@@ -370,22 +380,22 @@ void Label::_notification(int p_what) {
370
380
style->draw (ci, Rect2 (Point2 (0 , 0 ), get_size ()));
371
381
372
382
float total_h = 0.0 ;
373
- int lines_visible = 0 ;
383
+ int visible_lines = 0 ;
374
384
375
385
// Get number of lines to fit to the height.
376
386
for (int64_t i = lines_skipped; i < lines_rid.size (); i++) {
377
387
total_h += TS->shaped_text_get_size (lines_rid[i]).y + line_spacing;
378
388
if (total_h > (get_size ().height - style->get_minimum_size ().height + line_spacing)) {
379
389
break ;
380
390
}
381
- lines_visible ++;
391
+ visible_lines ++;
382
392
}
383
393
384
- if (max_lines_visible >= 0 && lines_visible > max_lines_visible) {
385
- lines_visible = max_lines_visible;
394
+ if (max_lines_visible >= 0 && visible_lines > max_lines_visible) {
395
+ visible_lines = max_lines_visible;
386
396
}
387
397
388
- int last_line = MIN (lines_rid.size (), lines_visible + lines_skipped);
398
+ int last_line = MIN (lines_rid.size (), visible_lines + lines_skipped);
389
399
bool trim_chars = (visible_chars >= 0 ) && (visible_chars_behavior == TextServer::VC_CHARS_AFTER_SHAPING);
390
400
bool trim_glyphs_ltr = (visible_chars >= 0 ) && ((visible_chars_behavior == TextServer::VC_GLYPHS_LTR) || ((visible_chars_behavior == TextServer::VC_GLYPHS_AUTO) && !rtl_layout));
391
401
bool trim_glyphs_rtl = (visible_chars >= 0 ) && ((visible_chars_behavior == TextServer::VC_GLYPHS_RTL) || ((visible_chars_behavior == TextServer::VC_GLYPHS_AUTO) && rtl_layout));
@@ -402,7 +412,7 @@ void Label::_notification(int p_what) {
402
412
total_h += style->get_margin (SIDE_TOP) + style->get_margin (SIDE_BOTTOM);
403
413
404
414
int vbegin = 0 , vsep = 0 ;
405
- if (lines_visible > 0 ) {
415
+ if (visible_lines > 0 ) {
406
416
switch (vertical_alignment) {
407
417
case VERTICAL_ALIGNMENT_TOP: {
408
418
// Nothing.
@@ -419,8 +429,8 @@ void Label::_notification(int p_what) {
419
429
} break ;
420
430
case VERTICAL_ALIGNMENT_FILL: {
421
431
vbegin = 0 ;
422
- if (lines_visible > 1 ) {
423
- vsep = (size.y - (total_h - line_spacing)) / (lines_visible - 1 );
432
+ if (visible_lines > 1 ) {
433
+ vsep = (size.y - (total_h - line_spacing)) / (visible_lines - 1 );
424
434
} else {
425
435
vsep = 0 ;
426
436
}
@@ -597,13 +607,7 @@ void Label::_notification(int p_what) {
597
607
} break ;
598
608
599
609
case NOTIFICATION_RESIZED: {
600
- // It may happen that the reshaping due to this size change triggers a cascade of re-layout
601
- // across the hierarchy where this label belongs to in a way that its size changes multiple
602
- // times, but ending up with the original size it was already shaped for.
603
- // This check prevents the catastrophic, freezing infinite cascade of re-layout.
604
- if (lines_shaped_last_width != get_size ().width ) {
605
- lines_dirty = true ;
606
- }
610
+ lines_dirty = true ;
607
611
} break ;
608
612
}
609
613
}
@@ -646,25 +650,25 @@ int Label::get_line_count() const {
646
650
int Label::get_visible_line_count () const {
647
651
Ref<StyleBox> style = theme_cache.normal_style ;
648
652
int line_spacing = settings.is_valid () ? settings->get_line_spacing () : theme_cache.line_spacing ;
649
- int lines_visible = 0 ;
653
+ int visible_lines = 0 ;
650
654
float total_h = 0.0 ;
651
655
for (int64_t i = lines_skipped; i < lines_rid.size (); i++) {
652
656
total_h += TS->shaped_text_get_size (lines_rid[i]).y + line_spacing;
653
657
if (total_h > (get_size ().height - style->get_minimum_size ().height + line_spacing)) {
654
658
break ;
655
659
}
656
- lines_visible ++;
660
+ visible_lines ++;
657
661
}
658
662
659
- if (lines_visible > lines_rid.size ()) {
660
- lines_visible = lines_rid.size ();
663
+ if (visible_lines > lines_rid.size ()) {
664
+ visible_lines = lines_rid.size ();
661
665
}
662
666
663
- if (max_lines_visible >= 0 && lines_visible > max_lines_visible) {
664
- lines_visible = max_lines_visible;
667
+ if (max_lines_visible >= 0 && visible_lines > max_lines_visible) {
668
+ visible_lines = max_lines_visible;
665
669
}
666
670
667
- return lines_visible ;
671
+ return visible_lines ;
668
672
}
669
673
670
674
void Label::set_horizontal_alignment (HorizontalAlignment p_alignment) {
@@ -886,7 +890,7 @@ void Label::set_lines_skipped(int p_lines) {
886
890
}
887
891
888
892
lines_skipped = p_lines;
889
- _update_visible () ;
893
+ lines_dirty = true ;
890
894
queue_redraw ();
891
895
}
892
896
@@ -900,7 +904,7 @@ void Label::set_max_lines_visible(int p_lines) {
900
904
}
901
905
902
906
max_lines_visible = p_lines;
903
- _update_visible () ;
907
+ lines_dirty = true ;
904
908
queue_redraw ();
905
909
}
906
910
@@ -949,7 +953,7 @@ void Label::_bind_methods() {
949
953
ClassDB::bind_method (D_METHOD (" get_visible_ratio" ), &Label::get_visible_ratio);
950
954
ClassDB::bind_method (D_METHOD (" set_lines_skipped" , " lines_skipped" ), &Label::set_lines_skipped);
951
955
ClassDB::bind_method (D_METHOD (" get_lines_skipped" ), &Label::get_lines_skipped);
952
- ClassDB::bind_method (D_METHOD (" set_max_lines_visible" , " lines_visible " ), &Label::set_max_lines_visible);
956
+ ClassDB::bind_method (D_METHOD (" set_max_lines_visible" , " visible_lines " ), &Label::set_max_lines_visible);
953
957
ClassDB::bind_method (D_METHOD (" get_max_lines_visible" ), &Label::get_max_lines_visible);
954
958
ClassDB::bind_method (D_METHOD (" set_structured_text_bidi_override" , " parser" ), &Label::set_structured_text_bidi_override);
955
959
ClassDB::bind_method (D_METHOD (" get_structured_text_bidi_override" ), &Label::get_structured_text_bidi_override);
0 commit comments