Merge pull request #104230 from bruvzg/start_clip

Add properties to configure space trimming on line break.
This commit is contained in:
Thaddeus Crews 2025-03-18 14:42:44 -05:00
commit b7eda9dbc1
No known key found for this signature in database
GPG key ID: 62181B86FE9E5D84
18 changed files with 171 additions and 30 deletions

View file

@ -86,6 +86,9 @@ void Label3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_autowrap_mode", "autowrap_mode"), &Label3D::set_autowrap_mode);
ClassDB::bind_method(D_METHOD("get_autowrap_mode"), &Label3D::get_autowrap_mode);
ClassDB::bind_method(D_METHOD("set_autowrap_trim_flags", "autowrap_trim_flags"), &Label3D::set_autowrap_trim_flags);
ClassDB::bind_method(D_METHOD("get_autowrap_trim_flags"), &Label3D::get_autowrap_trim_flags);
ClassDB::bind_method(D_METHOD("set_justification_flags", "justification_flags"), &Label3D::set_justification_flags);
ClassDB::bind_method(D_METHOD("get_justification_flags"), &Label3D::get_justification_flags);
@ -154,6 +157,7 @@ void Label3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "uppercase"), "set_uppercase", "is_uppercase");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "line_spacing", PROPERTY_HINT_NONE, "suffix:px"), "set_line_spacing", "get_line_spacing");
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::INT, "autowrap_trim_flags", PROPERTY_HINT_FLAGS, vformat("Trim Spaces After Break:%d,Trim Spaces Before Break:%d", TextServer::BREAK_TRIM_START_EDGE_SPACES, TextServer::BREAK_TRIM_END_EDGE_SPACES)), "set_autowrap_trim_flags", "get_autowrap_trim_flags");
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");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "width", PROPERTY_HINT_NONE, "suffix:px"), "set_width", "get_width");
@ -519,7 +523,7 @@ void Label3D::_shape() {
case TextServer::AUTOWRAP_OFF:
break;
}
autowrap_flags = autowrap_flags | TextServer::BREAK_TRIM_EDGE_SPACES;
autowrap_flags = autowrap_flags | autowrap_flags_trim;
PackedInt32Array line_breaks = TS->shaped_text_get_line_breaks(text_rid, width, 0, autowrap_flags);
float max_line_w = 0.0;
@ -891,6 +895,18 @@ TextServer::AutowrapMode Label3D::get_autowrap_mode() const {
return autowrap_mode;
}
void Label3D::set_autowrap_trim_flags(BitField<TextServer::LineBreakFlag> p_flags) {
if (autowrap_flags_trim != (p_flags & TextServer::BREAK_TRIM_MASK)) {
autowrap_flags_trim = (p_flags & TextServer::BREAK_TRIM_MASK);
dirty_lines = true;
_queue_update();
}
}
BitField<TextServer::LineBreakFlag> Label3D::get_autowrap_trim_flags() const {
return autowrap_flags_trim;
}
void Label3D::set_justification_flags(BitField<TextServer::JustificationFlag> p_flags) {
if (jst_flags != p_flags) {
jst_flags = p_flags;

View file

@ -111,6 +111,7 @@ private:
bool uppercase = false;
TextServer::AutowrapMode autowrap_mode = TextServer::AUTOWRAP_OFF;
BitField<TextServer::LineBreakFlag> autowrap_flags_trim = TextServer::BREAK_TRIM_START_EDGE_SPACES | TextServer::BREAK_TRIM_END_EDGE_SPACES;
BitField<TextServer::JustificationFlag> jst_flags = TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_SKIP_LAST_LINE | TextServer::JUSTIFICATION_DO_NOT_SKIP_SINGLE_LINE;
float width = 500.0;
@ -215,6 +216,9 @@ public:
void set_autowrap_mode(TextServer::AutowrapMode p_mode);
TextServer::AutowrapMode get_autowrap_mode() const;
void set_autowrap_trim_flags(BitField<TextServer::LineBreakFlag> p_flags);
BitField<TextServer::LineBreakFlag> get_autowrap_trim_flags() const;
void set_justification_flags(BitField<TextServer::JustificationFlag> p_flags);
BitField<TextServer::JustificationFlag> get_justification_flags() const;

View file

@ -570,7 +570,7 @@ void Button::_shape(Ref<TextParagraph> p_paragraph, String p_text) const {
case TextServer::AUTOWRAP_OFF:
break;
}
autowrap_flags = autowrap_flags | TextServer::BREAK_TRIM_EDGE_SPACES;
autowrap_flags = autowrap_flags | autowrap_flags_trim;
p_paragraph->set_break_flags(autowrap_flags);
p_paragraph->set_line_spacing(theme_cache.line_spacing);
@ -631,6 +631,19 @@ TextServer::AutowrapMode Button::get_autowrap_mode() const {
return autowrap_mode;
}
void Button::set_autowrap_trim_flags(BitField<TextServer::LineBreakFlag> p_flags) {
if (autowrap_flags_trim != (p_flags & TextServer::BREAK_TRIM_MASK)) {
autowrap_flags_trim = p_flags & TextServer::BREAK_TRIM_MASK;
_shape();
queue_redraw();
update_minimum_size();
}
}
BitField<TextServer::LineBreakFlag> Button::get_autowrap_trim_flags() const {
return autowrap_flags_trim;
}
void Button::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) {
@ -772,6 +785,8 @@ void Button::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_text_overrun_behavior"), &Button::get_text_overrun_behavior);
ClassDB::bind_method(D_METHOD("set_autowrap_mode", "autowrap_mode"), &Button::set_autowrap_mode);
ClassDB::bind_method(D_METHOD("get_autowrap_mode"), &Button::get_autowrap_mode);
ClassDB::bind_method(D_METHOD("set_autowrap_trim_flags", "autowrap_trim_flags"), &Button::set_autowrap_trim_flags);
ClassDB::bind_method(D_METHOD("get_autowrap_trim_flags"), &Button::get_autowrap_trim_flags);
ClassDB::bind_method(D_METHOD("set_text_direction", "direction"), &Button::set_text_direction);
ClassDB::bind_method(D_METHOD("get_text_direction"), &Button::get_text_direction);
ClassDB::bind_method(D_METHOD("set_language", "language"), &Button::set_language);
@ -799,6 +814,7 @@ void Button::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "alignment", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_text_alignment", "get_text_alignment");
ADD_PROPERTY(PropertyInfo(Variant::INT, "text_overrun_behavior", PROPERTY_HINT_ENUM, "Trim Nothing,Trim Characters,Trim Words,Ellipsis (6+ Characters),Word Ellipsis (6+ Characters),Ellipsis (Always),Word Ellipsis (Always)"), "set_text_overrun_behavior", "get_text_overrun_behavior");
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::INT, "autowrap_trim_flags", PROPERTY_HINT_FLAGS, vformat("Trim Spaces After Break:%d,Trim Spaces Before Break:%d", TextServer::BREAK_TRIM_START_EDGE_SPACES, TextServer::BREAK_TRIM_END_EDGE_SPACES)), "set_autowrap_trim_flags", "get_autowrap_trim_flags");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_text"), "set_clip_text", "get_clip_text");
ADD_GROUP("Icon Behavior", "");
@ -852,7 +868,7 @@ void Button::_bind_methods() {
Button::Button(const String &p_text) {
text_buf.instantiate();
text_buf->set_break_flags(TextServer::BREAK_MANDATORY | TextServer::BREAK_TRIM_EDGE_SPACES);
text_buf->set_break_flags(TextServer::BREAK_MANDATORY | autowrap_flags_trim);
set_mouse_filter(MOUSE_FILTER_STOP);
set_text(p_text);

View file

@ -45,6 +45,7 @@ private:
String language;
TextDirection text_direction = TEXT_DIRECTION_AUTO;
TextServer::AutowrapMode autowrap_mode = TextServer::AUTOWRAP_OFF;
BitField<TextServer::LineBreakFlag> autowrap_flags_trim = TextServer::BREAK_TRIM_END_EDGE_SPACES;
TextServer::OverrunBehavior overrun_behavior = TextServer::OVERRUN_NO_TRIMMING;
Ref<Texture2D> icon;
@ -132,6 +133,9 @@ public:
void set_autowrap_mode(TextServer::AutowrapMode p_mode);
TextServer::AutowrapMode get_autowrap_mode() const;
void set_autowrap_trim_flags(BitField<TextServer::LineBreakFlag> p_flags);
BitField<TextServer::LineBreakFlag> get_autowrap_trim_flags() const;
void set_text_direction(TextDirection p_text_direction);
TextDirection get_text_direction() const;

View file

@ -45,7 +45,7 @@ void ItemList::_shape_text(int p_idx) {
}
item.text_buf->add_string(item.xl_text, theme_cache.font, theme_cache.font_size, item.language);
if (icon_mode == ICON_MODE_TOP && max_text_lines > 0) {
item.text_buf->set_break_flags(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::BREAK_GRAPHEME_BOUND | TextServer::BREAK_TRIM_EDGE_SPACES);
item.text_buf->set_break_flags(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::BREAK_GRAPHEME_BOUND | TextServer::BREAK_TRIM_START_EDGE_SPACES | TextServer::BREAK_TRIM_END_EDGE_SPACES);
} else {
item.text_buf->set_break_flags(TextServer::BREAK_NONE);
}
@ -558,7 +558,7 @@ void ItemList::set_max_text_lines(int p_lines) {
max_text_lines = p_lines;
for (int i = 0; i < items.size(); i++) {
if (icon_mode == ICON_MODE_TOP && max_text_lines > 0) {
items.write[i].text_buf->set_break_flags(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::BREAK_GRAPHEME_BOUND | TextServer::BREAK_TRIM_EDGE_SPACES);
items.write[i].text_buf->set_break_flags(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::BREAK_GRAPHEME_BOUND | TextServer::BREAK_TRIM_START_EDGE_SPACES | TextServer::BREAK_TRIM_END_EDGE_SPACES);
items.write[i].text_buf->set_max_lines_visible(p_lines);
} else {
items.write[i].text_buf->set_break_flags(TextServer::BREAK_NONE);
@ -608,7 +608,7 @@ void ItemList::set_icon_mode(IconMode p_mode) {
icon_mode = p_mode;
for (int i = 0; i < items.size(); i++) {
if (icon_mode == ICON_MODE_TOP && max_text_lines > 0) {
items.write[i].text_buf->set_break_flags(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::BREAK_GRAPHEME_BOUND | TextServer::BREAK_TRIM_EDGE_SPACES);
items.write[i].text_buf->set_break_flags(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::BREAK_GRAPHEME_BOUND | TextServer::BREAK_TRIM_START_EDGE_SPACES | TextServer::BREAK_TRIM_END_EDGE_SPACES);
} else {
items.write[i].text_buf->set_break_flags(TextServer::BREAK_NONE);
}

View file

@ -55,6 +55,27 @@ TextServer::AutowrapMode Label::get_autowrap_mode() const {
return autowrap_mode;
}
void Label::set_autowrap_trim_flags(BitField<TextServer::LineBreakFlag> p_flags) {
if (autowrap_flags_trim == (p_flags & TextServer::BREAK_TRIM_MASK)) {
return;
}
autowrap_flags_trim = p_flags & TextServer::BREAK_TRIM_MASK;
for (Paragraph &para : paragraphs) {
para.lines_dirty = true;
}
queue_redraw();
update_configuration_warnings();
if (clip || overrun_behavior != TextServer::OVERRUN_NO_TRIMMING) {
update_minimum_size();
}
}
BitField<TextServer::LineBreakFlag> Label::get_autowrap_trim_flags() const {
return autowrap_flags_trim;
}
void Label::set_justification_flags(BitField<TextServer::JustificationFlag> p_flags) {
if (jst_flags == p_flags) {
return;
@ -196,7 +217,7 @@ void Label::_shape() const {
case TextServer::AUTOWRAP_OFF:
break;
}
autowrap_flags = autowrap_flags | TextServer::BREAK_TRIM_EDGE_SPACES;
autowrap_flags = autowrap_flags | autowrap_flags_trim;
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) {
@ -1362,6 +1383,8 @@ void Label::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_paragraph_separator"), &Label::get_paragraph_separator);
ClassDB::bind_method(D_METHOD("set_autowrap_mode", "autowrap_mode"), &Label::set_autowrap_mode);
ClassDB::bind_method(D_METHOD("get_autowrap_mode"), &Label::get_autowrap_mode);
ClassDB::bind_method(D_METHOD("set_autowrap_trim_flags", "autowrap_trim_flags"), &Label::set_autowrap_trim_flags);
ClassDB::bind_method(D_METHOD("get_autowrap_trim_flags"), &Label::get_autowrap_trim_flags);
ClassDB::bind_method(D_METHOD("set_justification_flags", "justification_flags"), &Label::set_justification_flags);
ClassDB::bind_method(D_METHOD("get_justification_flags"), &Label::get_justification_flags);
ClassDB::bind_method(D_METHOD("set_clip_text", "enable"), &Label::set_clip_text);
@ -1400,6 +1423,7 @@ void Label::_bind_methods() {
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::INT, "autowrap_trim_flags", PROPERTY_HINT_FLAGS, vformat("Trim Spaces After Break:%d,Trim Spaces Before Break:%d", TextServer::BREAK_TRIM_START_EDGE_SPACES, TextServer::BREAK_TRIM_END_EDGE_SPACES)), "set_autowrap_trim_flags", "get_autowrap_trim_flags");
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");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "paragraph_separator"), "set_paragraph_separator", "get_paragraph_separator");

View file

@ -50,6 +50,7 @@ private:
String text;
String xl_text;
TextServer::AutowrapMode autowrap_mode = TextServer::AUTOWRAP_OFF;
BitField<TextServer::LineBreakFlag> autowrap_flags_trim = TextServer::BREAK_TRIM_START_EDGE_SPACES | TextServer::BREAK_TRIM_END_EDGE_SPACES;
BitField<TextServer::JustificationFlag> jst_flags = TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_SKIP_LAST_LINE | TextServer::JUSTIFICATION_DO_NOT_SKIP_SINGLE_LINE;
bool clip = false;
String el_char = U"";
@ -153,6 +154,9 @@ public:
void set_autowrap_mode(TextServer::AutowrapMode p_mode);
TextServer::AutowrapMode get_autowrap_mode() const;
void set_autowrap_trim_flags(BitField<TextServer::LineBreakFlag> p_flags);
BitField<TextServer::LineBreakFlag> get_autowrap_trim_flags() const;
void set_justification_flags(BitField<TextServer::JustificationFlag> p_flags);
BitField<TextServer::JustificationFlag> get_justification_flags() const;

View file

@ -446,7 +446,7 @@ float RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
case TextServer::AUTOWRAP_OFF:
break;
}
autowrap_flags = autowrap_flags | TextServer::BREAK_TRIM_EDGE_SPACES;
autowrap_flags = autowrap_flags | autowrap_flags_trim;
// Clear cache.
l.text_buf->clear();
@ -6327,6 +6327,21 @@ TextServer::AutowrapMode RichTextLabel::get_autowrap_mode() const {
return autowrap_mode;
}
void RichTextLabel::set_autowrap_trim_flags(BitField<TextServer::LineBreakFlag> p_flags) {
if (autowrap_flags_trim != (p_flags & TextServer::BREAK_TRIM_MASK)) {
_stop_thread();
autowrap_flags_trim = p_flags & TextServer::BREAK_TRIM_MASK;
main->first_invalid_line = 0; // Invalidate all lines.
_validate_line_caches();
queue_redraw();
}
}
BitField<TextServer::LineBreakFlag> RichTextLabel::get_autowrap_trim_flags() const {
return autowrap_flags_trim;
}
void RichTextLabel::set_visible_ratio(float p_ratio) {
if (visible_ratio != p_ratio) {
_stop_thread();
@ -6487,6 +6502,9 @@ void RichTextLabel::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_autowrap_mode", "autowrap_mode"), &RichTextLabel::set_autowrap_mode);
ClassDB::bind_method(D_METHOD("get_autowrap_mode"), &RichTextLabel::get_autowrap_mode);
ClassDB::bind_method(D_METHOD("set_autowrap_trim_flags", "autowrap_trim_flags"), &RichTextLabel::set_autowrap_trim_flags);
ClassDB::bind_method(D_METHOD("get_autowrap_trim_flags"), &RichTextLabel::get_autowrap_trim_flags);
ClassDB::bind_method(D_METHOD("set_meta_underline", "enable"), &RichTextLabel::set_meta_underline);
ClassDB::bind_method(D_METHOD("is_meta_underlined"), &RichTextLabel::is_meta_underlined);
@ -6598,6 +6616,7 @@ void RichTextLabel::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_active"), "set_scroll_active", "is_scroll_active");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_following"), "set_scroll_follow", "is_scroll_following");
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::INT, "autowrap_trim_flags", PROPERTY_HINT_FLAGS, vformat("Trim Spaces After Break:%d,Trim Spaces Before Break:%d", TextServer::BREAK_TRIM_START_EDGE_SPACES, TextServer::BREAK_TRIM_END_EDGE_SPACES)), "set_autowrap_trim_flags", "get_autowrap_trim_flags");
ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_size", PROPERTY_HINT_RANGE, "0,24,1"), "set_tab_size", "get_tab_size");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "context_menu_enabled"), "set_context_menu_enabled", "is_context_menu_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shortcut_keys_enabled"), "set_shortcut_keys_enabled", "is_shortcut_keys_enabled");

View file

@ -472,6 +472,7 @@ private:
VScrollBar *vscroll = nullptr;
TextServer::AutowrapMode autowrap_mode = TextServer::AUTOWRAP_WORD_SMART;
BitField<TextServer::LineBreakFlag> autowrap_flags_trim = TextServer::BREAK_TRIM_START_EDGE_SPACES | TextServer::BREAK_TRIM_END_EDGE_SPACES;
bool scroll_visible = false;
bool scroll_follow = false;
@ -858,6 +859,9 @@ public:
void set_autowrap_mode(TextServer::AutowrapMode p_mode);
TextServer::AutowrapMode get_autowrap_mode() const;
void set_autowrap_trim_flags(BitField<TextServer::LineBreakFlag> p_flags);
BitField<TextServer::LineBreakFlag> get_autowrap_trim_flags() const;
void set_structured_text_bidi_override(TextServer::StructuredTextParser p_parser);
TextServer::StructuredTextParser get_structured_text_bidi_override() const;

View file

@ -2082,7 +2082,7 @@ void Tree::update_item_cell(TreeItem *p_item, int p_col) const {
}
p_item->cells.write[p_col].text_buf->add_string(valtext, font, font_size, p_item->cells[p_col].language);
BitField<TextServer::LineBreakFlag> break_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_TRIM_EDGE_SPACES;
BitField<TextServer::LineBreakFlag> break_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_TRIM_START_EDGE_SPACES | TextServer::BREAK_TRIM_END_EDGE_SPACES;
switch (p_item->cells.write[p_col].autowrap_mode) {
case TextServer::AUTOWRAP_OFF:
break;