diff options
Diffstat (limited to 'scene/gui/label.cpp')
-rw-r--r-- | scene/gui/label.cpp | 813 |
1 files changed, 432 insertions, 381 deletions
diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp index 3f87003423..e7f48beb00 100644 --- a/scene/gui/label.cpp +++ b/scene/gui/label.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -31,24 +31,25 @@ #include "label.h" #include "core/config/project_settings.h" +#include "core/core_string_names.h" #include "core/string/print_string.h" #include "core/string/translation.h" #include "servers/text_server.h" -void Label::set_autowrap_mode(Label::AutowrapMode p_mode) { +void Label::set_autowrap_mode(TextServer::AutowrapMode p_mode) { if (autowrap_mode != p_mode) { autowrap_mode = p_mode; lines_dirty = true; } update(); - if (clip || overrun_behavior != OVERRUN_NO_TRIMMING) { - minimum_size_changed(); + if (clip || overrun_behavior != TextServer::OVERRUN_NO_TRIMMING) { + update_minimum_size(); } } -Label::AutowrapMode Label::get_autowrap_mode() const { +TextServer::AutowrapMode Label::get_autowrap_mode() const { return autowrap_mode; } @@ -64,17 +65,18 @@ bool Label::is_uppercase() const { } int Label::get_line_height(int p_line) const { - Ref<Font> font = get_theme_font(SNAME("font")); + Ref<Font> font = (settings.is_valid() && settings->get_font().is_valid()) ? settings->get_font() : get_theme_font(SNAME("font")); if (p_line >= 0 && p_line < lines_rid.size()) { - return TS->shaped_text_get_size(lines_rid[p_line]).y + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM); + return TS->shaped_text_get_size(lines_rid[p_line]).y; } else if (lines_rid.size() > 0) { int h = 0; for (int i = 0; i < lines_rid.size(); i++) { - h = MAX(h, TS->shaped_text_get_size(lines_rid[i]).y) + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM); + h = MAX(h, TS->shaped_text_get_size(lines_rid[i]).y); } return h; } else { - return font->get_height(get_theme_font_size(SNAME("font_size"))); + int font_size = settings.is_valid() ? settings->get_font_size() : get_theme_font_size(SNAME("font_size")); + return font->get_height(font_size); } } @@ -82,46 +84,63 @@ void Label::_shape() { Ref<StyleBox> style = get_theme_stylebox(SNAME("normal"), SNAME("Label")); int width = (get_size().width - style->get_minimum_size().width); - if (dirty) { - TS->shaped_text_clear(text_rid); + if (dirty || font_dirty) { + if (dirty) { + TS->shaped_text_clear(text_rid); + } if (text_direction == Control::TEXT_DIRECTION_INHERITED) { TS->shaped_text_set_direction(text_rid, is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR); } else { TS->shaped_text_set_direction(text_rid, (TextServer::Direction)text_direction); } - const Ref<Font> &font = get_theme_font(SNAME("font")); - int font_size = get_theme_font_size(SNAME("font_size")); + const Ref<Font> &font = (settings.is_valid() && settings->get_font().is_valid()) ? settings->get_font() : get_theme_font(SNAME("font")); + int font_size = settings.is_valid() ? settings->get_font_size() : get_theme_font_size(SNAME("font_size")); ERR_FAIL_COND(font.is_null()); - TS->shaped_text_add_string(text_rid, (uppercase) ? xl_text.to_upper() : xl_text, font->get_rids(), font_size, opentype_features, (language != "") ? language : TranslationServer::get_singleton()->get_tool_locale()); - TS->shaped_text_set_bidi_override(text_rid, structured_text_parser(st_parser, st_args, xl_text)); + String text = (uppercase) ? TS->string_to_upper(xl_text, language) : xl_text; + if (visible_chars >= 0 && visible_chars_behavior == TextServer::VC_CHARS_BEFORE_SHAPING) { + text = text.substr(0, visible_chars); + } + if (dirty) { + TS->shaped_text_add_string(text_rid, text, font->get_rids(), font_size, font->get_opentype_features(), language); + } else { + int spans = TS->shaped_get_span_count(text_rid); + for (int i = 0; i < spans; i++) { + TS->shaped_set_span_update_font(text_rid, i, font->get_rids(), font_size, font->get_opentype_features()); + } + } + for (int i = 0; i < TextServer::SPACING_MAX; i++) { + TS->shaped_text_set_spacing(text_rid, TextServer::SpacingType(i), font->get_spacing(TextServer::SpacingType(i))); + } + TS->shaped_text_set_bidi_override(text_rid, structured_text_parser(st_parser, st_args, text)); dirty = false; + font_dirty = false; lines_dirty = true; } if (lines_dirty) { for (int i = 0; i < lines_rid.size(); i++) { - TS->free(lines_rid[i]); + TS->free_rid(lines_rid[i]); } lines_rid.clear(); - uint8_t autowrap_flags = TextServer::BREAK_MANDATORY; + BitField<TextServer::LineBreakFlag> autowrap_flags = TextServer::BREAK_MANDATORY; switch (autowrap_mode) { - case AUTOWRAP_WORD_SMART: - autowrap_flags = TextServer::BREAK_WORD_BOUND_ADAPTIVE | TextServer::BREAK_MANDATORY; + case TextServer::AUTOWRAP_WORD_SMART: + autowrap_flags = TextServer::BREAK_WORD_BOUND | TextServer::BREAK_ADAPTIVE | TextServer::BREAK_MANDATORY; break; - case AUTOWRAP_WORD: + case TextServer::AUTOWRAP_WORD: autowrap_flags = TextServer::BREAK_WORD_BOUND | TextServer::BREAK_MANDATORY; break; - case AUTOWRAP_ARBITRARY: + case TextServer::AUTOWRAP_ARBITRARY: autowrap_flags = TextServer::BREAK_GRAPHEME_BOUND | TextServer::BREAK_MANDATORY; break; - case AUTOWRAP_OFF: + case TextServer::AUTOWRAP_OFF: break; } - Vector<Vector2i> line_breaks = TS->shaped_text_get_line_breaks(text_rid, width, 0, autowrap_flags); + PackedInt32Array line_breaks = TS->shaped_text_get_line_breaks(text_rid, width, 0, autowrap_flags); - for (int i = 0; i < line_breaks.size(); i++) { - RID line = TS->shaped_text_substr(text_rid, line_breaks[i].x, line_breaks[i].y - line_breaks[i].x); + for (int i = 0; i < line_breaks.size(); i = i + 2) { + RID line = TS->shaped_text_substr(text_rid, line_breaks[i], line_breaks[i + 1] - line_breaks[i]); lines_rid.push_back(line); } } @@ -131,7 +150,7 @@ void Label::_shape() { return; } - if (autowrap_mode == AUTOWRAP_OFF) { + if (autowrap_mode == TextServer::AUTOWRAP_OFF) { minsize.width = 0.0f; for (int i = 0; i < lines_rid.size(); i++) { if (minsize.width < TS->shaped_text_get_size(lines_rid[i]).x) { @@ -141,37 +160,37 @@ void Label::_shape() { } if (lines_dirty) { - uint8_t overrun_flags = TextServer::OVERRUN_NO_TRIMMING; + BitField<TextServer::TextOverrunFlag> overrun_flags = TextServer::OVERRUN_NO_TRIM; switch (overrun_behavior) { - case OVERRUN_TRIM_WORD_ELLIPSIS: - overrun_flags |= TextServer::OVERRUN_TRIM; - overrun_flags |= TextServer::OVERRUN_TRIM_WORD_ONLY; - overrun_flags |= TextServer::OVERRUN_ADD_ELLIPSIS; + case TextServer::OVERRUN_TRIM_WORD_ELLIPSIS: + overrun_flags.set_flag(TextServer::OVERRUN_TRIM); + overrun_flags.set_flag(TextServer::OVERRUN_TRIM_WORD_ONLY); + overrun_flags.set_flag(TextServer::OVERRUN_ADD_ELLIPSIS); break; - case OVERRUN_TRIM_ELLIPSIS: - overrun_flags |= TextServer::OVERRUN_TRIM; - overrun_flags |= TextServer::OVERRUN_ADD_ELLIPSIS; + case TextServer::OVERRUN_TRIM_ELLIPSIS: + overrun_flags.set_flag(TextServer::OVERRUN_TRIM); + overrun_flags.set_flag(TextServer::OVERRUN_ADD_ELLIPSIS); break; - case OVERRUN_TRIM_WORD: - overrun_flags |= TextServer::OVERRUN_TRIM; - overrun_flags |= TextServer::OVERRUN_TRIM_WORD_ONLY; + case TextServer::OVERRUN_TRIM_WORD: + overrun_flags.set_flag(TextServer::OVERRUN_TRIM); + overrun_flags.set_flag(TextServer::OVERRUN_TRIM_WORD_ONLY); break; - case OVERRUN_TRIM_CHAR: - overrun_flags |= TextServer::OVERRUN_TRIM; + case TextServer::OVERRUN_TRIM_CHAR: + overrun_flags.set_flag(TextServer::OVERRUN_TRIM); break; - case OVERRUN_NO_TRIMMING: + case TextServer::OVERRUN_NO_TRIMMING: break; } // Fill after min_size calculation. - if (autowrap_mode != AUTOWRAP_OFF) { + if (autowrap_mode != TextServer::AUTOWRAP_OFF) { int visible_lines = get_visible_line_count(); bool lines_hidden = visible_lines > 0 && visible_lines < lines_rid.size(); if (lines_hidden) { - overrun_flags |= TextServer::OVERRUN_ENFORCE_ELLIPSIS; + overrun_flags.set_flag(TextServer::OVERRUN_ENFORCE_ELLIPSIS); } - if (align == ALIGN_FILL) { + if (horizontal_alignment == HORIZONTAL_ALIGNMENT_FILL) { for (int i = 0; i < lines_rid.size(); i++) { if (i < visible_lines - 1 || lines_rid.size() == 1) { TS->shaped_text_fit_to_width(lines_rid[i], width); @@ -179,17 +198,15 @@ void Label::_shape() { TS->shaped_text_overrun_trim_to_width(lines_rid[visible_lines - 1], width, overrun_flags); } } - } else if (lines_hidden) { TS->shaped_text_overrun_trim_to_width(lines_rid[visible_lines - 1], width, overrun_flags); } - } else { // Autowrap disabled. for (int i = 0; i < lines_rid.size(); i++) { - if (align == ALIGN_FILL) { + if (horizontal_alignment == HORIZONTAL_ALIGNMENT_FILL) { TS->shaped_text_fit_to_width(lines_rid[i], width); - overrun_flags |= TextServer::OVERRUN_JUSTIFICATION_AWARE; + overrun_flags.set_flag(TextServer::OVERRUN_JUSTIFICATION_AWARE); TS->shaped_text_overrun_trim_to_width(lines_rid[i], width, overrun_flags); TS->shaped_text_fit_to_width(lines_rid[i], width, TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_CONSTRAIN_ELLIPSIS); } else { @@ -202,15 +219,14 @@ void Label::_shape() { _update_visible(); - if (autowrap_mode == AUTOWRAP_OFF || !clip || overrun_behavior == OVERRUN_NO_TRIMMING) { - minimum_size_changed(); + if (autowrap_mode == TextServer::AUTOWRAP_OFF || !clip || overrun_behavior == TextServer::OVERRUN_NO_TRIMMING) { + update_minimum_size(); } } void Label::_update_visible() { - int line_spacing = get_theme_constant(SNAME("line_spacing"), SNAME("Label")); + int line_spacing = settings.is_valid() ? settings->get_line_spacing() : get_theme_constant(SNAME("line_spacing"), SNAME("Label")); Ref<StyleBox> style = get_theme_stylebox(SNAME("normal"), SNAME("Label")); - Ref<Font> font = get_theme_font(SNAME("font")); int lines_visible = lines_rid.size(); if (max_lines_visible >= 0 && lines_visible > max_lines_visible) { @@ -220,251 +236,335 @@ void Label::_update_visible() { minsize.height = 0; int last_line = MIN(lines_rid.size(), lines_visible + lines_skipped); for (int64_t i = lines_skipped; i < last_line; i++) { - minsize.height += TS->shaped_text_get_size(lines_rid[i]).y + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM) + line_spacing; + minsize.height += TS->shaped_text_get_size(lines_rid[i]).y + line_spacing; if (minsize.height > (get_size().height - style->get_minimum_size().height + line_spacing)) { break; } } } -inline void draw_glyph(const TextServer::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) { +inline void draw_glyph(const Glyph &p_gl, const RID &p_canvas, const Color &p_font_color, const Vector2 &p_ofs) { + if (p_gl.font_rid != RID()) { + 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); + } else { + 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); + } +} + +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) { if (p_gl.font_rid != RID()) { if (p_font_shadow_color.a > 0) { 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); - if (p_shadow_outline_size > 0) { - 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) + Vector2(-shadow_ofs.x, shadow_ofs.y), p_gl.index, p_font_shadow_color); - 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) + Vector2(shadow_ofs.x, -shadow_ofs.y), p_gl.index, p_font_shadow_color); - 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) + Vector2(-shadow_ofs.x, -shadow_ofs.y), p_gl.index, p_font_shadow_color); - } + } + if (p_font_shadow_color.a > 0 && p_shadow_outline_size > 0) { + 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); } if (p_font_outline_color.a != 0.0 && p_outline_size > 0) { 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); } - 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); - } else { - 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); } } void Label::_notification(int p_what) { - if (p_what == NOTIFICATION_TRANSLATION_CHANGED) { - String new_text = atr(text); - if (new_text == xl_text) { - return; // Nothing new. - } - xl_text = new_text; - dirty = true; + switch (p_what) { + case NOTIFICATION_TRANSLATION_CHANGED: { + String new_text = atr(text); + if (new_text == xl_text) { + return; // Nothing new. + } + xl_text = new_text; + if (percent_visible < 1) { + visible_chars = get_total_character_count() * percent_visible; + } + dirty = true; - update(); - } + update(); + } break; - if (p_what == NOTIFICATION_DRAW) { - if (clip) { - RenderingServer::get_singleton()->canvas_item_set_clip(get_canvas_item(), true); - } + case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: { + update(); + } break; - if (dirty || lines_dirty) { - _shape(); - } + case NOTIFICATION_DRAW: { + if (clip) { + RenderingServer::get_singleton()->canvas_item_set_clip(get_canvas_item(), true); + } - RID ci = get_canvas_item(); - - Size2 string_size; - Size2 size = get_size(); - Ref<StyleBox> style = get_theme_stylebox(SNAME("normal")); - Ref<Font> font = get_theme_font(SNAME("font")); - Color font_color = get_theme_color(SNAME("font_color")); - Color font_shadow_color = get_theme_color(SNAME("font_shadow_color")); - Point2 shadow_ofs(get_theme_constant(SNAME("shadow_offset_x")), get_theme_constant(SNAME("shadow_offset_y"))); - int line_spacing = get_theme_constant(SNAME("line_spacing")); - Color font_outline_color = get_theme_color(SNAME("font_outline_color")); - int outline_size = get_theme_constant(SNAME("outline_size")); - int shadow_outline_size = get_theme_constant(SNAME("shadow_outline_size")); - bool rtl = TS->shaped_text_get_direction(text_rid); - - style->draw(ci, Rect2(Point2(0, 0), get_size())); - - float total_h = 0.0; - int lines_visible = 0; - - // Get number of lines to fit to the height. - for (int64_t i = lines_skipped; i < lines_rid.size(); i++) { - total_h += TS->shaped_text_get_size(lines_rid[i]).y + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM) + line_spacing; - if (total_h > (get_size().height - style->get_minimum_size().height + line_spacing)) { - break; + if (dirty || font_dirty || lines_dirty) { + _shape(); } - lines_visible++; - } - if (max_lines_visible >= 0 && lines_visible > max_lines_visible) { - lines_visible = max_lines_visible; - } + RID ci = get_canvas_item(); + + bool has_settings = settings.is_valid(); + + Size2 string_size; + Size2 size = get_size(); + Ref<StyleBox> style = get_theme_stylebox(SNAME("normal")); + Ref<Font> font = (has_settings && settings->get_font().is_valid()) ? settings->get_font() : get_theme_font(SNAME("font")); + Color font_color = has_settings ? settings->get_font_color() : get_theme_color(SNAME("font_color")); + Color font_shadow_color = has_settings ? settings->get_shadow_color() : get_theme_color(SNAME("font_shadow_color")); + Point2 shadow_ofs = has_settings ? settings->get_shadow_offset() : Point2(get_theme_constant(SNAME("shadow_offset_x")), get_theme_constant(SNAME("shadow_offset_y"))); + int line_spacing = has_settings ? settings->get_line_spacing() : get_theme_constant(SNAME("line_spacing")); + Color font_outline_color = has_settings ? settings->get_outline_color() : get_theme_color(SNAME("font_outline_color")); + int outline_size = has_settings ? settings->get_outline_size() : get_theme_constant(SNAME("outline_size")); + int shadow_outline_size = has_settings ? settings->get_shadow_size() : get_theme_constant(SNAME("shadow_outline_size")); + bool rtl = (TS->shaped_text_get_inferred_direction(text_rid) == TextServer::DIRECTION_RTL); + bool rtl_layout = is_layout_rtl(); + + style->draw(ci, Rect2(Point2(0, 0), get_size())); + + float total_h = 0.0; + int lines_visible = 0; + + // Get number of lines to fit to the height. + for (int64_t i = lines_skipped; i < lines_rid.size(); i++) { + total_h += TS->shaped_text_get_size(lines_rid[i]).y + line_spacing; + if (total_h > (get_size().height - style->get_minimum_size().height + line_spacing)) { + break; + } + lines_visible++; + } - int last_line = MIN(lines_rid.size(), lines_visible + lines_skipped); + if (max_lines_visible >= 0 && lines_visible > max_lines_visible) { + lines_visible = max_lines_visible; + } - // Get real total height. - total_h = 0; - for (int64_t i = lines_skipped; i < last_line; i++) { - total_h += TS->shaped_text_get_size(lines_rid[i]).y + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM) + line_spacing; - } - total_h += style->get_margin(SIDE_TOP) + style->get_margin(SIDE_BOTTOM); - - int vbegin = 0, vsep = 0; - if (lines_visible > 0) { - switch (valign) { - case VALIGN_TOP: { - // Nothing. - } break; - case VALIGN_CENTER: { - vbegin = (size.y - (total_h - line_spacing)) / 2; - vsep = 0; - - } break; - case VALIGN_BOTTOM: { - vbegin = size.y - (total_h - line_spacing); - vsep = 0; - - } break; - case VALIGN_FILL: { - vbegin = 0; - if (lines_visible > 1) { - vsep = (size.y - (total_h - line_spacing)) / (lines_visible - 1); - } else { - vsep = 0; - } + int last_line = MIN(lines_rid.size(), lines_visible + lines_skipped); + bool trim_chars = (visible_chars >= 0) && (visible_chars_behavior == TextServer::VC_CHARS_AFTER_SHAPING); + bool trim_glyphs_ltr = (visible_chars >= 0) && ((visible_chars_behavior == TextServer::VC_GLYPHS_LTR) || ((visible_chars_behavior == TextServer::VC_GLYPHS_AUTO) && !rtl_layout)); + bool trim_glyphs_rtl = (visible_chars >= 0) && ((visible_chars_behavior == TextServer::VC_GLYPHS_RTL) || ((visible_chars_behavior == TextServer::VC_GLYPHS_AUTO) && rtl_layout)); - } break; + // Get real total height. + int total_glyphs = 0; + total_h = 0; + for (int64_t i = lines_skipped; i < last_line; i++) { + total_h += TS->shaped_text_get_size(lines_rid[i]).y + line_spacing; + total_glyphs += TS->shaped_text_get_glyph_count(lines_rid[i]) + TS->shaped_text_get_ellipsis_glyph_count(lines_rid[i]); } - } + int visible_glyphs = total_glyphs * percent_visible; + int processed_glyphs = 0; + total_h += style->get_margin(SIDE_TOP) + style->get_margin(SIDE_BOTTOM); + + int vbegin = 0, vsep = 0; + if (lines_visible > 0) { + switch (vertical_alignment) { + case VERTICAL_ALIGNMENT_TOP: { + // Nothing. + } break; + case VERTICAL_ALIGNMENT_CENTER: { + vbegin = (size.y - (total_h - line_spacing)) / 2; + vsep = 0; - int visible_glyphs = -1; - int glyhps_drawn = 0; - if (percent_visible < 1) { - int total_glyphs = 0; - for (int i = lines_skipped; i < last_line; i++) { - const Vector<TextServer::Glyph> visual = TS->shaped_text_get_glyphs(lines_rid[i]); - const TextServer::Glyph *glyphs = visual.ptr(); - int gl_size = visual.size(); - for (int j = 0; j < gl_size; j++) { - if ((glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) { - total_glyphs++; - } + } break; + case VERTICAL_ALIGNMENT_BOTTOM: { + vbegin = size.y - (total_h - line_spacing); + vsep = 0; + + } break; + case VERTICAL_ALIGNMENT_FILL: { + vbegin = 0; + if (lines_visible > 1) { + vsep = (size.y - (total_h - line_spacing)) / (lines_visible - 1); + } else { + vsep = 0; + } + + } break; } } - visible_glyphs = MIN(total_glyphs, visible_chars); - } + Vector2 ofs; + ofs.y = style->get_offset().y + vbegin; + for (int i = lines_skipped; i < last_line; i++) { + Size2 line_size = TS->shaped_text_get_size(lines_rid[i]); + ofs.x = 0; + ofs.y += TS->shaped_text_get_ascent(lines_rid[i]); + switch (horizontal_alignment) { + case HORIZONTAL_ALIGNMENT_FILL: + if (rtl && autowrap_mode != TextServer::AUTOWRAP_OFF) { + ofs.x = int(size.width - style->get_margin(SIDE_RIGHT) - line_size.width); + } else { + ofs.x = style->get_offset().x; + } + break; + case HORIZONTAL_ALIGNMENT_LEFT: { + if (rtl_layout) { + ofs.x = int(size.width - style->get_margin(SIDE_RIGHT) - line_size.width); + } else { + ofs.x = style->get_offset().x; + } + } break; + case HORIZONTAL_ALIGNMENT_CENTER: { + ofs.x = int(size.width - line_size.width) / 2; + } break; + case HORIZONTAL_ALIGNMENT_RIGHT: { + if (rtl_layout) { + ofs.x = style->get_offset().x; + } else { + ofs.x = int(size.width - style->get_margin(SIDE_RIGHT) - line_size.width); + } + } break; + } - Vector2 ofs; - ofs.y = style->get_offset().y + vbegin; - for (int i = lines_skipped; i < last_line; i++) { - Size2 line_size = TS->shaped_text_get_size(lines_rid[i]); - ofs.x = 0; - ofs.y += TS->shaped_text_get_ascent(lines_rid[i]) + font->get_spacing(TextServer::SPACING_TOP); - switch (align) { - case ALIGN_FILL: - if (rtl && autowrap_mode != AUTOWRAP_OFF) { - ofs.x = int(size.width - style->get_margin(SIDE_RIGHT) - line_size.width); - } else { - ofs.x = style->get_offset().x; + const Glyph *glyphs = TS->shaped_text_get_glyphs(lines_rid[i]); + int gl_size = TS->shaped_text_get_glyph_count(lines_rid[i]); + + int ellipsis_pos = TS->shaped_text_get_ellipsis_pos(lines_rid[i]); + int trim_pos = TS->shaped_text_get_trim_pos(lines_rid[i]); + + const Glyph *ellipsis_glyphs = TS->shaped_text_get_ellipsis_glyphs(lines_rid[i]); + int ellipsis_gl_size = TS->shaped_text_get_ellipsis_glyph_count(lines_rid[i]); + + // Draw outline. Note: Do not merge this into the single loop with the main text, to prevent overlaps. + int processed_glyphs_ol = processed_glyphs; + if ((outline_size > 0 && font_outline_color.a != 0) || (font_shadow_color.a != 0)) { + Vector2 offset = ofs; + // Draw RTL ellipsis string when necessary. + if (rtl && ellipsis_pos >= 0) { + for (int gl_idx = ellipsis_gl_size - 1; gl_idx >= 0; gl_idx--) { + for (int j = 0; j < ellipsis_glyphs[gl_idx].repeat; j++) { + 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)); + //Draw glyph outlines and shadow. + if (!skip) { + draw_glyph_outline(ellipsis_glyphs[gl_idx], ci, font_color, font_shadow_color, font_outline_color, shadow_outline_size, outline_size, offset, shadow_ofs); + } + processed_glyphs_ol++; + offset.x += ellipsis_glyphs[gl_idx].advance; + } + } } - break; - case ALIGN_LEFT: { - ofs.x = style->get_offset().x; - } break; - case ALIGN_CENTER: { - ofs.x = int(size.width - line_size.width) / 2; - } break; - case ALIGN_RIGHT: { - ofs.x = int(size.width - style->get_margin(SIDE_RIGHT) - line_size.width); - } break; - } - const Vector<TextServer::Glyph> visual = TS->shaped_text_get_glyphs(lines_rid[i]); - const TextServer::Glyph *glyphs = visual.ptr(); - int gl_size = visual.size(); - TextServer::TrimData trim_data = TS->shaped_text_get_trim_data(lines_rid[i]); - - // Draw RTL ellipsis string when necessary. - if (rtl && trim_data.ellipsis_pos >= 0) { - for (int gl_idx = trim_data.ellipsis_glyph_buf.size() - 1; gl_idx >= 0; gl_idx--) { - for (int j = 0; j < trim_data.ellipsis_glyph_buf[gl_idx].repeat; j++) { - //Draw glyph outlines and shadow. - draw_glyph(trim_data.ellipsis_glyph_buf[gl_idx], ci, font_color, font_shadow_color, font_outline_color, shadow_outline_size, outline_size, ofs, shadow_ofs); - ofs.x += trim_data.ellipsis_glyph_buf[gl_idx].advance; + // Draw main text. + for (int j = 0; j < gl_size; j++) { + // Trim when necessary. + if (trim_pos >= 0) { + if (rtl) { + if (j < trim_pos) { + continue; + } + } else { + if (j >= trim_pos) { + break; + } + } + } + for (int k = 0; k < glyphs[j].repeat; k++) { + 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)); + + // Draw glyph outlines and shadow. + if (!skip) { + draw_glyph_outline(glyphs[j], ci, font_color, font_shadow_color, font_outline_color, shadow_outline_size, outline_size, offset, shadow_ofs); + } + processed_glyphs_ol++; + offset.x += glyphs[j].advance; + } + } + // Draw LTR ellipsis string when necessary. + if (!rtl && ellipsis_pos >= 0) { + for (int gl_idx = 0; gl_idx < ellipsis_gl_size; gl_idx++) { + for (int j = 0; j < ellipsis_glyphs[gl_idx].repeat; j++) { + 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)); + //Draw glyph outlines and shadow. + if (!skip) { + draw_glyph_outline(ellipsis_glyphs[gl_idx], ci, font_color, font_shadow_color, font_outline_color, shadow_outline_size, outline_size, offset, shadow_ofs); + } + processed_glyphs_ol++; + offset.x += ellipsis_glyphs[gl_idx].advance; + } + } } } - } - // Draw main text. - for (int j = 0; j < gl_size; j++) { - for (int k = 0; k < glyphs[j].repeat; k++) { - if (visible_glyphs != -1) { - if ((glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) { - if (glyhps_drawn >= visible_glyphs) { - return; + // Draw main text. Note: Do not merge this into the single loop with the outline, to prevent overlaps. + + // Draw RTL ellipsis string when necessary. + if (rtl && ellipsis_pos >= 0) { + for (int gl_idx = ellipsis_gl_size - 1; gl_idx >= 0; gl_idx--) { + for (int j = 0; j < ellipsis_glyphs[gl_idx].repeat; j++) { + 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)); + //Draw glyph outlines and shadow. + if (!skip) { + draw_glyph(ellipsis_glyphs[gl_idx], ci, font_color, ofs); } + processed_glyphs++; + ofs.x += ellipsis_glyphs[gl_idx].advance; } } + } + // Draw main text. + for (int j = 0; j < gl_size; j++) { // Trim when necessary. - if (trim_data.trim_pos >= 0) { + if (trim_pos >= 0) { if (rtl) { - if (j < trim_data.trim_pos && (glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) { + if (j < trim_pos) { continue; } } else { - if (j >= trim_data.trim_pos && (glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) { + if (j >= trim_pos) { break; } } } + for (int k = 0; k < glyphs[j].repeat; k++) { + 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)); - // Draw glyph outlines and shadow. - draw_glyph(glyphs[j], ci, font_color, font_shadow_color, font_outline_color, shadow_outline_size, outline_size, ofs, shadow_ofs); - ofs.x += glyphs[j].advance; - glyhps_drawn++; + // Draw glyph outlines and shadow. + if (!skip) { + draw_glyph(glyphs[j], ci, font_color, ofs); + } + processed_glyphs++; + ofs.x += glyphs[j].advance; + } } - } - // Draw LTR ellipsis string when necessary. - if (!rtl && trim_data.ellipsis_pos >= 0) { - for (int gl_idx = 0; gl_idx < trim_data.ellipsis_glyph_buf.size(); gl_idx++) { - for (int j = 0; j < trim_data.ellipsis_glyph_buf[gl_idx].repeat; j++) { - //Draw glyph outlines and shadow. - draw_glyph(trim_data.ellipsis_glyph_buf[gl_idx], ci, font_color, font_shadow_color, font_outline_color, shadow_outline_size, outline_size, ofs, shadow_ofs); - ofs.x += trim_data.ellipsis_glyph_buf[gl_idx].advance; + // Draw LTR ellipsis string when necessary. + if (!rtl && ellipsis_pos >= 0) { + for (int gl_idx = 0; gl_idx < ellipsis_gl_size; gl_idx++) { + for (int j = 0; j < ellipsis_glyphs[gl_idx].repeat; j++) { + 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)); + //Draw glyph outlines and shadow. + if (!skip) { + draw_glyph(ellipsis_glyphs[gl_idx], ci, font_color, ofs); + } + processed_glyphs++; + ofs.x += ellipsis_glyphs[gl_idx].advance; + } } } + ofs.y += TS->shaped_text_get_descent(lines_rid[i]) + vsep + line_spacing; } - ofs.y += TS->shaped_text_get_descent(lines_rid[i]) + vsep + line_spacing + font->get_spacing(TextServer::SPACING_BOTTOM); - } - } + } break; - if (p_what == NOTIFICATION_THEME_CHANGED) { - dirty = true; - update(); - } - if (p_what == NOTIFICATION_RESIZED) { - lines_dirty = true; + case NOTIFICATION_THEME_CHANGED: { + font_dirty = true; + update(); + } break; + + case NOTIFICATION_RESIZED: { + lines_dirty = true; + } break; } } Size2 Label::get_minimum_size() const { // don't want to mutable everything - if (dirty || lines_dirty) { + if (dirty || font_dirty || lines_dirty) { const_cast<Label *>(this)->_shape(); } Size2 min_size = minsize; - Ref<Font> font = get_theme_font(SNAME("font")); - min_size.height = MAX(min_size.height, font->get_height(get_theme_font_size(SNAME("font_size"))) + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM)); + const Ref<Font> &font = (settings.is_valid() && settings->get_font().is_valid()) ? settings->get_font() : get_theme_font(SNAME("font")); + int font_size = settings.is_valid() ? settings->get_font_size() : get_theme_font_size(SNAME("font_size")); + + min_size.height = MAX(min_size.height, font->get_height(font_size) + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM)); Size2 min_style = get_theme_stylebox(SNAME("normal"))->get_minimum_size(); - if (autowrap_mode != AUTOWRAP_OFF) { - return Size2(1, (clip || overrun_behavior != OVERRUN_NO_TRIMMING) ? 1 : min_size.height) + min_style; + if (autowrap_mode != TextServer::AUTOWRAP_OFF) { + return Size2(1, (clip || overrun_behavior != TextServer::OVERRUN_NO_TRIMMING) ? 1 : min_size.height) + min_style; } else { - if (clip || overrun_behavior != OVERRUN_NO_TRIMMING) { + if (clip || overrun_behavior != TextServer::OVERRUN_NO_TRIMMING) { min_size.width = 1; } return min_size + min_style; @@ -475,7 +575,7 @@ int Label::get_line_count() const { if (!is_inside_tree()) { return 1; } - if (dirty || lines_dirty) { + if (dirty || font_dirty || lines_dirty) { const_cast<Label *>(this)->_shape(); } @@ -483,13 +583,12 @@ int Label::get_line_count() const { } int Label::get_visible_line_count() const { - Ref<Font> font = get_theme_font(SNAME("font")); Ref<StyleBox> style = get_theme_stylebox(SNAME("normal")); - int line_spacing = get_theme_constant(SNAME("line_spacing")); + int line_spacing = settings.is_valid() ? settings->get_line_spacing() : get_theme_constant(SNAME("line_spacing")); int lines_visible = 0; float total_h = 0.0; for (int64_t i = lines_skipped; i < lines_rid.size(); i++) { - total_h += TS->shaped_text_get_size(lines_rid[i]).y + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM) + line_spacing; + total_h += TS->shaped_text_get_size(lines_rid[i]).y + line_spacing; if (total_h > (get_size().height - style->get_minimum_size().height + line_spacing)) { break; } @@ -507,29 +606,29 @@ int Label::get_visible_line_count() const { return lines_visible; } -void Label::set_align(Align p_align) { - ERR_FAIL_INDEX((int)p_align, 4); - if (align != p_align) { - if (align == ALIGN_FILL || p_align == ALIGN_FILL) { +void Label::set_horizontal_alignment(HorizontalAlignment p_alignment) { + ERR_FAIL_INDEX((int)p_alignment, 4); + if (horizontal_alignment != p_alignment) { + if (horizontal_alignment == HORIZONTAL_ALIGNMENT_FILL || p_alignment == HORIZONTAL_ALIGNMENT_FILL) { lines_dirty = true; // Reshape lines. } - align = p_align; + horizontal_alignment = p_alignment; } update(); } -Label::Align Label::get_align() const { - return align; +HorizontalAlignment Label::get_horizontal_alignment() const { + return horizontal_alignment; } -void Label::set_valign(VAlign p_align) { - ERR_FAIL_INDEX((int)p_align, 4); - valign = p_align; +void Label::set_vertical_alignment(VerticalAlignment p_alignment) { + ERR_FAIL_INDEX((int)p_alignment, 4); + vertical_alignment = p_alignment; update(); } -Label::VAlign Label::get_valign() const { - return valign; +VerticalAlignment Label::get_vertical_alignment() const { + return vertical_alignment; } void Label::set_text(const String &p_string) { @@ -543,19 +642,41 @@ void Label::set_text(const String &p_string) { visible_chars = get_total_character_count() * percent_visible; } update(); - minimum_size_changed(); + update_minimum_size(); +} + +void Label::_invalidate() { + font_dirty = true; + update(); +} + +void Label::set_label_settings(const Ref<LabelSettings> &p_settings) { + if (settings != p_settings) { + if (settings.is_valid()) { + settings->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Label::_invalidate)); + } + settings = p_settings; + if (settings.is_valid()) { + settings->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Label::_invalidate), CONNECT_REFERENCE_COUNTED); + } + _invalidate(); + } +} + +Ref<LabelSettings> Label::get_label_settings() const { + return settings; } void Label::set_text_direction(Control::TextDirection p_text_direction) { ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3); if (text_direction != p_text_direction) { text_direction = p_text_direction; - dirty = true; + font_dirty = true; update(); } } -void Label::set_structured_text_bidi_override(Control::StructuredTextParser p_parser) { +void Label::set_structured_text_bidi_override(TextServer::StructuredTextParser p_parser) { if (st_parser != p_parser) { st_parser = p_parser; dirty = true; @@ -563,7 +684,7 @@ void Label::set_structured_text_bidi_override(Control::StructuredTextParser p_pa } } -Control::StructuredTextParser Label::get_structured_text_bidi_override() const { +TextServer::StructuredTextParser Label::get_structured_text_bidi_override() const { return st_parser; } @@ -581,29 +702,6 @@ Control::TextDirection Label::get_text_direction() const { return text_direction; } -void Label::clear_opentype_features() { - opentype_features.clear(); - dirty = true; - update(); -} - -void Label::set_opentype_feature(const String &p_name, int p_value) { - int32_t tag = TS->name_to_tag(p_name); - if (!opentype_features.has(tag) || (int)opentype_features[tag] != p_value) { - opentype_features[tag] = p_value; - dirty = true; - update(); - } -} - -int Label::get_opentype_feature(const String &p_name) const { - int32_t tag = TS->name_to_tag(p_name); - if (!opentype_features.has(tag)) { - return -1; - } - return opentype_features[tag]; -} - void Label::set_language(const String &p_language) { if (language != p_language) { language = p_language; @@ -619,25 +717,25 @@ String Label::get_language() const { void Label::set_clip_text(bool p_clip) { clip = p_clip; update(); - minimum_size_changed(); + update_minimum_size(); } bool Label::is_clipping_text() const { return clip; } -void Label::set_text_overrun_behavior(Label::OverrunBehavior p_behavior) { +void Label::set_text_overrun_behavior(TextServer::OverrunBehavior p_behavior) { if (overrun_behavior != p_behavior) { overrun_behavior = p_behavior; lines_dirty = true; } update(); - if (clip || overrun_behavior != OVERRUN_NO_TRIMMING) { - minimum_size_changed(); + if (clip || overrun_behavior != TextServer::OVERRUN_NO_TRIMMING) { + update_minimum_size(); } } -Label::OverrunBehavior Label::get_text_overrun_behavior() const { +TextServer::OverrunBehavior Label::get_text_overrun_behavior() const { return overrun_behavior; } @@ -646,16 +744,18 @@ String Label::get_text() const { } void Label::set_visible_characters(int p_amount) { - visible_chars = p_amount; - if (get_total_character_count() > 0) { - percent_visible = (float)p_amount / (float)get_total_character_count(); - } else { - percent_visible = 1.0; - } - if (p_amount == -1) { - lines_dirty = true; + if (visible_chars != p_amount) { + visible_chars = p_amount; + if (get_total_character_count() > 0) { + percent_visible = (float)p_amount / (float)get_total_character_count(); + } else { + percent_visible = 1.0; + } + if (visible_chars_behavior == TextServer::VC_CHARS_BEFORE_SHAPING) { + dirty = true; + } + update(); } - update(); } int Label::get_visible_characters() const { @@ -663,21 +763,37 @@ int Label::get_visible_characters() const { } void Label::set_percent_visible(float p_percent) { - if (p_percent < 0 || p_percent >= 1) { - visible_chars = -1; - percent_visible = 1; - lines_dirty = true; - } else { - visible_chars = get_total_character_count() * p_percent; - percent_visible = p_percent; + if (percent_visible != p_percent) { + if (p_percent < 0 || p_percent >= 1) { + visible_chars = -1; + percent_visible = 1; + } else { + visible_chars = get_total_character_count() * p_percent; + percent_visible = p_percent; + } + if (visible_chars_behavior == TextServer::VC_CHARS_BEFORE_SHAPING) { + dirty = true; + } + update(); } - update(); } float Label::get_percent_visible() const { return percent_visible; } +TextServer::VisibleCharactersBehavior Label::get_visible_characters_behavior() const { + return visible_chars_behavior; +} + +void Label::set_visible_characters_behavior(TextServer::VisibleCharactersBehavior p_behavior) { + if (visible_chars_behavior != p_behavior) { + visible_chars_behavior = p_behavior; + dirty = true; + update(); + } +} + void Label::set_lines_skipped(int p_lines) { ERR_FAIL_COND(p_lines < 0); lines_skipped = p_lines; @@ -700,75 +816,24 @@ int Label::get_max_lines_visible() const { } int Label::get_total_character_count() const { - if (dirty || lines_dirty) { + if (dirty || font_dirty || lines_dirty) { const_cast<Label *>(this)->_shape(); } return xl_text.length(); } -bool Label::_set(const StringName &p_name, const Variant &p_value) { - String str = p_name; - if (str.begins_with("opentype_features/")) { - String name = str.get_slicec('/', 1); - int32_t tag = TS->name_to_tag(name); - double value = p_value; - if (value == -1) { - if (opentype_features.has(tag)) { - opentype_features.erase(tag); - dirty = true; - update(); - } - } else { - if ((double)opentype_features[tag] != value) { - opentype_features[tag] = value; - dirty = true; - update(); - } - } - notify_property_list_changed(); - return true; - } - - return false; -} - -bool Label::_get(const StringName &p_name, Variant &r_ret) const { - String str = p_name; - if (str.begins_with("opentype_features/")) { - String name = str.get_slicec('/', 1); - int32_t tag = TS->name_to_tag(name); - if (opentype_features.has(tag)) { - r_ret = opentype_features[tag]; - return true; - } else { - r_ret = -1; - return true; - } - } - return false; -} - -void Label::_get_property_list(List<PropertyInfo> *p_list) const { - for (const Variant *ftr = opentype_features.next(nullptr); ftr != nullptr; ftr = opentype_features.next(ftr)) { - String name = TS->tag_to_name(*ftr); - p_list->push_back(PropertyInfo(Variant::FLOAT, "opentype_features/" + name)); - } - p_list->push_back(PropertyInfo(Variant::NIL, "opentype_features/_new", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR)); -} - void Label::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_align", "align"), &Label::set_align); - ClassDB::bind_method(D_METHOD("get_align"), &Label::get_align); - ClassDB::bind_method(D_METHOD("set_valign", "valign"), &Label::set_valign); - ClassDB::bind_method(D_METHOD("get_valign"), &Label::get_valign); + ClassDB::bind_method(D_METHOD("set_horizontal_alignment", "alignment"), &Label::set_horizontal_alignment); + ClassDB::bind_method(D_METHOD("get_horizontal_alignment"), &Label::get_horizontal_alignment); + ClassDB::bind_method(D_METHOD("set_vertical_alignment", "alignment"), &Label::set_vertical_alignment); + ClassDB::bind_method(D_METHOD("get_vertical_alignment"), &Label::get_vertical_alignment); ClassDB::bind_method(D_METHOD("set_text", "text"), &Label::set_text); ClassDB::bind_method(D_METHOD("get_text"), &Label::get_text); + ClassDB::bind_method(D_METHOD("set_label_settings", "settings"), &Label::set_label_settings); + ClassDB::bind_method(D_METHOD("get_label_settings"), &Label::get_label_settings); ClassDB::bind_method(D_METHOD("set_text_direction", "direction"), &Label::set_text_direction); ClassDB::bind_method(D_METHOD("get_text_direction"), &Label::get_text_direction); - ClassDB::bind_method(D_METHOD("set_opentype_feature", "tag", "value"), &Label::set_opentype_feature); - ClassDB::bind_method(D_METHOD("get_opentype_feature", "tag"), &Label::get_opentype_feature); - ClassDB::bind_method(D_METHOD("clear_opentype_features"), &Label::clear_opentype_features); ClassDB::bind_method(D_METHOD("set_language", "language"), &Label::set_language); ClassDB::bind_method(D_METHOD("get_language"), &Label::get_language); ClassDB::bind_method(D_METHOD("set_autowrap_mode", "autowrap_mode"), &Label::set_autowrap_mode); @@ -785,6 +850,8 @@ void Label::_bind_methods() { ClassDB::bind_method(D_METHOD("get_total_character_count"), &Label::get_total_character_count); ClassDB::bind_method(D_METHOD("set_visible_characters", "amount"), &Label::set_visible_characters); ClassDB::bind_method(D_METHOD("get_visible_characters"), &Label::get_visible_characters); + ClassDB::bind_method(D_METHOD("get_visible_characters_behavior"), &Label::get_visible_characters_behavior); + ClassDB::bind_method(D_METHOD("set_visible_characters_behavior", "behavior"), &Label::set_visible_characters_behavior); ClassDB::bind_method(D_METHOD("set_percent_visible", "percent_visible"), &Label::set_percent_visible); ClassDB::bind_method(D_METHOD("get_percent_visible"), &Label::get_percent_visible); ClassDB::bind_method(D_METHOD("set_lines_skipped", "lines_skipped"), &Label::set_lines_skipped); @@ -796,41 +863,25 @@ void Label::_bind_methods() { ClassDB::bind_method(D_METHOD("set_structured_text_bidi_override_options", "args"), &Label::set_structured_text_bidi_override_options); ClassDB::bind_method(D_METHOD("get_structured_text_bidi_override_options"), &Label::get_structured_text_bidi_override_options); - BIND_ENUM_CONSTANT(ALIGN_LEFT); - BIND_ENUM_CONSTANT(ALIGN_CENTER); - BIND_ENUM_CONSTANT(ALIGN_RIGHT); - BIND_ENUM_CONSTANT(ALIGN_FILL); - - BIND_ENUM_CONSTANT(VALIGN_TOP); - BIND_ENUM_CONSTANT(VALIGN_CENTER); - BIND_ENUM_CONSTANT(VALIGN_BOTTOM); - BIND_ENUM_CONSTANT(VALIGN_FILL); - - BIND_ENUM_CONSTANT(AUTOWRAP_OFF); - BIND_ENUM_CONSTANT(AUTOWRAP_ARBITRARY); - BIND_ENUM_CONSTANT(AUTOWRAP_WORD); - BIND_ENUM_CONSTANT(AUTOWRAP_WORD_SMART); - - BIND_ENUM_CONSTANT(OVERRUN_NO_TRIMMING); - BIND_ENUM_CONSTANT(OVERRUN_TRIM_CHAR); - BIND_ENUM_CONSTANT(OVERRUN_TRIM_WORD); - BIND_ENUM_CONSTANT(OVERRUN_TRIM_ELLIPSIS); - BIND_ENUM_CONSTANT(OVERRUN_TRIM_WORD_ELLIPSIS); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT, "", PROPERTY_USAGE_DEFAULT_INTL), "set_text", "get_text"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction"); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "language"), "set_language", "get_language"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "align", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_align", "get_align"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "valign", PROPERTY_HINT_ENUM, "Top,Center,Bottom,Fill"), "set_valign", "get_valign"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "label_settings", PROPERTY_HINT_RESOURCE_TYPE, "LabelSettings"), "set_label_settings", "get_label_settings"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "horizontal_alignment", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_horizontal_alignment", "get_horizontal_alignment"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "vertical_alignment", PROPERTY_HINT_ENUM, "Top,Center,Bottom,Fill"), "set_vertical_alignment", "get_vertical_alignment"); ADD_PROPERTY(PropertyInfo(Variant::INT, "autowrap_mode", PROPERTY_HINT_ENUM, "Off,Arbitrary,Word,Word (Smart)"), "set_autowrap_mode", "get_autowrap_mode"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_text"), "set_clip_text", "is_clipping_text"); 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"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "uppercase"), "set_uppercase", "is_uppercase"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "visible_characters", PROPERTY_HINT_RANGE, "-1,128000,1", PROPERTY_USAGE_EDITOR), "set_visible_characters", "get_visible_characters"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "percent_visible", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_percent_visible", "get_percent_visible"); ADD_PROPERTY(PropertyInfo(Variant::INT, "lines_skipped", PROPERTY_HINT_RANGE, "0,999,1"), "set_lines_skipped", "get_lines_skipped"); ADD_PROPERTY(PropertyInfo(Variant::INT, "max_lines_visible", PROPERTY_HINT_RANGE, "-1,999,1"), "set_max_lines_visible", "get_max_lines_visible"); - ADD_GROUP("Structured Text", "structured_text_"); + + // Note: "visible_characters" and "percent_visible" should be set after "text" to be correctly applied. + ADD_PROPERTY(PropertyInfo(Variant::INT, "visible_characters", PROPERTY_HINT_RANGE, "-1,128000,1"), "set_visible_characters", "get_visible_characters"); + 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"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "percent_visible", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_percent_visible", "get_percent_visible"); + + ADD_GROUP("BiDi", ""); + ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language"); 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"); ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "structured_text_bidi_override_options"), "set_structured_text_bidi_override_options", "get_structured_text_bidi_override_options"); } @@ -845,8 +896,8 @@ Label::Label(const String &p_text) { Label::~Label() { for (int i = 0; i < lines_rid.size(); i++) { - TS->free(lines_rid[i]); + TS->free_rid(lines_rid[i]); } lines_rid.clear(); - TS->free(text_rid); + TS->free_rid(text_rid); } |