From 2bbf0f2317c7dc97d0f8b5fa9f925b46cbecfe63 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pa=CC=84vels=20Nadtoc=CC=8Cajevs?=
<7645683+bruvzg@users.noreply.github.com>
Date: Sun, 16 Mar 2025 13:57:01 +0200
Subject: [PATCH] Add properties to configure space trimming on line break.
---
doc/classes/Button.xml | 3 +++
doc/classes/Label.xml | 3 +++
doc/classes/Label3D.xml | 3 +++
doc/classes/RichTextLabel.xml | 3 +++
doc/classes/TextServer.xml | 10 +++++++-
scene/3d/label_3d.cpp | 18 ++++++++++++-
scene/3d/label_3d.h | 4 +++
scene/gui/button.cpp | 20 +++++++++++++--
scene/gui/button.h | 4 +++
scene/gui/item_list.cpp | 6 ++---
scene/gui/label.cpp | 26 ++++++++++++++++++-
scene/gui/label.h | 4 +++
scene/gui/rich_text_label.cpp | 21 ++++++++++++++-
scene/gui/rich_text_label.h | 4 +++
scene/gui/tree.cpp | 2 +-
servers/text_server.cpp | 44 ++++++++++++++++++++++----------
servers/text_server.h | 8 ++++++
tests/servers/test_text_server.h | 18 ++++++++-----
18 files changed, 171 insertions(+), 30 deletions(-)
diff --git a/doc/classes/Button.xml b/doc/classes/Button.xml
index 5103134f657..cc13cb54f36 100644
--- a/doc/classes/Button.xml
+++ b/doc/classes/Button.xml
@@ -46,6 +46,9 @@
If set to something other than [constant TextServer.AUTOWRAP_OFF], the text gets wrapped inside the node's bounding rectangle.
+
+ Autowrap space trimming flags. See [constant TextServer.BREAK_TRIM_START_EDGE_SPACES] and [constant TextServer.BREAK_TRIM_END_EDGE_SPACES] for more info.
+
If [code]true[/code], text that is too large to fit the button is clipped horizontally. If [code]false[/code], the button will always be wide enough to hold the text. The text is not vertically clipped, and the button's height is not affected by this property.
diff --git a/doc/classes/Label.xml b/doc/classes/Label.xml
index 7657cf31214..6f99d27cb16 100644
--- a/doc/classes/Label.xml
+++ b/doc/classes/Label.xml
@@ -49,6 +49,9 @@
If set to something other than [constant TextServer.AUTOWRAP_OFF], the text gets wrapped inside the node's bounding rectangle. If you resize the node, it will change its height automatically to show all the text. To see how each mode behaves, see [enum TextServer.AutowrapMode].
+
+ Autowrap space trimming flags. See [constant TextServer.BREAK_TRIM_START_EDGE_SPACES] and [constant TextServer.BREAK_TRIM_END_EDGE_SPACES] for more info.
+
If [code]true[/code], the Label only shows the text that fits inside its bounding rectangle and will clip text horizontally.
diff --git a/doc/classes/Label3D.xml b/doc/classes/Label3D.xml
index 188a4aac9a2..b655d77b544 100644
--- a/doc/classes/Label3D.xml
+++ b/doc/classes/Label3D.xml
@@ -51,6 +51,9 @@
If set to something other than [constant TextServer.AUTOWRAP_OFF], the text gets wrapped inside the node's bounding rectangle. If you resize the node, it will change its height automatically to show all the text. To see how each mode behaves, see [enum TextServer.AutowrapMode].
+
+ Autowrap space trimming flags. See [constant TextServer.BREAK_TRIM_START_EDGE_SPACES] and [constant TextServer.BREAK_TRIM_END_EDGE_SPACES] for more info.
+
The billboard mode to use for the label. See [enum BaseMaterial3D.BillboardMode] for possible values.
diff --git a/doc/classes/RichTextLabel.xml b/doc/classes/RichTextLabel.xml
index b106b485eec..b8d442a8309 100644
--- a/doc/classes/RichTextLabel.xml
+++ b/doc/classes/RichTextLabel.xml
@@ -632,6 +632,9 @@
If set to something other than [constant TextServer.AUTOWRAP_OFF], the text gets wrapped inside the node's bounding rectangle. To see how each mode behaves, see [enum TextServer.AutowrapMode].
+
+ Autowrap space trimming flags. See [constant TextServer.BREAK_TRIM_START_EDGE_SPACES] and [constant TextServer.BREAK_TRIM_END_EDGE_SPACES] for more info.
+
If [code]true[/code], the label uses BBCode formatting.
[b]Note:[/b] This only affects the contents of [member text], not the tag stack.
diff --git a/doc/classes/TextServer.xml b/doc/classes/TextServer.xml
index f6f1ad8e538..9098a01e9d8 100644
--- a/doc/classes/TextServer.xml
+++ b/doc/classes/TextServer.xml
@@ -1938,12 +1938,20 @@
Should be used only in conjunction with [constant BREAK_WORD_BOUND], break the line between any unconnected graphemes, if it's impossible to break it between the words.
-
+
Remove edge spaces from the broken line segments.
Subtract first line indentation width from all lines after the first one.
+
+ Remove spaces and line break characters from the start of broken line segments.
+ E.g, after line breaking, the second segment of the following text [code]test \n next[/code], is [code]next[/code] if the flag is set, and [code] next[/code] if it is not.
+
+
+ Remove spaces and line break characters from the end of broken line segments.
+ E.g, after line breaking, the first segment of the following text [code]test \n next[/code], is [code]test[/code] if the flag is set, and [code]test \n[/code] if it is not.
+
Trims text before the shaping. e.g, increasing [member Label.visible_characters] or [member RichTextLabel.visible_characters] value is visually identical to typing the text.
[b]Note:[/b] In this mode, trimmed text is not processed at all. It is not accounted for in line breaking and size calculations.
diff --git a/scene/3d/label_3d.cpp b/scene/3d/label_3d.cpp
index 0c55cde2a37..e8738bb03b7 100644
--- a/scene/3d/label_3d.cpp
+++ b/scene/3d/label_3d.cpp
@@ -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 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 Label3D::get_autowrap_trim_flags() const {
+ return autowrap_flags_trim;
+}
+
void Label3D::set_justification_flags(BitField p_flags) {
if (jst_flags != p_flags) {
jst_flags = p_flags;
diff --git a/scene/3d/label_3d.h b/scene/3d/label_3d.h
index 8ed99ed3e7e..a27f6d62319 100644
--- a/scene/3d/label_3d.h
+++ b/scene/3d/label_3d.h
@@ -111,6 +111,7 @@ private:
bool uppercase = false;
TextServer::AutowrapMode autowrap_mode = TextServer::AUTOWRAP_OFF;
+ BitField autowrap_flags_trim = TextServer::BREAK_TRIM_START_EDGE_SPACES | TextServer::BREAK_TRIM_END_EDGE_SPACES;
BitField 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 p_flags);
+ BitField get_autowrap_trim_flags() const;
+
void set_justification_flags(BitField p_flags);
BitField get_justification_flags() const;
diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp
index 738637ef36a..8cfe6723447 100644
--- a/scene/gui/button.cpp
+++ b/scene/gui/button.cpp
@@ -566,7 +566,7 @@ void Button::_shape(Ref 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);
@@ -625,6 +625,19 @@ TextServer::AutowrapMode Button::get_autowrap_mode() const {
return autowrap_mode;
}
+void Button::set_autowrap_trim_flags(BitField 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 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) {
@@ -766,6 +779,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);
@@ -793,6 +808,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", "");
@@ -846,7 +862,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);
diff --git a/scene/gui/button.h b/scene/gui/button.h
index 26e6154bf7f..2758fdef36a 100644
--- a/scene/gui/button.h
+++ b/scene/gui/button.h
@@ -45,6 +45,7 @@ private:
String language;
TextDirection text_direction = TEXT_DIRECTION_AUTO;
TextServer::AutowrapMode autowrap_mode = TextServer::AUTOWRAP_OFF;
+ BitField autowrap_flags_trim = TextServer::BREAK_TRIM_END_EDGE_SPACES;
TextServer::OverrunBehavior overrun_behavior = TextServer::OVERRUN_NO_TRIMMING;
Ref icon;
@@ -131,6 +132,9 @@ public:
void set_autowrap_mode(TextServer::AutowrapMode p_mode);
TextServer::AutowrapMode get_autowrap_mode() const;
+ void set_autowrap_trim_flags(BitField p_flags);
+ BitField get_autowrap_trim_flags() const;
+
void set_text_direction(TextDirection p_text_direction);
TextDirection get_text_direction() const;
diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp
index 59ac5718e05..fe66819b091 100644
--- a/scene/gui/item_list.cpp
+++ b/scene/gui/item_list.cpp
@@ -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);
}
diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp
index e503a1d7fd4..176a72e9d6b 100644
--- a/scene/gui/label.cpp
+++ b/scene/gui/label.cpp
@@ -55,6 +55,27 @@ TextServer::AutowrapMode Label::get_autowrap_mode() const {
return autowrap_mode;
}
+void Label::set_autowrap_trim_flags(BitField p_flags) {
+ if (autowrap_flags_trim == (p_flags & TextServer::BREAK_TRIM_MASK)) {
+ return;
+ }
+
+ autowrap_flags_trim = p_flags & TextServer::BREAK_TRIM_MASK;
+ for (Paragraph ¶ : paragraphs) {
+ para.lines_dirty = true;
+ }
+ queue_redraw();
+ update_configuration_warnings();
+
+ if (clip || overrun_behavior != TextServer::OVERRUN_NO_TRIMMING) {
+ update_minimum_size();
+ }
+}
+
+BitField Label::get_autowrap_trim_flags() const {
+ return autowrap_flags_trim;
+}
+
void Label::set_justification_flags(BitField 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) {
@@ -1363,6 +1384,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);
@@ -1401,6 +1424,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");
diff --git a/scene/gui/label.h b/scene/gui/label.h
index 892127d8dc4..34b25d77262 100644
--- a/scene/gui/label.h
+++ b/scene/gui/label.h
@@ -50,6 +50,7 @@ private:
String text;
String xl_text;
TextServer::AutowrapMode autowrap_mode = TextServer::AUTOWRAP_OFF;
+ BitField autowrap_flags_trim = TextServer::BREAK_TRIM_START_EDGE_SPACES | TextServer::BREAK_TRIM_END_EDGE_SPACES;
BitField 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 p_flags);
+ BitField get_autowrap_trim_flags() const;
+
void set_justification_flags(BitField p_flags);
BitField get_justification_flags() const;
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index 6233d7441f5..2598ef23eb5 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -446,7 +446,7 @@ float RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref
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 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 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");
diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h
index 72c7cc0397b..013ee15c03f 100644
--- a/scene/gui/rich_text_label.h
+++ b/scene/gui/rich_text_label.h
@@ -472,6 +472,7 @@ private:
VScrollBar *vscroll = nullptr;
TextServer::AutowrapMode autowrap_mode = TextServer::AUTOWRAP_WORD_SMART;
+ BitField 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 p_flags);
+ BitField get_autowrap_trim_flags() const;
+
void set_structured_text_bidi_override(TextServer::StructuredTextParser p_parser);
TextServer::StructuredTextParser get_structured_text_bidi_override() const;
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index 04d8970c9c9..10cf912aabb 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -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 break_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_TRIM_EDGE_SPACES;
+ BitField 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;
diff --git a/servers/text_server.cpp b/servers/text_server.cpp
index f279ffa5147..653653971cb 100644
--- a/servers/text_server.cpp
+++ b/servers/text_server.cpp
@@ -549,8 +549,12 @@ void TextServer::_bind_methods() {
BIND_BITFIELD_FLAG(BREAK_WORD_BOUND);
BIND_BITFIELD_FLAG(BREAK_GRAPHEME_BOUND);
BIND_BITFIELD_FLAG(BREAK_ADAPTIVE);
+#ifndef DISABLE_DEPRECATED
BIND_BITFIELD_FLAG(BREAK_TRIM_EDGE_SPACES);
+#endif
BIND_BITFIELD_FLAG(BREAK_TRIM_INDENT);
+ BIND_BITFIELD_FLAG(BREAK_TRIM_START_EDGE_SPACES);
+ BIND_BITFIELD_FLAG(BREAK_TRIM_END_EDGE_SPACES);
/* VisibleCharactersBehavior */
BIND_ENUM_CONSTANT(VC_CHARS_BEFORE_SHAPING);
@@ -808,6 +812,12 @@ PackedInt32Array TextServer::shaped_text_get_line_breaks_adv(const RID &p_shaped
int prev_chunk = -1;
bool trim_next = false;
+#ifndef DISABLE_DEPRECATED
+ if (p_break_flags.has_flag(BREAK_TRIM_EDGE_SPACES)) {
+ p_break_flags = p_break_flags | BREAK_TRIM_START_EDGE_SPACES | BREAK_TRIM_END_EDGE_SPACES;
+ }
+#endif
+
int l_size = shaped_text_get_glyph_count(p_shaped);
const Glyph *l_gl = const_cast(this)->shaped_text_sort_logical(p_shaped);
@@ -848,13 +858,13 @@ PackedInt32Array TextServer::shaped_text_get_line_breaks_adv(const RID &p_shaped
}
if ((l_width > 0) && (width + adv > l_width) && (last_safe_break >= 0)) {
int cur_safe_brk = last_safe_break;
- if (p_break_flags.has_flag(BREAK_TRIM_EDGE_SPACES)) {
+ if (p_break_flags.has_flag(BREAK_TRIM_START_EDGE_SPACES) || p_break_flags.has_flag(BREAK_TRIM_END_EDGE_SPACES)) {
int start_pos = prev_safe_break;
int end_pos = last_safe_break;
- while (trim_next && (start_pos < end_pos) && ((l_gl[start_pos].flags & GRAPHEME_IS_SOFT_HYPHEN) != GRAPHEME_IS_SOFT_HYPHEN) && ((l_gl[start_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
+ while (p_break_flags.has_flag(BREAK_TRIM_START_EDGE_SPACES) && trim_next && (start_pos < end_pos) && ((l_gl[start_pos].flags & GRAPHEME_IS_SOFT_HYPHEN) != GRAPHEME_IS_SOFT_HYPHEN) && ((l_gl[start_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
start_pos += l_gl[start_pos].count;
}
- while ((start_pos < end_pos) && ((l_gl[end_pos].flags & GRAPHEME_IS_SOFT_HYPHEN) != GRAPHEME_IS_SOFT_HYPHEN) && ((l_gl[end_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
+ while (p_break_flags.has_flag(BREAK_TRIM_END_EDGE_SPACES) && (start_pos < end_pos) && ((l_gl[end_pos].flags & GRAPHEME_IS_SOFT_HYPHEN) != GRAPHEME_IS_SOFT_HYPHEN) && ((l_gl[end_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
end_pos -= l_gl[end_pos].count;
}
if (last_end <= l_gl[start_pos].start) {
@@ -892,13 +902,13 @@ PackedInt32Array TextServer::shaped_text_get_line_breaks_adv(const RID &p_shaped
if (p_break_flags.has_flag(BREAK_MANDATORY)) {
if ((l_gl[i].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD) {
int cur_safe_brk = i;
- if (p_break_flags.has_flag(BREAK_TRIM_EDGE_SPACES)) {
+ if (p_break_flags.has_flag(BREAK_TRIM_START_EDGE_SPACES) || p_break_flags.has_flag(BREAK_TRIM_END_EDGE_SPACES)) {
int start_pos = prev_safe_break;
int end_pos = i;
- while (trim_next && (start_pos < end_pos) && ((l_gl[start_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
+ while (p_break_flags.has_flag(BREAK_TRIM_START_EDGE_SPACES) && trim_next && (start_pos < end_pos) && ((l_gl[start_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
start_pos += l_gl[start_pos].count;
}
- while ((start_pos < end_pos) && ((l_gl[end_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
+ while (p_break_flags.has_flag(BREAK_TRIM_END_EDGE_SPACES) && (start_pos < end_pos) && ((l_gl[end_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
end_pos -= l_gl[end_pos].count;
}
if (last_end <= l_gl[start_pos].start) {
@@ -953,7 +963,7 @@ PackedInt32Array TextServer::shaped_text_get_line_breaks_adv(const RID &p_shaped
if (l_size > 0) {
if (lines.size() == 0 || (lines[lines.size() - 1] < range.y && prev_safe_break < l_size)) {
- if (p_break_flags.has_flag(BREAK_TRIM_EDGE_SPACES)) {
+ if (p_break_flags.has_flag(BREAK_TRIM_START_EDGE_SPACES)) {
int start_pos = (prev_safe_break < l_size) ? prev_safe_break : l_size - 1;
if (last_end <= l_gl[start_pos].start) {
int end_pos = l_size - 1;
@@ -991,6 +1001,12 @@ PackedInt32Array TextServer::shaped_text_get_line_breaks(const RID &p_shaped, do
int word_count = 0;
bool trim_next = false;
+#ifndef DISABLE_DEPRECATED
+ if (p_break_flags.has_flag(BREAK_TRIM_EDGE_SPACES)) {
+ p_break_flags = p_break_flags | BREAK_TRIM_START_EDGE_SPACES | BREAK_TRIM_END_EDGE_SPACES;
+ }
+#endif
+
TextServer::Orientation orientation = shaped_text_get_orientation(p_shaped);
int l_size = shaped_text_get_glyph_count(p_shaped);
const Glyph *l_gl = const_cast(this)->shaped_text_sort_logical(p_shaped);
@@ -1025,13 +1041,13 @@ PackedInt32Array TextServer::shaped_text_get_line_breaks(const RID &p_shaped, do
}
if ((l_width > 0) && (width + adv > l_width) && (last_safe_break >= 0)) {
int cur_safe_brk = last_safe_break;
- if (p_break_flags.has_flag(BREAK_TRIM_EDGE_SPACES)) {
+ if (p_break_flags.has_flag(BREAK_TRIM_START_EDGE_SPACES) || p_break_flags.has_flag(BREAK_TRIM_END_EDGE_SPACES)) {
int start_pos = prev_safe_break;
int end_pos = last_safe_break;
- while (trim_next && (start_pos < end_pos) && ((l_gl[start_pos].flags & GRAPHEME_IS_SOFT_HYPHEN) != GRAPHEME_IS_SOFT_HYPHEN) && ((l_gl[start_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
+ while (p_break_flags.has_flag(BREAK_TRIM_START_EDGE_SPACES) && trim_next && (start_pos < end_pos) && ((l_gl[start_pos].flags & GRAPHEME_IS_SOFT_HYPHEN) != GRAPHEME_IS_SOFT_HYPHEN) && ((l_gl[start_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
start_pos += l_gl[start_pos].count;
}
- while ((start_pos < end_pos) && ((l_gl[end_pos].flags & GRAPHEME_IS_SOFT_HYPHEN) != GRAPHEME_IS_SOFT_HYPHEN) && ((l_gl[end_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
+ while (p_break_flags.has_flag(BREAK_TRIM_END_EDGE_SPACES) && (start_pos < end_pos) && ((l_gl[end_pos].flags & GRAPHEME_IS_SOFT_HYPHEN) != GRAPHEME_IS_SOFT_HYPHEN) && ((l_gl[end_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
end_pos -= l_gl[end_pos].count;
}
if (last_end <= l_gl[start_pos].start) {
@@ -1068,13 +1084,13 @@ PackedInt32Array TextServer::shaped_text_get_line_breaks(const RID &p_shaped, do
if (p_break_flags.has_flag(BREAK_MANDATORY)) {
if ((l_gl[i].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD) {
int cur_safe_brk = i;
- if (p_break_flags.has_flag(BREAK_TRIM_EDGE_SPACES)) {
+ if (p_break_flags.has_flag(BREAK_TRIM_START_EDGE_SPACES) || p_break_flags.has_flag(BREAK_TRIM_END_EDGE_SPACES)) {
int start_pos = prev_safe_break;
int end_pos = i;
- while (trim_next && (start_pos < end_pos) && ((l_gl[start_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
+ while (p_break_flags.has_flag(BREAK_TRIM_START_EDGE_SPACES) && trim_next && (start_pos < end_pos) && ((l_gl[start_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
start_pos += l_gl[start_pos].count;
}
- while ((start_pos < end_pos) && ((l_gl[end_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
+ while (p_break_flags.has_flag(BREAK_TRIM_END_EDGE_SPACES) && (start_pos < end_pos) && ((l_gl[end_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
end_pos -= l_gl[end_pos].count;
}
trim_next = true;
@@ -1134,7 +1150,7 @@ PackedInt32Array TextServer::shaped_text_get_line_breaks(const RID &p_shaped, do
if (l_size > 0) {
if (lines.size() == 0 || (lines[lines.size() - 1] < range.y && prev_safe_break < l_size)) {
- if (p_break_flags.has_flag(BREAK_TRIM_EDGE_SPACES)) {
+ if (p_break_flags.has_flag(BREAK_TRIM_START_EDGE_SPACES)) {
int start_pos = (prev_safe_break < l_size) ? prev_safe_break : l_size - 1;
if (last_end <= l_gl[start_pos].start) {
int end_pos = l_size - 1;
diff --git a/servers/text_server.h b/servers/text_server.h
index 2c974d64c4e..e9ee5c0909c 100644
--- a/servers/text_server.h
+++ b/servers/text_server.h
@@ -108,8 +108,16 @@ public:
BREAK_WORD_BOUND = 1 << 1,
BREAK_GRAPHEME_BOUND = 1 << 2,
BREAK_ADAPTIVE = 1 << 3,
+#ifndef DISABLE_DEPRECATED
BREAK_TRIM_EDGE_SPACES = 1 << 4,
+#else
+ // RESERVED = 1 << 4,
+#endif
BREAK_TRIM_INDENT = 1 << 5,
+ BREAK_TRIM_START_EDGE_SPACES = 1 << 6,
+ BREAK_TRIM_END_EDGE_SPACES = 1 << 7,
+
+ BREAK_TRIM_MASK = BREAK_TRIM_INDENT | BREAK_TRIM_START_EDGE_SPACES | BREAK_TRIM_END_EDGE_SPACES,
};
enum OverrunBehavior {
diff --git a/tests/servers/test_text_server.h b/tests/servers/test_text_server.h
index 7b071a5ec3c..7db377ec603 100644
--- a/tests/servers/test_text_server.h
+++ b/tests/servers/test_text_server.h
@@ -527,11 +527,17 @@ TEST_SUITE("[TextServer]") {
struct TestCase {
String text;
PackedInt32Array breaks;
+ BitField flags;
};
TestCase cases[] = {
- { U"test \rtest", { 0, 4, 6, 10 } },
- { U"test\r test", { 0, 4, 6, 10 } },
- { U"test\r test \r test", { 0, 4, 6, 10, 13, 17 } },
+ { U"test \rtest", { 0, 4, 6, 10 }, TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::BREAK_TRIM_START_EDGE_SPACES | TextServer::BREAK_TRIM_END_EDGE_SPACES },
+ { U"test \rtest", { 0, 6, 6, 10 }, TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::BREAK_TRIM_START_EDGE_SPACES },
+ { U"test\r test", { 0, 4, 6, 10 }, TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::BREAK_TRIM_START_EDGE_SPACES | TextServer::BREAK_TRIM_END_EDGE_SPACES },
+ { U"test\r test", { 0, 4, 5, 10 }, TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::BREAK_TRIM_END_EDGE_SPACES },
+ { U"test\r test \r test", { 0, 4, 6, 10, 13, 17 }, TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::BREAK_TRIM_START_EDGE_SPACES | TextServer::BREAK_TRIM_END_EDGE_SPACES },
+ { U"test\r test \r test", { 0, 5, 6, 12, 13, 17 }, TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::BREAK_TRIM_START_EDGE_SPACES },
+ { U"test\r test \r test", { 0, 4, 5, 10, 12, 17 }, TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::BREAK_TRIM_END_EDGE_SPACES },
+ { U"test\r test \r test", { 0, 5, 5, 12, 12, 17 }, TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND },
};
for (size_t j = 0; j < sizeof(cases) / sizeof(TestCase); j++) {
RID ctx = ts->create_shaped_text();
@@ -539,10 +545,10 @@ TEST_SUITE("[TextServer]") {
bool ok = ts->shaped_text_add_string(ctx, cases[j].text, font, 16);
CHECK_FALSE_MESSAGE(!ok, "Adding text to the buffer failed.");
- PackedInt32Array breaks = ts->shaped_text_get_line_breaks(ctx, 90.0, 0, TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::BREAK_TRIM_EDGE_SPACES);
+ PackedInt32Array breaks = ts->shaped_text_get_line_breaks(ctx, 90.0, 0, cases[j].flags);
CHECK_FALSE_MESSAGE(breaks != cases[j].breaks, "Invalid break points.");
- breaks = ts->shaped_text_get_line_breaks_adv(ctx, { 90.0 }, 0, false, TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::BREAK_TRIM_EDGE_SPACES);
+ breaks = ts->shaped_text_get_line_breaks_adv(ctx, { 90.0 }, 0, false, cases[j].flags);
CHECK_FALSE_MESSAGE(breaks != cases[j].breaks, "Invalid break points.");
ts->free_rid(ctx);
@@ -595,7 +601,7 @@ TEST_SUITE("[TextServer]") {
CHECK_FALSE_MESSAGE(brks[5] != 14, "Invalid line break position.");
}
- brks = ts->shaped_text_get_line_breaks(ctx, 35.0, 0, TextServer::BREAK_WORD_BOUND | TextServer::BREAK_MANDATORY | TextServer::BREAK_TRIM_EDGE_SPACES);
+ brks = ts->shaped_text_get_line_breaks(ctx, 35.0, 0, TextServer::BREAK_WORD_BOUND | TextServer::BREAK_MANDATORY | TextServer::BREAK_TRIM_START_EDGE_SPACES | TextServer::BREAK_TRIM_END_EDGE_SPACES);
CHECK_FALSE_MESSAGE(brks.size() != 6, "Invalid line breaks number.");
if (brks.size() == 6) {
CHECK_FALSE_MESSAGE(brks[0] != 0, "Invalid line break position.");