1 | /**************************************************************************/ |
2 | /* label.cpp */ |
3 | /**************************************************************************/ |
4 | /* This file is part of: */ |
5 | /* GODOT ENGINE */ |
6 | /* https://godotengine.org */ |
7 | /**************************************************************************/ |
8 | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ |
9 | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ |
10 | /* */ |
11 | /* Permission is hereby granted, free of charge, to any person obtaining */ |
12 | /* a copy of this software and associated documentation files (the */ |
13 | /* "Software"), to deal in the Software without restriction, including */ |
14 | /* without limitation the rights to use, copy, modify, merge, publish, */ |
15 | /* distribute, sublicense, and/or sell copies of the Software, and to */ |
16 | /* permit persons to whom the Software is furnished to do so, subject to */ |
17 | /* the following conditions: */ |
18 | /* */ |
19 | /* The above copyright notice and this permission notice shall be */ |
20 | /* included in all copies or substantial portions of the Software. */ |
21 | /* */ |
22 | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ |
23 | /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ |
24 | /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ |
25 | /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ |
26 | /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ |
27 | /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ |
28 | /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ |
29 | /**************************************************************************/ |
30 | |
31 | #include "label.h" |
32 | |
33 | #include "core/config/project_settings.h" |
34 | #include "core/string/print_string.h" |
35 | #include "core/string/translation.h" |
36 | #include "scene/theme/theme_db.h" |
37 | #include "servers/text_server.h" |
38 | |
39 | void Label::set_autowrap_mode(TextServer::AutowrapMode p_mode) { |
40 | if (autowrap_mode == p_mode) { |
41 | return; |
42 | } |
43 | |
44 | autowrap_mode = p_mode; |
45 | lines_dirty = true; |
46 | queue_redraw(); |
47 | |
48 | if (clip || overrun_behavior != TextServer::OVERRUN_NO_TRIMMING) { |
49 | update_minimum_size(); |
50 | } |
51 | } |
52 | |
53 | TextServer::AutowrapMode Label::get_autowrap_mode() const { |
54 | return autowrap_mode; |
55 | } |
56 | |
57 | void Label::set_justification_flags(BitField<TextServer::JustificationFlag> p_flags) { |
58 | if (jst_flags == p_flags) { |
59 | return; |
60 | } |
61 | |
62 | jst_flags = p_flags; |
63 | lines_dirty = true; |
64 | queue_redraw(); |
65 | } |
66 | |
67 | BitField<TextServer::JustificationFlag> Label::get_justification_flags() const { |
68 | return jst_flags; |
69 | } |
70 | |
71 | void Label::set_uppercase(bool p_uppercase) { |
72 | if (uppercase == p_uppercase) { |
73 | return; |
74 | } |
75 | |
76 | uppercase = p_uppercase; |
77 | dirty = true; |
78 | |
79 | queue_redraw(); |
80 | } |
81 | |
82 | bool Label::is_uppercase() const { |
83 | return uppercase; |
84 | } |
85 | |
86 | int Label::get_line_height(int p_line) const { |
87 | Ref<Font> font = (settings.is_valid() && settings->get_font().is_valid()) ? settings->get_font() : theme_cache.font; |
88 | if (p_line >= 0 && p_line < lines_rid.size()) { |
89 | return TS->shaped_text_get_size(lines_rid[p_line]).y; |
90 | } else if (lines_rid.size() > 0) { |
91 | int h = 0; |
92 | for (int i = 0; i < lines_rid.size(); i++) { |
93 | h = MAX(h, TS->shaped_text_get_size(lines_rid[i]).y); |
94 | } |
95 | return h; |
96 | } else { |
97 | int font_size = settings.is_valid() ? settings->get_font_size() : theme_cache.font_size; |
98 | return font->get_height(font_size); |
99 | } |
100 | } |
101 | |
102 | void Label::_shape() { |
103 | Ref<StyleBox> style = theme_cache.normal_style; |
104 | int width = (get_size().width - style->get_minimum_size().width); |
105 | |
106 | if (dirty || font_dirty) { |
107 | if (dirty) { |
108 | TS->shaped_text_clear(text_rid); |
109 | } |
110 | if (text_direction == Control::TEXT_DIRECTION_INHERITED) { |
111 | TS->shaped_text_set_direction(text_rid, is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR); |
112 | } else { |
113 | TS->shaped_text_set_direction(text_rid, (TextServer::Direction)text_direction); |
114 | } |
115 | const Ref<Font> &font = (settings.is_valid() && settings->get_font().is_valid()) ? settings->get_font() : theme_cache.font; |
116 | int font_size = settings.is_valid() ? settings->get_font_size() : theme_cache.font_size; |
117 | ERR_FAIL_COND(font.is_null()); |
118 | String txt = (uppercase) ? TS->string_to_upper(xl_text, language) : xl_text; |
119 | if (visible_chars >= 0 && visible_chars_behavior == TextServer::VC_CHARS_BEFORE_SHAPING) { |
120 | txt = txt.substr(0, visible_chars); |
121 | } |
122 | if (dirty) { |
123 | TS->shaped_text_add_string(text_rid, txt, font->get_rids(), font_size, font->get_opentype_features(), language); |
124 | } else { |
125 | int spans = TS->shaped_get_span_count(text_rid); |
126 | for (int i = 0; i < spans; i++) { |
127 | TS->shaped_set_span_update_font(text_rid, i, font->get_rids(), font_size, font->get_opentype_features()); |
128 | } |
129 | } |
130 | TS->shaped_text_set_bidi_override(text_rid, structured_text_parser(st_parser, st_args, txt)); |
131 | if (!tab_stops.is_empty()) { |
132 | TS->shaped_text_tab_align(text_rid, tab_stops); |
133 | } |
134 | dirty = false; |
135 | font_dirty = false; |
136 | lines_dirty = true; |
137 | } |
138 | |
139 | if (lines_dirty) { |
140 | for (int i = 0; i < lines_rid.size(); i++) { |
141 | TS->free_rid(lines_rid[i]); |
142 | } |
143 | lines_rid.clear(); |
144 | |
145 | BitField<TextServer::LineBreakFlag> autowrap_flags = TextServer::BREAK_MANDATORY; |
146 | switch (autowrap_mode) { |
147 | case TextServer::AUTOWRAP_WORD_SMART: |
148 | autowrap_flags = TextServer::BREAK_WORD_BOUND | TextServer::BREAK_ADAPTIVE | TextServer::BREAK_MANDATORY; |
149 | break; |
150 | case TextServer::AUTOWRAP_WORD: |
151 | autowrap_flags = TextServer::BREAK_WORD_BOUND | TextServer::BREAK_MANDATORY; |
152 | break; |
153 | case TextServer::AUTOWRAP_ARBITRARY: |
154 | autowrap_flags = TextServer::BREAK_GRAPHEME_BOUND | TextServer::BREAK_MANDATORY; |
155 | break; |
156 | case TextServer::AUTOWRAP_OFF: |
157 | break; |
158 | } |
159 | autowrap_flags = autowrap_flags | TextServer::BREAK_TRIM_EDGE_SPACES; |
160 | |
161 | PackedInt32Array line_breaks = TS->shaped_text_get_line_breaks(text_rid, width, 0, autowrap_flags); |
162 | for (int i = 0; i < line_breaks.size(); i = i + 2) { |
163 | RID line = TS->shaped_text_substr(text_rid, line_breaks[i], line_breaks[i + 1] - line_breaks[i]); |
164 | if (!tab_stops.is_empty()) { |
165 | TS->shaped_text_tab_align(line, tab_stops); |
166 | } |
167 | lines_rid.push_back(line); |
168 | } |
169 | } |
170 | |
171 | if (xl_text.length() == 0) { |
172 | minsize = Size2(1, get_line_height()); |
173 | return; |
174 | } |
175 | |
176 | if (autowrap_mode == TextServer::AUTOWRAP_OFF) { |
177 | minsize.width = 0.0f; |
178 | for (int i = 0; i < lines_rid.size(); i++) { |
179 | if (minsize.width < TS->shaped_text_get_size(lines_rid[i]).x) { |
180 | minsize.width = TS->shaped_text_get_size(lines_rid[i]).x; |
181 | } |
182 | } |
183 | } |
184 | |
185 | if (lines_dirty) { |
186 | BitField<TextServer::TextOverrunFlag> overrun_flags = TextServer::OVERRUN_NO_TRIM; |
187 | switch (overrun_behavior) { |
188 | case TextServer::OVERRUN_TRIM_WORD_ELLIPSIS: |
189 | overrun_flags.set_flag(TextServer::OVERRUN_TRIM); |
190 | overrun_flags.set_flag(TextServer::OVERRUN_TRIM_WORD_ONLY); |
191 | overrun_flags.set_flag(TextServer::OVERRUN_ADD_ELLIPSIS); |
192 | break; |
193 | case TextServer::OVERRUN_TRIM_ELLIPSIS: |
194 | overrun_flags.set_flag(TextServer::OVERRUN_TRIM); |
195 | overrun_flags.set_flag(TextServer::OVERRUN_ADD_ELLIPSIS); |
196 | break; |
197 | case TextServer::OVERRUN_TRIM_WORD: |
198 | overrun_flags.set_flag(TextServer::OVERRUN_TRIM); |
199 | overrun_flags.set_flag(TextServer::OVERRUN_TRIM_WORD_ONLY); |
200 | break; |
201 | case TextServer::OVERRUN_TRIM_CHAR: |
202 | overrun_flags.set_flag(TextServer::OVERRUN_TRIM); |
203 | break; |
204 | case TextServer::OVERRUN_NO_TRIMMING: |
205 | break; |
206 | } |
207 | |
208 | // Fill after min_size calculation. |
209 | |
210 | BitField<TextServer::JustificationFlag> line_jst_flags = jst_flags; |
211 | if (!tab_stops.is_empty()) { |
212 | line_jst_flags.set_flag(TextServer::JUSTIFICATION_AFTER_LAST_TAB); |
213 | } |
214 | if (autowrap_mode != TextServer::AUTOWRAP_OFF) { |
215 | int visible_lines = get_visible_line_count(); |
216 | bool lines_hidden = visible_lines > 0 && visible_lines < lines_rid.size(); |
217 | if (lines_hidden) { |
218 | overrun_flags.set_flag(TextServer::OVERRUN_ENFORCE_ELLIPSIS); |
219 | } |
220 | if (horizontal_alignment == HORIZONTAL_ALIGNMENT_FILL) { |
221 | int jst_to_line = visible_lines; |
222 | if (lines_rid.size() == 1 && line_jst_flags.has_flag(TextServer::JUSTIFICATION_DO_NOT_SKIP_SINGLE_LINE)) { |
223 | jst_to_line = lines_rid.size(); |
224 | } else { |
225 | if (line_jst_flags.has_flag(TextServer::JUSTIFICATION_SKIP_LAST_LINE)) { |
226 | jst_to_line = visible_lines - 1; |
227 | } |
228 | if (line_jst_flags.has_flag(TextServer::JUSTIFICATION_SKIP_LAST_LINE_WITH_VISIBLE_CHARS)) { |
229 | for (int i = visible_lines - 1; i >= 0; i--) { |
230 | if (TS->shaped_text_has_visible_chars(lines_rid[i])) { |
231 | jst_to_line = i; |
232 | break; |
233 | } |
234 | } |
235 | } |
236 | } |
237 | for (int i = 0; i < lines_rid.size(); i++) { |
238 | if (i < jst_to_line) { |
239 | TS->shaped_text_fit_to_width(lines_rid[i], width, line_jst_flags); |
240 | } else if (i == (visible_lines - 1)) { |
241 | TS->shaped_text_overrun_trim_to_width(lines_rid[i], width, overrun_flags); |
242 | } |
243 | } |
244 | } else if (lines_hidden) { |
245 | TS->shaped_text_overrun_trim_to_width(lines_rid[visible_lines - 1], width, overrun_flags); |
246 | } |
247 | } else { |
248 | // Autowrap disabled. |
249 | int jst_to_line = lines_rid.size(); |
250 | if (lines_rid.size() == 1 && line_jst_flags.has_flag(TextServer::JUSTIFICATION_DO_NOT_SKIP_SINGLE_LINE)) { |
251 | jst_to_line = lines_rid.size(); |
252 | } else { |
253 | if (line_jst_flags.has_flag(TextServer::JUSTIFICATION_SKIP_LAST_LINE)) { |
254 | jst_to_line = lines_rid.size() - 1; |
255 | } |
256 | if (line_jst_flags.has_flag(TextServer::JUSTIFICATION_SKIP_LAST_LINE_WITH_VISIBLE_CHARS)) { |
257 | for (int i = lines_rid.size() - 1; i >= 0; i--) { |
258 | if (TS->shaped_text_has_visible_chars(lines_rid[i])) { |
259 | jst_to_line = i; |
260 | break; |
261 | } |
262 | } |
263 | } |
264 | } |
265 | for (int i = 0; i < lines_rid.size(); i++) { |
266 | if (i < jst_to_line && horizontal_alignment == HORIZONTAL_ALIGNMENT_FILL) { |
267 | TS->shaped_text_fit_to_width(lines_rid[i], width, line_jst_flags); |
268 | overrun_flags.set_flag(TextServer::OVERRUN_JUSTIFICATION_AWARE); |
269 | TS->shaped_text_overrun_trim_to_width(lines_rid[i], width, overrun_flags); |
270 | TS->shaped_text_fit_to_width(lines_rid[i], width, line_jst_flags | TextServer::JUSTIFICATION_CONSTRAIN_ELLIPSIS); |
271 | } else { |
272 | TS->shaped_text_overrun_trim_to_width(lines_rid[i], width, overrun_flags); |
273 | } |
274 | } |
275 | } |
276 | lines_dirty = false; |
277 | } |
278 | |
279 | _update_visible(); |
280 | |
281 | if (autowrap_mode == TextServer::AUTOWRAP_OFF || !clip || overrun_behavior == TextServer::OVERRUN_NO_TRIMMING) { |
282 | update_minimum_size(); |
283 | } |
284 | } |
285 | |
286 | void Label::_update_visible() { |
287 | int line_spacing = settings.is_valid() ? settings->get_line_spacing() : theme_cache.line_spacing; |
288 | Ref<StyleBox> style = theme_cache.normal_style; |
289 | int lines_visible = lines_rid.size(); |
290 | |
291 | if (max_lines_visible >= 0 && lines_visible > max_lines_visible) { |
292 | lines_visible = max_lines_visible; |
293 | } |
294 | |
295 | minsize.height = 0; |
296 | int last_line = MIN(lines_rid.size(), lines_visible + lines_skipped); |
297 | for (int64_t i = lines_skipped; i < last_line; i++) { |
298 | minsize.height += TS->shaped_text_get_size(lines_rid[i]).y + line_spacing; |
299 | } |
300 | if (minsize.height > 0) { |
301 | minsize.height -= line_spacing; |
302 | } |
303 | } |
304 | |
305 | inline void draw_glyph(const Glyph &p_gl, const RID &p_canvas, const Color &p_font_color, const Vector2 &p_ofs) { |
306 | if (p_gl.font_rid != RID()) { |
307 | TS->font_draw_glyph(p_gl.font_rid, p_canvas, p_gl.font_size, p_ofs + Vector2(p_gl.x_off, p_gl.y_off), p_gl.index, p_font_color); |
308 | } else { |
309 | TS->draw_hex_code_box(p_canvas, p_gl.font_size, p_ofs + Vector2(p_gl.x_off, p_gl.y_off), p_gl.index, p_font_color); |
310 | } |
311 | } |
312 | |
313 | inline void draw_glyph_outline(const Glyph &p_gl, const RID &p_canvas, const Color &p_font_color, const Color &p_font_shadow_color, const Color &p_font_outline_color, const int &p_shadow_outline_size, const int &p_outline_size, const Vector2 &p_ofs, const Vector2 &shadow_ofs) { |
314 | if (p_gl.font_rid != RID()) { |
315 | if (p_font_shadow_color.a > 0) { |
316 | TS->font_draw_glyph(p_gl.font_rid, p_canvas, p_gl.font_size, p_ofs + Vector2(p_gl.x_off, p_gl.y_off) + shadow_ofs, p_gl.index, p_font_shadow_color); |
317 | } |
318 | if (p_font_shadow_color.a > 0 && p_shadow_outline_size > 0) { |
319 | TS->font_draw_glyph_outline(p_gl.font_rid, p_canvas, p_gl.font_size, p_shadow_outline_size, p_ofs + Vector2(p_gl.x_off, p_gl.y_off) + shadow_ofs, p_gl.index, p_font_shadow_color); |
320 | } |
321 | if (p_font_outline_color.a != 0.0 && p_outline_size > 0) { |
322 | TS->font_draw_glyph_outline(p_gl.font_rid, p_canvas, p_gl.font_size, p_outline_size, p_ofs + Vector2(p_gl.x_off, p_gl.y_off), p_gl.index, p_font_outline_color); |
323 | } |
324 | } |
325 | } |
326 | |
327 | PackedStringArray Label::get_configuration_warnings() const { |
328 | PackedStringArray warnings = Control::get_configuration_warnings(); |
329 | |
330 | // Ensure that the font can render all of the required glyphs. |
331 | Ref<Font> font; |
332 | if (settings.is_valid()) { |
333 | font = settings->get_font(); |
334 | } |
335 | if (font.is_null()) { |
336 | font = theme_cache.font; |
337 | } |
338 | |
339 | if (font.is_valid()) { |
340 | if (dirty || font_dirty || lines_dirty) { |
341 | const_cast<Label *>(this)->_shape(); |
342 | } |
343 | |
344 | const Glyph *glyph = TS->shaped_text_get_glyphs(text_rid); |
345 | int64_t glyph_count = TS->shaped_text_get_glyph_count(text_rid); |
346 | for (int64_t i = 0; i < glyph_count; i++) { |
347 | if (glyph[i].font_rid == RID()) { |
348 | warnings.push_back(RTR("The current font does not support rendering one or more characters used in this Label's text." )); |
349 | break; |
350 | } |
351 | } |
352 | } |
353 | |
354 | return warnings; |
355 | } |
356 | |
357 | void Label::_notification(int p_what) { |
358 | switch (p_what) { |
359 | case NOTIFICATION_TRANSLATION_CHANGED: { |
360 | String new_text = atr(text); |
361 | if (new_text == xl_text) { |
362 | return; // Nothing new. |
363 | } |
364 | xl_text = new_text; |
365 | if (visible_ratio < 1) { |
366 | visible_chars = get_total_character_count() * visible_ratio; |
367 | } |
368 | dirty = true; |
369 | |
370 | queue_redraw(); |
371 | update_configuration_warnings(); |
372 | } break; |
373 | |
374 | case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: { |
375 | queue_redraw(); |
376 | } break; |
377 | |
378 | case NOTIFICATION_DRAW: { |
379 | if (clip) { |
380 | RenderingServer::get_singleton()->canvas_item_set_clip(get_canvas_item(), true); |
381 | } |
382 | |
383 | // When a shaped text is invalidated by an external source, we want to reshape it. |
384 | if (!TS->shaped_text_is_ready(text_rid)) { |
385 | dirty = true; |
386 | } |
387 | |
388 | for (const RID &line_rid : lines_rid) { |
389 | if (!TS->shaped_text_is_ready(line_rid)) { |
390 | lines_dirty = true; |
391 | break; |
392 | } |
393 | } |
394 | |
395 | if (dirty || font_dirty || lines_dirty) { |
396 | _shape(); |
397 | } |
398 | |
399 | RID ci = get_canvas_item(); |
400 | |
401 | bool has_settings = settings.is_valid(); |
402 | |
403 | Size2 string_size; |
404 | Size2 size = get_size(); |
405 | Ref<StyleBox> style = theme_cache.normal_style; |
406 | Ref<Font> font = (has_settings && settings->get_font().is_valid()) ? settings->get_font() : theme_cache.font; |
407 | Color font_color = has_settings ? settings->get_font_color() : theme_cache.font_color; |
408 | Color font_shadow_color = has_settings ? settings->get_shadow_color() : theme_cache.font_shadow_color; |
409 | Point2 shadow_ofs = has_settings ? settings->get_shadow_offset() : theme_cache.font_shadow_offset; |
410 | int line_spacing = has_settings ? settings->get_line_spacing() : theme_cache.line_spacing; |
411 | Color font_outline_color = has_settings ? settings->get_outline_color() : theme_cache.font_outline_color; |
412 | int outline_size = has_settings ? settings->get_outline_size() : theme_cache.font_outline_size; |
413 | int shadow_outline_size = has_settings ? settings->get_shadow_size() : theme_cache.font_shadow_outline_size; |
414 | bool rtl = (TS->shaped_text_get_inferred_direction(text_rid) == TextServer::DIRECTION_RTL); |
415 | bool rtl_layout = is_layout_rtl(); |
416 | |
417 | style->draw(ci, Rect2(Point2(0, 0), get_size())); |
418 | |
419 | float total_h = 0.0; |
420 | int lines_visible = 0; |
421 | |
422 | // Get number of lines to fit to the height. |
423 | for (int64_t i = lines_skipped; i < lines_rid.size(); i++) { |
424 | total_h += TS->shaped_text_get_size(lines_rid[i]).y + line_spacing; |
425 | if (total_h > (get_size().height - style->get_minimum_size().height + line_spacing)) { |
426 | break; |
427 | } |
428 | lines_visible++; |
429 | } |
430 | |
431 | if (max_lines_visible >= 0 && lines_visible > max_lines_visible) { |
432 | lines_visible = max_lines_visible; |
433 | } |
434 | |
435 | int last_line = MIN(lines_rid.size(), lines_visible + lines_skipped); |
436 | bool trim_chars = (visible_chars >= 0) && (visible_chars_behavior == TextServer::VC_CHARS_AFTER_SHAPING); |
437 | bool trim_glyphs_ltr = (visible_chars >= 0) && ((visible_chars_behavior == TextServer::VC_GLYPHS_LTR) || ((visible_chars_behavior == TextServer::VC_GLYPHS_AUTO) && !rtl_layout)); |
438 | bool trim_glyphs_rtl = (visible_chars >= 0) && ((visible_chars_behavior == TextServer::VC_GLYPHS_RTL) || ((visible_chars_behavior == TextServer::VC_GLYPHS_AUTO) && rtl_layout)); |
439 | |
440 | // Get real total height. |
441 | int total_glyphs = 0; |
442 | total_h = 0; |
443 | for (int64_t i = lines_skipped; i < last_line; i++) { |
444 | total_h += TS->shaped_text_get_size(lines_rid[i]).y + line_spacing; |
445 | total_glyphs += TS->shaped_text_get_glyph_count(lines_rid[i]) + TS->shaped_text_get_ellipsis_glyph_count(lines_rid[i]); |
446 | } |
447 | int visible_glyphs = total_glyphs * visible_ratio; |
448 | int processed_glyphs = 0; |
449 | total_h += style->get_margin(SIDE_TOP) + style->get_margin(SIDE_BOTTOM); |
450 | |
451 | int vbegin = 0, vsep = 0; |
452 | if (lines_visible > 0) { |
453 | switch (vertical_alignment) { |
454 | case VERTICAL_ALIGNMENT_TOP: { |
455 | // Nothing. |
456 | } break; |
457 | case VERTICAL_ALIGNMENT_CENTER: { |
458 | vbegin = (size.y - (total_h - line_spacing)) / 2; |
459 | vsep = 0; |
460 | |
461 | } break; |
462 | case VERTICAL_ALIGNMENT_BOTTOM: { |
463 | vbegin = size.y - (total_h - line_spacing); |
464 | vsep = 0; |
465 | |
466 | } break; |
467 | case VERTICAL_ALIGNMENT_FILL: { |
468 | vbegin = 0; |
469 | if (lines_visible > 1) { |
470 | vsep = (size.y - (total_h - line_spacing)) / (lines_visible - 1); |
471 | } else { |
472 | vsep = 0; |
473 | } |
474 | |
475 | } break; |
476 | } |
477 | } |
478 | |
479 | Vector2 ofs; |
480 | ofs.y = style->get_offset().y + vbegin; |
481 | for (int i = lines_skipped; i < last_line; i++) { |
482 | Size2 line_size = TS->shaped_text_get_size(lines_rid[i]); |
483 | ofs.x = 0; |
484 | ofs.y += TS->shaped_text_get_ascent(lines_rid[i]); |
485 | switch (horizontal_alignment) { |
486 | case HORIZONTAL_ALIGNMENT_FILL: |
487 | if (rtl && autowrap_mode != TextServer::AUTOWRAP_OFF) { |
488 | ofs.x = int(size.width - style->get_margin(SIDE_RIGHT) - line_size.width); |
489 | } else { |
490 | ofs.x = style->get_offset().x; |
491 | } |
492 | break; |
493 | case HORIZONTAL_ALIGNMENT_LEFT: { |
494 | if (rtl_layout) { |
495 | ofs.x = int(size.width - style->get_margin(SIDE_RIGHT) - line_size.width); |
496 | } else { |
497 | ofs.x = style->get_offset().x; |
498 | } |
499 | } break; |
500 | case HORIZONTAL_ALIGNMENT_CENTER: { |
501 | ofs.x = int(size.width - line_size.width) / 2; |
502 | } break; |
503 | case HORIZONTAL_ALIGNMENT_RIGHT: { |
504 | if (rtl_layout) { |
505 | ofs.x = style->get_offset().x; |
506 | } else { |
507 | ofs.x = int(size.width - style->get_margin(SIDE_RIGHT) - line_size.width); |
508 | } |
509 | } break; |
510 | } |
511 | |
512 | const Glyph *glyphs = TS->shaped_text_get_glyphs(lines_rid[i]); |
513 | int gl_size = TS->shaped_text_get_glyph_count(lines_rid[i]); |
514 | |
515 | int ellipsis_pos = TS->shaped_text_get_ellipsis_pos(lines_rid[i]); |
516 | int trim_pos = TS->shaped_text_get_trim_pos(lines_rid[i]); |
517 | |
518 | const Glyph *ellipsis_glyphs = TS->shaped_text_get_ellipsis_glyphs(lines_rid[i]); |
519 | int ellipsis_gl_size = TS->shaped_text_get_ellipsis_glyph_count(lines_rid[i]); |
520 | |
521 | // Draw outline. Note: Do not merge this into the single loop with the main text, to prevent overlaps. |
522 | int processed_glyphs_ol = processed_glyphs; |
523 | if ((outline_size > 0 && font_outline_color.a != 0) || (font_shadow_color.a != 0)) { |
524 | Vector2 offset = ofs; |
525 | // Draw RTL ellipsis string when necessary. |
526 | if (rtl && ellipsis_pos >= 0) { |
527 | for (int gl_idx = ellipsis_gl_size - 1; gl_idx >= 0; gl_idx--) { |
528 | for (int j = 0; j < ellipsis_glyphs[gl_idx].repeat; j++) { |
529 | bool skip = (trim_chars && ellipsis_glyphs[gl_idx].end > visible_chars) || (trim_glyphs_ltr && (processed_glyphs_ol >= visible_glyphs)) || (trim_glyphs_rtl && (processed_glyphs_ol < total_glyphs - visible_glyphs)); |
530 | //Draw glyph outlines and shadow. |
531 | if (!skip) { |
532 | draw_glyph_outline(ellipsis_glyphs[gl_idx], ci, font_color, font_shadow_color, font_outline_color, shadow_outline_size, outline_size, offset, shadow_ofs); |
533 | } |
534 | processed_glyphs_ol++; |
535 | offset.x += ellipsis_glyphs[gl_idx].advance; |
536 | } |
537 | } |
538 | } |
539 | |
540 | // Draw main text. |
541 | for (int j = 0; j < gl_size; j++) { |
542 | // Trim when necessary. |
543 | if (trim_pos >= 0) { |
544 | if (rtl) { |
545 | if (j < trim_pos) { |
546 | continue; |
547 | } |
548 | } else { |
549 | if (j >= trim_pos) { |
550 | break; |
551 | } |
552 | } |
553 | } |
554 | for (int k = 0; k < glyphs[j].repeat; k++) { |
555 | bool skip = (trim_chars && glyphs[j].end > visible_chars) || (trim_glyphs_ltr && (processed_glyphs_ol >= visible_glyphs)) || (trim_glyphs_rtl && (processed_glyphs_ol < total_glyphs - visible_glyphs)); |
556 | |
557 | // Draw glyph outlines and shadow. |
558 | if (!skip) { |
559 | draw_glyph_outline(glyphs[j], ci, font_color, font_shadow_color, font_outline_color, shadow_outline_size, outline_size, offset, shadow_ofs); |
560 | } |
561 | processed_glyphs_ol++; |
562 | offset.x += glyphs[j].advance; |
563 | } |
564 | } |
565 | // Draw LTR ellipsis string when necessary. |
566 | if (!rtl && ellipsis_pos >= 0) { |
567 | for (int gl_idx = 0; gl_idx < ellipsis_gl_size; gl_idx++) { |
568 | for (int j = 0; j < ellipsis_glyphs[gl_idx].repeat; j++) { |
569 | bool skip = (trim_chars && ellipsis_glyphs[gl_idx].end > visible_chars) || (trim_glyphs_ltr && (processed_glyphs_ol >= visible_glyphs)) || (trim_glyphs_rtl && (processed_glyphs_ol < total_glyphs - visible_glyphs)); |
570 | //Draw glyph outlines and shadow. |
571 | if (!skip) { |
572 | draw_glyph_outline(ellipsis_glyphs[gl_idx], ci, font_color, font_shadow_color, font_outline_color, shadow_outline_size, outline_size, offset, shadow_ofs); |
573 | } |
574 | processed_glyphs_ol++; |
575 | offset.x += ellipsis_glyphs[gl_idx].advance; |
576 | } |
577 | } |
578 | } |
579 | } |
580 | |
581 | // Draw main text. Note: Do not merge this into the single loop with the outline, to prevent overlaps. |
582 | |
583 | // Draw RTL ellipsis string when necessary. |
584 | if (rtl && ellipsis_pos >= 0) { |
585 | for (int gl_idx = ellipsis_gl_size - 1; gl_idx >= 0; gl_idx--) { |
586 | for (int j = 0; j < ellipsis_glyphs[gl_idx].repeat; j++) { |
587 | bool skip = (trim_chars && ellipsis_glyphs[gl_idx].end > visible_chars) || (trim_glyphs_ltr && (processed_glyphs >= visible_glyphs)) || (trim_glyphs_rtl && (processed_glyphs < total_glyphs - visible_glyphs)); |
588 | //Draw glyph outlines and shadow. |
589 | if (!skip) { |
590 | draw_glyph(ellipsis_glyphs[gl_idx], ci, font_color, ofs); |
591 | } |
592 | processed_glyphs++; |
593 | ofs.x += ellipsis_glyphs[gl_idx].advance; |
594 | } |
595 | } |
596 | } |
597 | |
598 | // Draw main text. |
599 | for (int j = 0; j < gl_size; j++) { |
600 | // Trim when necessary. |
601 | if (trim_pos >= 0) { |
602 | if (rtl) { |
603 | if (j < trim_pos) { |
604 | continue; |
605 | } |
606 | } else { |
607 | if (j >= trim_pos) { |
608 | break; |
609 | } |
610 | } |
611 | } |
612 | for (int k = 0; k < glyphs[j].repeat; k++) { |
613 | bool skip = (trim_chars && glyphs[j].end > visible_chars) || (trim_glyphs_ltr && (processed_glyphs >= visible_glyphs)) || (trim_glyphs_rtl && (processed_glyphs < total_glyphs - visible_glyphs)); |
614 | |
615 | // Draw glyph outlines and shadow. |
616 | if (!skip) { |
617 | draw_glyph(glyphs[j], ci, font_color, ofs); |
618 | } |
619 | processed_glyphs++; |
620 | ofs.x += glyphs[j].advance; |
621 | } |
622 | } |
623 | // Draw LTR ellipsis string when necessary. |
624 | if (!rtl && ellipsis_pos >= 0) { |
625 | for (int gl_idx = 0; gl_idx < ellipsis_gl_size; gl_idx++) { |
626 | for (int j = 0; j < ellipsis_glyphs[gl_idx].repeat; j++) { |
627 | bool skip = (trim_chars && ellipsis_glyphs[gl_idx].end > visible_chars) || (trim_glyphs_ltr && (processed_glyphs >= visible_glyphs)) || (trim_glyphs_rtl && (processed_glyphs < total_glyphs - visible_glyphs)); |
628 | //Draw glyph outlines and shadow. |
629 | if (!skip) { |
630 | draw_glyph(ellipsis_glyphs[gl_idx], ci, font_color, ofs); |
631 | } |
632 | processed_glyphs++; |
633 | ofs.x += ellipsis_glyphs[gl_idx].advance; |
634 | } |
635 | } |
636 | } |
637 | ofs.y += TS->shaped_text_get_descent(lines_rid[i]) + vsep + line_spacing; |
638 | } |
639 | } break; |
640 | |
641 | case NOTIFICATION_THEME_CHANGED: { |
642 | font_dirty = true; |
643 | queue_redraw(); |
644 | } break; |
645 | |
646 | case NOTIFICATION_RESIZED: { |
647 | lines_dirty = true; |
648 | } break; |
649 | } |
650 | } |
651 | |
652 | Size2 Label::get_minimum_size() const { |
653 | // don't want to mutable everything |
654 | if (dirty || font_dirty || lines_dirty) { |
655 | const_cast<Label *>(this)->_shape(); |
656 | } |
657 | |
658 | Size2 min_size = minsize; |
659 | |
660 | const Ref<Font> &font = (settings.is_valid() && settings->get_font().is_valid()) ? settings->get_font() : theme_cache.font; |
661 | int font_size = settings.is_valid() ? settings->get_font_size() : theme_cache.font_size; |
662 | |
663 | min_size.height = MAX(min_size.height, font->get_height(font_size) + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM)); |
664 | |
665 | Size2 min_style = theme_cache.normal_style->get_minimum_size(); |
666 | if (autowrap_mode != TextServer::AUTOWRAP_OFF) { |
667 | return Size2(1, (clip || overrun_behavior != TextServer::OVERRUN_NO_TRIMMING) ? 1 : min_size.height) + min_style; |
668 | } else { |
669 | if (clip || overrun_behavior != TextServer::OVERRUN_NO_TRIMMING) { |
670 | min_size.width = 1; |
671 | } |
672 | return min_size + min_style; |
673 | } |
674 | } |
675 | |
676 | int Label::get_line_count() const { |
677 | if (!is_inside_tree()) { |
678 | return 1; |
679 | } |
680 | if (dirty || font_dirty || lines_dirty) { |
681 | const_cast<Label *>(this)->_shape(); |
682 | } |
683 | |
684 | return lines_rid.size(); |
685 | } |
686 | |
687 | int Label::get_visible_line_count() const { |
688 | Ref<StyleBox> style = theme_cache.normal_style; |
689 | int line_spacing = settings.is_valid() ? settings->get_line_spacing() : theme_cache.line_spacing; |
690 | int lines_visible = 0; |
691 | float total_h = 0.0; |
692 | for (int64_t i = lines_skipped; i < lines_rid.size(); i++) { |
693 | total_h += TS->shaped_text_get_size(lines_rid[i]).y + line_spacing; |
694 | if (total_h > (get_size().height - style->get_minimum_size().height + line_spacing)) { |
695 | break; |
696 | } |
697 | lines_visible++; |
698 | } |
699 | |
700 | if (lines_visible > lines_rid.size()) { |
701 | lines_visible = lines_rid.size(); |
702 | } |
703 | |
704 | if (max_lines_visible >= 0 && lines_visible > max_lines_visible) { |
705 | lines_visible = max_lines_visible; |
706 | } |
707 | |
708 | return lines_visible; |
709 | } |
710 | |
711 | void Label::set_horizontal_alignment(HorizontalAlignment p_alignment) { |
712 | ERR_FAIL_INDEX((int)p_alignment, 4); |
713 | if (horizontal_alignment == p_alignment) { |
714 | return; |
715 | } |
716 | |
717 | if (horizontal_alignment == HORIZONTAL_ALIGNMENT_FILL || p_alignment == HORIZONTAL_ALIGNMENT_FILL) { |
718 | lines_dirty = true; // Reshape lines. |
719 | } |
720 | horizontal_alignment = p_alignment; |
721 | |
722 | queue_redraw(); |
723 | } |
724 | |
725 | HorizontalAlignment Label::get_horizontal_alignment() const { |
726 | return horizontal_alignment; |
727 | } |
728 | |
729 | void Label::set_vertical_alignment(VerticalAlignment p_alignment) { |
730 | ERR_FAIL_INDEX((int)p_alignment, 4); |
731 | |
732 | if (vertical_alignment == p_alignment) { |
733 | return; |
734 | } |
735 | |
736 | vertical_alignment = p_alignment; |
737 | queue_redraw(); |
738 | } |
739 | |
740 | VerticalAlignment Label::get_vertical_alignment() const { |
741 | return vertical_alignment; |
742 | } |
743 | |
744 | void Label::set_text(const String &p_string) { |
745 | if (text == p_string) { |
746 | return; |
747 | } |
748 | text = p_string; |
749 | xl_text = atr(p_string); |
750 | dirty = true; |
751 | if (visible_ratio < 1) { |
752 | visible_chars = get_total_character_count() * visible_ratio; |
753 | } |
754 | queue_redraw(); |
755 | update_minimum_size(); |
756 | update_configuration_warnings(); |
757 | } |
758 | |
759 | void Label::_invalidate() { |
760 | font_dirty = true; |
761 | queue_redraw(); |
762 | } |
763 | |
764 | void Label::set_label_settings(const Ref<LabelSettings> &p_settings) { |
765 | if (settings != p_settings) { |
766 | if (settings.is_valid()) { |
767 | settings->disconnect_changed(callable_mp(this, &Label::_invalidate)); |
768 | } |
769 | settings = p_settings; |
770 | if (settings.is_valid()) { |
771 | settings->connect_changed(callable_mp(this, &Label::_invalidate), CONNECT_REFERENCE_COUNTED); |
772 | } |
773 | _invalidate(); |
774 | } |
775 | } |
776 | |
777 | Ref<LabelSettings> Label::get_label_settings() const { |
778 | return settings; |
779 | } |
780 | |
781 | void Label::set_text_direction(Control::TextDirection p_text_direction) { |
782 | ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3); |
783 | if (text_direction != p_text_direction) { |
784 | text_direction = p_text_direction; |
785 | font_dirty = true; |
786 | queue_redraw(); |
787 | } |
788 | } |
789 | |
790 | void Label::set_structured_text_bidi_override(TextServer::StructuredTextParser p_parser) { |
791 | if (st_parser != p_parser) { |
792 | st_parser = p_parser; |
793 | dirty = true; |
794 | queue_redraw(); |
795 | } |
796 | } |
797 | |
798 | TextServer::StructuredTextParser Label::get_structured_text_bidi_override() const { |
799 | return st_parser; |
800 | } |
801 | |
802 | void Label::set_structured_text_bidi_override_options(Array p_args) { |
803 | if (st_args == p_args) { |
804 | return; |
805 | } |
806 | |
807 | st_args = p_args; |
808 | dirty = true; |
809 | queue_redraw(); |
810 | } |
811 | |
812 | Array Label::get_structured_text_bidi_override_options() const { |
813 | return st_args; |
814 | } |
815 | |
816 | Control::TextDirection Label::get_text_direction() const { |
817 | return text_direction; |
818 | } |
819 | |
820 | void Label::set_language(const String &p_language) { |
821 | if (language != p_language) { |
822 | language = p_language; |
823 | dirty = true; |
824 | queue_redraw(); |
825 | } |
826 | } |
827 | |
828 | String Label::get_language() const { |
829 | return language; |
830 | } |
831 | |
832 | void Label::set_clip_text(bool p_clip) { |
833 | if (clip == p_clip) { |
834 | return; |
835 | } |
836 | |
837 | clip = p_clip; |
838 | queue_redraw(); |
839 | update_minimum_size(); |
840 | } |
841 | |
842 | bool Label::is_clipping_text() const { |
843 | return clip; |
844 | } |
845 | |
846 | void Label::set_tab_stops(const PackedFloat32Array &p_tab_stops) { |
847 | if (tab_stops != p_tab_stops) { |
848 | tab_stops = p_tab_stops; |
849 | dirty = true; |
850 | queue_redraw(); |
851 | } |
852 | } |
853 | |
854 | PackedFloat32Array Label::get_tab_stops() const { |
855 | return tab_stops; |
856 | } |
857 | |
858 | void Label::set_text_overrun_behavior(TextServer::OverrunBehavior p_behavior) { |
859 | if (overrun_behavior == p_behavior) { |
860 | return; |
861 | } |
862 | |
863 | overrun_behavior = p_behavior; |
864 | lines_dirty = true; |
865 | queue_redraw(); |
866 | if (clip || overrun_behavior != TextServer::OVERRUN_NO_TRIMMING) { |
867 | update_minimum_size(); |
868 | } |
869 | } |
870 | |
871 | TextServer::OverrunBehavior Label::get_text_overrun_behavior() const { |
872 | return overrun_behavior; |
873 | } |
874 | |
875 | String Label::get_text() const { |
876 | return text; |
877 | } |
878 | |
879 | void Label::set_visible_characters(int p_amount) { |
880 | if (visible_chars != p_amount) { |
881 | visible_chars = p_amount; |
882 | if (get_total_character_count() > 0) { |
883 | visible_ratio = (float)p_amount / (float)get_total_character_count(); |
884 | } else { |
885 | visible_ratio = 1.0; |
886 | } |
887 | if (visible_chars_behavior == TextServer::VC_CHARS_BEFORE_SHAPING) { |
888 | dirty = true; |
889 | } |
890 | queue_redraw(); |
891 | } |
892 | } |
893 | |
894 | int Label::get_visible_characters() const { |
895 | return visible_chars; |
896 | } |
897 | |
898 | void Label::set_visible_ratio(float p_ratio) { |
899 | if (visible_ratio != p_ratio) { |
900 | if (p_ratio >= 1.0) { |
901 | visible_chars = -1; |
902 | visible_ratio = 1.0; |
903 | } else if (p_ratio < 0.0) { |
904 | visible_chars = 0; |
905 | visible_ratio = 0.0; |
906 | } else { |
907 | visible_chars = get_total_character_count() * p_ratio; |
908 | visible_ratio = p_ratio; |
909 | } |
910 | |
911 | if (visible_chars_behavior == TextServer::VC_CHARS_BEFORE_SHAPING) { |
912 | dirty = true; |
913 | } |
914 | queue_redraw(); |
915 | } |
916 | } |
917 | |
918 | float Label::get_visible_ratio() const { |
919 | return visible_ratio; |
920 | } |
921 | |
922 | TextServer::VisibleCharactersBehavior Label::get_visible_characters_behavior() const { |
923 | return visible_chars_behavior; |
924 | } |
925 | |
926 | void Label::set_visible_characters_behavior(TextServer::VisibleCharactersBehavior p_behavior) { |
927 | if (visible_chars_behavior != p_behavior) { |
928 | visible_chars_behavior = p_behavior; |
929 | dirty = true; |
930 | queue_redraw(); |
931 | } |
932 | } |
933 | |
934 | void Label::set_lines_skipped(int p_lines) { |
935 | ERR_FAIL_COND(p_lines < 0); |
936 | |
937 | if (lines_skipped == p_lines) { |
938 | return; |
939 | } |
940 | |
941 | lines_skipped = p_lines; |
942 | _update_visible(); |
943 | queue_redraw(); |
944 | } |
945 | |
946 | int Label::get_lines_skipped() const { |
947 | return lines_skipped; |
948 | } |
949 | |
950 | void Label::set_max_lines_visible(int p_lines) { |
951 | if (max_lines_visible == p_lines) { |
952 | return; |
953 | } |
954 | |
955 | max_lines_visible = p_lines; |
956 | _update_visible(); |
957 | queue_redraw(); |
958 | } |
959 | |
960 | int Label::get_max_lines_visible() const { |
961 | return max_lines_visible; |
962 | } |
963 | |
964 | int Label::get_total_character_count() const { |
965 | if (dirty || font_dirty || lines_dirty) { |
966 | const_cast<Label *>(this)->_shape(); |
967 | } |
968 | |
969 | return xl_text.length(); |
970 | } |
971 | |
972 | void Label::_bind_methods() { |
973 | ClassDB::bind_method(D_METHOD("set_horizontal_alignment" , "alignment" ), &Label::set_horizontal_alignment); |
974 | ClassDB::bind_method(D_METHOD("get_horizontal_alignment" ), &Label::get_horizontal_alignment); |
975 | ClassDB::bind_method(D_METHOD("set_vertical_alignment" , "alignment" ), &Label::set_vertical_alignment); |
976 | ClassDB::bind_method(D_METHOD("get_vertical_alignment" ), &Label::get_vertical_alignment); |
977 | ClassDB::bind_method(D_METHOD("set_text" , "text" ), &Label::set_text); |
978 | ClassDB::bind_method(D_METHOD("get_text" ), &Label::get_text); |
979 | ClassDB::bind_method(D_METHOD("set_label_settings" , "settings" ), &Label::set_label_settings); |
980 | ClassDB::bind_method(D_METHOD("get_label_settings" ), &Label::get_label_settings); |
981 | ClassDB::bind_method(D_METHOD("set_text_direction" , "direction" ), &Label::set_text_direction); |
982 | ClassDB::bind_method(D_METHOD("get_text_direction" ), &Label::get_text_direction); |
983 | ClassDB::bind_method(D_METHOD("set_language" , "language" ), &Label::set_language); |
984 | ClassDB::bind_method(D_METHOD("get_language" ), &Label::get_language); |
985 | ClassDB::bind_method(D_METHOD("set_autowrap_mode" , "autowrap_mode" ), &Label::set_autowrap_mode); |
986 | ClassDB::bind_method(D_METHOD("get_autowrap_mode" ), &Label::get_autowrap_mode); |
987 | ClassDB::bind_method(D_METHOD("set_justification_flags" , "justification_flags" ), &Label::set_justification_flags); |
988 | ClassDB::bind_method(D_METHOD("get_justification_flags" ), &Label::get_justification_flags); |
989 | ClassDB::bind_method(D_METHOD("set_clip_text" , "enable" ), &Label::set_clip_text); |
990 | ClassDB::bind_method(D_METHOD("is_clipping_text" ), &Label::is_clipping_text); |
991 | ClassDB::bind_method(D_METHOD("set_tab_stops" , "tab_stops" ), &Label::set_tab_stops); |
992 | ClassDB::bind_method(D_METHOD("get_tab_stops" ), &Label::get_tab_stops); |
993 | ClassDB::bind_method(D_METHOD("set_text_overrun_behavior" , "overrun_behavior" ), &Label::set_text_overrun_behavior); |
994 | ClassDB::bind_method(D_METHOD("get_text_overrun_behavior" ), &Label::get_text_overrun_behavior); |
995 | ClassDB::bind_method(D_METHOD("set_uppercase" , "enable" ), &Label::set_uppercase); |
996 | ClassDB::bind_method(D_METHOD("is_uppercase" ), &Label::is_uppercase); |
997 | ClassDB::bind_method(D_METHOD("get_line_height" , "line" ), &Label::get_line_height, DEFVAL(-1)); |
998 | ClassDB::bind_method(D_METHOD("get_line_count" ), &Label::get_line_count); |
999 | ClassDB::bind_method(D_METHOD("get_visible_line_count" ), &Label::get_visible_line_count); |
1000 | ClassDB::bind_method(D_METHOD("get_total_character_count" ), &Label::get_total_character_count); |
1001 | ClassDB::bind_method(D_METHOD("set_visible_characters" , "amount" ), &Label::set_visible_characters); |
1002 | ClassDB::bind_method(D_METHOD("get_visible_characters" ), &Label::get_visible_characters); |
1003 | ClassDB::bind_method(D_METHOD("get_visible_characters_behavior" ), &Label::get_visible_characters_behavior); |
1004 | ClassDB::bind_method(D_METHOD("set_visible_characters_behavior" , "behavior" ), &Label::set_visible_characters_behavior); |
1005 | ClassDB::bind_method(D_METHOD("set_visible_ratio" , "ratio" ), &Label::set_visible_ratio); |
1006 | ClassDB::bind_method(D_METHOD("get_visible_ratio" ), &Label::get_visible_ratio); |
1007 | ClassDB::bind_method(D_METHOD("set_lines_skipped" , "lines_skipped" ), &Label::set_lines_skipped); |
1008 | ClassDB::bind_method(D_METHOD("get_lines_skipped" ), &Label::get_lines_skipped); |
1009 | ClassDB::bind_method(D_METHOD("set_max_lines_visible" , "lines_visible" ), &Label::set_max_lines_visible); |
1010 | ClassDB::bind_method(D_METHOD("get_max_lines_visible" ), &Label::get_max_lines_visible); |
1011 | ClassDB::bind_method(D_METHOD("set_structured_text_bidi_override" , "parser" ), &Label::set_structured_text_bidi_override); |
1012 | ClassDB::bind_method(D_METHOD("get_structured_text_bidi_override" ), &Label::get_structured_text_bidi_override); |
1013 | ClassDB::bind_method(D_METHOD("set_structured_text_bidi_override_options" , "args" ), &Label::set_structured_text_bidi_override_options); |
1014 | ClassDB::bind_method(D_METHOD("get_structured_text_bidi_override_options" ), &Label::get_structured_text_bidi_override_options); |
1015 | |
1016 | ADD_PROPERTY(PropertyInfo(Variant::STRING, "text" , PROPERTY_HINT_MULTILINE_TEXT), "set_text" , "get_text" ); |
1017 | ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "label_settings" , PROPERTY_HINT_RESOURCE_TYPE, "LabelSettings" ), "set_label_settings" , "get_label_settings" ); |
1018 | ADD_PROPERTY(PropertyInfo(Variant::INT, "horizontal_alignment" , PROPERTY_HINT_ENUM, "Left,Center,Right,Fill" ), "set_horizontal_alignment" , "get_horizontal_alignment" ); |
1019 | ADD_PROPERTY(PropertyInfo(Variant::INT, "vertical_alignment" , PROPERTY_HINT_ENUM, "Top,Center,Bottom,Fill" ), "set_vertical_alignment" , "get_vertical_alignment" ); |
1020 | ADD_PROPERTY(PropertyInfo(Variant::INT, "autowrap_mode" , PROPERTY_HINT_ENUM, "Off,Arbitrary,Word,Word (Smart)" ), "set_autowrap_mode" , "get_autowrap_mode" ); |
1021 | ADD_PROPERTY(PropertyInfo(Variant::INT, "justification_flags" , PROPERTY_HINT_FLAGS, "Kashida Justification:1,Word Justification:2,Justify Only After Last Tab:8,Skip Last Line:32,Skip Last Line With Visible Characters:64,Do Not Skip Single Line:128" ), "set_justification_flags" , "get_justification_flags" ); |
1022 | |
1023 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_text" ), "set_clip_text" , "is_clipping_text" ); |
1024 | ADD_PROPERTY(PropertyInfo(Variant::INT, "text_overrun_behavior" , PROPERTY_HINT_ENUM, "Trim Nothing,Trim Characters,Trim Words,Ellipsis,Word Ellipsis" ), "set_text_overrun_behavior" , "get_text_overrun_behavior" ); |
1025 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "uppercase" ), "set_uppercase" , "is_uppercase" ); |
1026 | ADD_PROPERTY(PropertyInfo(Variant::PACKED_FLOAT32_ARRAY, "tab_stops" ), "set_tab_stops" , "get_tab_stops" ); |
1027 | |
1028 | ADD_GROUP("Displayed Text" , "" ); |
1029 | ADD_PROPERTY(PropertyInfo(Variant::INT, "lines_skipped" , PROPERTY_HINT_RANGE, "0,999,1" ), "set_lines_skipped" , "get_lines_skipped" ); |
1030 | ADD_PROPERTY(PropertyInfo(Variant::INT, "max_lines_visible" , PROPERTY_HINT_RANGE, "-1,999,1" ), "set_max_lines_visible" , "get_max_lines_visible" ); |
1031 | // Note: "visible_characters" and "visible_ratio" should be set after "text" to be correctly applied. |
1032 | ADD_PROPERTY(PropertyInfo(Variant::INT, "visible_characters" , PROPERTY_HINT_RANGE, "-1,128000,1" ), "set_visible_characters" , "get_visible_characters" ); |
1033 | ADD_PROPERTY(PropertyInfo(Variant::INT, "visible_characters_behavior" , PROPERTY_HINT_ENUM, "Characters Before Shaping,Characters After Shaping,Glyphs (Layout Direction),Glyphs (Left-to-Right),Glyphs (Right-to-Left)" ), "set_visible_characters_behavior" , "get_visible_characters_behavior" ); |
1034 | ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "visible_ratio" , PROPERTY_HINT_RANGE, "0,1,0.001" ), "set_visible_ratio" , "get_visible_ratio" ); |
1035 | |
1036 | ADD_GROUP("BiDi" , "" ); |
1037 | ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction" , PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited" ), "set_text_direction" , "get_text_direction" ); |
1038 | ADD_PROPERTY(PropertyInfo(Variant::STRING, "language" , PROPERTY_HINT_LOCALE_ID, "" ), "set_language" , "get_language" ); |
1039 | ADD_PROPERTY(PropertyInfo(Variant::INT, "structured_text_bidi_override" , PROPERTY_HINT_ENUM, "Default,URI,File,Email,List,None,Custom" ), "set_structured_text_bidi_override" , "get_structured_text_bidi_override" ); |
1040 | ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "structured_text_bidi_override_options" ), "set_structured_text_bidi_override_options" , "get_structured_text_bidi_override_options" ); |
1041 | |
1042 | BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, Label, normal_style, "normal" ); |
1043 | BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, Label, line_spacing); |
1044 | |
1045 | BIND_THEME_ITEM(Theme::DATA_TYPE_FONT, Label, font); |
1046 | BIND_THEME_ITEM(Theme::DATA_TYPE_FONT_SIZE, Label, font_size); |
1047 | BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, Label, font_color); |
1048 | BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, Label, font_shadow_color); |
1049 | BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_CONSTANT, Label, font_shadow_offset.x, "shadow_offset_x" ); |
1050 | BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_CONSTANT, Label, font_shadow_offset.y, "shadow_offset_y" ); |
1051 | BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, Label, font_outline_color); |
1052 | BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_CONSTANT, Label, font_outline_size, "outline_size" ); |
1053 | BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_CONSTANT, Label, font_shadow_outline_size, "shadow_outline_size" ); |
1054 | } |
1055 | |
1056 | Label::Label(const String &p_text) { |
1057 | text_rid = TS->create_shaped_text(); |
1058 | |
1059 | set_mouse_filter(MOUSE_FILTER_IGNORE); |
1060 | set_text(p_text); |
1061 | set_v_size_flags(SIZE_SHRINK_CENTER); |
1062 | } |
1063 | |
1064 | Label::~Label() { |
1065 | for (int i = 0; i < lines_rid.size(); i++) { |
1066 | TS->free_rid(lines_rid[i]); |
1067 | } |
1068 | lines_rid.clear(); |
1069 | TS->free_rid(text_rid); |
1070 | } |
1071 | |