/**************************************************************************/ /* label.cpp */ /**************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* https://godotengine.org */ /**************************************************************************/ /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ /* "Software"), to deal in the Software without restriction, including */ /* without limitation the rights to use, copy, modify, merge, publish, */ /* distribute, sublicense, and/or sell copies of the Software, and to */ /* permit persons to whom the Software is furnished to do so, subject to */ /* the following conditions: */ /* */ /* The above copyright notice and this permission notice shall be */ /* included in all copies or substantial portions of the Software. */ /* */ /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /**************************************************************************/ #include "label.h" #include "core/config/project_settings.h" #include "core/string/print_string.h" #include "core/string/translation.h" #include "scene/gui/container.h" #include "scene/theme/theme_db.h" #include "servers/text_server.h" void Label::set_autowrap_mode(TextServer::AutowrapMode p_mode) { if (autowrap_mode == p_mode) { return; } autowrap_mode = p_mode; for (Paragraph ¶ : paragraphs) { para.lines_dirty = true; } queue_redraw(); update_configuration_warnings(); if (clip || overrun_behavior != TextServer::OVERRUN_NO_TRIMMING) { update_minimum_size(); } } TextServer::AutowrapMode Label::get_autowrap_mode() const { return autowrap_mode; } void Label::set_justification_flags(BitField p_flags) { if (jst_flags == p_flags) { return; } jst_flags = p_flags; for (Paragraph ¶ : paragraphs) { para.lines_dirty = true; } queue_redraw(); } BitField Label::get_justification_flags() const { return jst_flags; } void Label::set_uppercase(bool p_uppercase) { if (uppercase == p_uppercase) { return; } uppercase = p_uppercase; text_dirty = true; queue_redraw(); } bool Label::is_uppercase() const { return uppercase; } int Label::get_line_height(int p_line) const { Ref font = (settings.is_valid() && settings->get_font().is_valid()) ? settings->get_font() : theme_cache.font; int font_size = settings.is_valid() ? settings->get_font_size() : theme_cache.font_size; int font_h = font->get_height(font_size); if (p_line >= 0 && p_line < total_line_count) { return MAX(font_h, TS->shaped_text_get_size(get_line_rid(p_line)).y); } else if (total_line_count > 0) { int h = font_h; for (const Paragraph ¶ : paragraphs) { for (const RID &line_rid : para.lines_rid) { h = MAX(h, TS->shaped_text_get_size(line_rid).y); } } return h; } else { return font->get_height(font_size); } } void Label::_shape() const { Ref style = theme_cache.normal_style; int width = (get_size().width - style->get_minimum_size().width); if (text_dirty) { for (Paragraph ¶ : paragraphs) { for (const RID &line_rid : para.lines_rid) { TS->free_rid(line_rid); } para.lines_rid.clear(); TS->free_rid(para.text_rid); } paragraphs.clear(); String txt = (uppercase) ? TS->string_to_upper(xl_text, language) : xl_text; if (visible_chars >= 0 && visible_chars_behavior == TextServer::VC_CHARS_BEFORE_SHAPING) { txt = txt.substr(0, visible_chars); } String ps = paragraph_separator.c_unescape(); Vector para_text = txt.split(ps); int start = 0; for (const String &str : para_text) { Paragraph para; para.text_rid = TS->create_shaped_text(); para.text = str; para.start = start; start += str.length() + ps.length(); paragraphs.push_back(para); } text_dirty = false; } total_line_count = 0; for (Paragraph ¶ : paragraphs) { if (para.dirty || font_dirty) { if (para.dirty) { TS->shaped_text_clear(para.text_rid); } if (text_direction == Control::TEXT_DIRECTION_INHERITED) { TS->shaped_text_set_direction(para.text_rid, is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR); } else { TS->shaped_text_set_direction(para.text_rid, (TextServer::Direction)text_direction); } const Ref &font = (settings.is_valid() && settings->get_font().is_valid()) ? settings->get_font() : theme_cache.font; int font_size = settings.is_valid() ? settings->get_font_size() : theme_cache.font_size; ERR_FAIL_COND(font.is_null()); if (para.dirty) { TS->shaped_text_add_string(para.text_rid, para.text, font->get_rids(), font_size, font->get_opentype_features(), language); } else { int spans = TS->shaped_get_span_count(para.text_rid); for (int i = 0; i < spans; i++) { TS->shaped_set_span_update_font(para.text_rid, i, font->get_rids(), font_size, font->get_opentype_features()); } } TS->shaped_text_set_bidi_override(para.text_rid, structured_text_parser(st_parser, st_args, para.text)); if (!tab_stops.is_empty()) { TS->shaped_text_tab_align(para.text_rid, tab_stops); } para.dirty = false; para.lines_dirty = true; } if (para.lines_dirty) { for (const RID &line_rid : para.lines_rid) { TS->free_rid(line_rid); } para.lines_rid.clear(); BitField autowrap_flags = TextServer::BREAK_MANDATORY; switch (autowrap_mode) { case TextServer::AUTOWRAP_WORD_SMART: autowrap_flags = TextServer::BREAK_WORD_BOUND | TextServer::BREAK_ADAPTIVE | TextServer::BREAK_MANDATORY; break; case TextServer::AUTOWRAP_WORD: autowrap_flags = TextServer::BREAK_WORD_BOUND | TextServer::BREAK_MANDATORY; break; case TextServer::AUTOWRAP_ARBITRARY: autowrap_flags = TextServer::BREAK_GRAPHEME_BOUND | TextServer::BREAK_MANDATORY; break; case TextServer::AUTOWRAP_OFF: break; } autowrap_flags = autowrap_flags | TextServer::BREAK_TRIM_EDGE_SPACES; PackedInt32Array line_breaks = TS->shaped_text_get_line_breaks(para.text_rid, width, 0, autowrap_flags); for (int i = 0; i < line_breaks.size(); i = i + 2) { RID line = TS->shaped_text_substr(para.text_rid, line_breaks[i], line_breaks[i + 1] - line_breaks[i]); if (!tab_stops.is_empty()) { TS->shaped_text_tab_align(line, tab_stops); } para.lines_rid.push_back(line); } } total_line_count += para.lines_rid.size(); } dirty = false; font_dirty = false; if (xl_text.length() == 0) { minsize = Size2(1, get_line_height()); return; } int visible_lines = get_visible_line_count(); bool lines_hidden = visible_lines > 0 && visible_lines < total_line_count; int line_index = 0; if (autowrap_mode == TextServer::AUTOWRAP_OFF) { minsize.width = 0.0f; } for (Paragraph ¶ : paragraphs) { if (autowrap_mode == TextServer::AUTOWRAP_OFF) { for (const RID &line_rid : para.lines_rid) { if (minsize.width < TS->shaped_text_get_size(line_rid).x) { minsize.width = TS->shaped_text_get_size(line_rid).x; } } } if (para.lines_dirty) { BitField overrun_flags = TextServer::OVERRUN_NO_TRIM; switch (overrun_behavior) { 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 TextServer::OVERRUN_TRIM_ELLIPSIS: overrun_flags.set_flag(TextServer::OVERRUN_TRIM); overrun_flags.set_flag(TextServer::OVERRUN_ADD_ELLIPSIS); break; case TextServer::OVERRUN_TRIM_WORD: overrun_flags.set_flag(TextServer::OVERRUN_TRIM); overrun_flags.set_flag(TextServer::OVERRUN_TRIM_WORD_ONLY); break; case TextServer::OVERRUN_TRIM_CHAR: overrun_flags.set_flag(TextServer::OVERRUN_TRIM); break; case TextServer::OVERRUN_NO_TRIMMING: break; } // Fill after min_size calculation. BitField line_jst_flags = jst_flags; if (!tab_stops.is_empty()) { line_jst_flags.set_flag(TextServer::JUSTIFICATION_AFTER_LAST_TAB); } if (autowrap_mode != TextServer::AUTOWRAP_OFF) { if (lines_hidden) { overrun_flags.set_flag(TextServer::OVERRUN_ENFORCE_ELLIPSIS); } if (horizontal_alignment == HORIZONTAL_ALIGNMENT_FILL) { int jst_to_line = para.lines_rid.size(); if (para.lines_rid.size() == 1 && line_jst_flags.has_flag(TextServer::JUSTIFICATION_DO_NOT_SKIP_SINGLE_LINE)) { jst_to_line = para.lines_rid.size(); } else { if (line_jst_flags.has_flag(TextServer::JUSTIFICATION_SKIP_LAST_LINE)) { jst_to_line = para.lines_rid.size() - 1; } if (line_jst_flags.has_flag(TextServer::JUSTIFICATION_SKIP_LAST_LINE_WITH_VISIBLE_CHARS)) { for (int i = para.lines_rid.size() - 1; i >= 0; i--) { if (TS->shaped_text_has_visible_chars(para.lines_rid[i])) { jst_to_line = i; break; } } } } for (int i = 0; i < para.lines_rid.size(); i++) { if (i < jst_to_line) { TS->shaped_text_fit_to_width(para.lines_rid[i], width, line_jst_flags); } else if (i == (visible_lines - line_index - 1)) { TS->shaped_text_set_custom_ellipsis(para.lines_rid[i], (el_char.length() > 0) ? el_char[0] : 0x2026); TS->shaped_text_overrun_trim_to_width(para.lines_rid[i], width, overrun_flags); } } } else if (lines_hidden && (visible_lines - line_index - 1 >= 0) && (visible_lines - line_index - 1) < para.lines_rid.size()) { TS->shaped_text_set_custom_ellipsis(para.lines_rid[visible_lines - line_index - 1], (el_char.length() > 0) ? el_char[0] : 0x2026); TS->shaped_text_overrun_trim_to_width(para.lines_rid[visible_lines - line_index - 1], width, overrun_flags); } } else { // Autowrap disabled. int jst_to_line = para.lines_rid.size(); if (para.lines_rid.size() == 1 && line_jst_flags.has_flag(TextServer::JUSTIFICATION_DO_NOT_SKIP_SINGLE_LINE)) { jst_to_line = para.lines_rid.size(); } else { if (line_jst_flags.has_flag(TextServer::JUSTIFICATION_SKIP_LAST_LINE)) { jst_to_line = para.lines_rid.size() - 1; } if (line_jst_flags.has_flag(TextServer::JUSTIFICATION_SKIP_LAST_LINE_WITH_VISIBLE_CHARS)) { for (int i = para.lines_rid.size() - 1; i >= 0; i--) { if (TS->shaped_text_has_visible_chars(para.lines_rid[i])) { jst_to_line = i; break; } } } } for (int i = 0; i < para.lines_rid.size(); i++) { if (i < jst_to_line && horizontal_alignment == HORIZONTAL_ALIGNMENT_FILL) { TS->shaped_text_fit_to_width(para.lines_rid[i], width, line_jst_flags); overrun_flags.set_flag(TextServer::OVERRUN_JUSTIFICATION_AWARE); TS->shaped_text_set_custom_ellipsis(para.lines_rid[i], (el_char.length() > 0) ? el_char[0] : 0x2026); TS->shaped_text_overrun_trim_to_width(para.lines_rid[i], width, overrun_flags); TS->shaped_text_fit_to_width(para.lines_rid[i], width, line_jst_flags | TextServer::JUSTIFICATION_CONSTRAIN_ELLIPSIS); } else { TS->shaped_text_set_custom_ellipsis(para.lines_rid[i], (el_char.length() > 0) ? el_char[0] : 0x2026); TS->shaped_text_overrun_trim_to_width(para.lines_rid[i], width, overrun_flags); } } } para.lines_dirty = false; } line_index += para.lines_rid.size(); } _update_visible(); if (autowrap_mode == TextServer::AUTOWRAP_OFF || !clip || overrun_behavior == TextServer::OVERRUN_NO_TRIMMING) { const_cast