From 309712551c97225272599c8f5f8ee84b72b5ef30 Mon Sep 17 00:00:00 2001
From: bruvzg <7645683+bruvzg@users.noreply.github.com>
Date: Wed, 9 Oct 2024 19:41:03 +0300
Subject: [PATCH] [TextParagraph/Button] Add support for line spacing.
---
doc/classes/Button.xml | 3 +++
doc/classes/Label.xml | 2 +-
doc/classes/Label3D.xml | 2 +-
doc/classes/LabelSettings.xml | 2 +-
doc/classes/RichTextLabel.xml | 2 +-
doc/classes/TextEdit.xml | 2 +-
doc/classes/TextMesh.xml | 2 +-
doc/classes/TextParagraph.xml | 3 +++
scene/gui/button.cpp | 2 ++
scene/gui/button.h | 1 +
scene/resources/text_paragraph.cpp | 41 ++++++++++++++++++++++++------
scene/resources/text_paragraph.h | 4 +++
12 files changed, 52 insertions(+), 14 deletions(-)
diff --git a/doc/classes/Button.xml b/doc/classes/Button.xml
index 68fb918904f..c774667a7db 100644
--- a/doc/classes/Button.xml
+++ b/doc/classes/Button.xml
@@ -127,6 +127,9 @@
The maximum allowed width of the [Button]'s icon. This limit is applied on top of the default size of the icon, or its expanded size if [member expand_icon] is [code]true[/code]. The height is adjusted according to the icon's ratio. If the button has additional icons (e.g. [CheckBox]), they will also be limited.
+
+ Additional vertical spacing between lines (in pixels), spacing is added to line descent. This value can be negative.
+
The size of the text outline.
[b]Note:[/b] If using a font with [member FontFile.multichannel_signed_distance_field] enabled, its [member FontFile.msdf_pixel_range] must be set to at least [i]twice[/i] the value of [theme_item outline_size] for outline rendering to look correct. Otherwise, the outline may appear to be cut off earlier than intended.
diff --git a/doc/classes/Label.xml b/doc/classes/Label.xml
index f91006f69a0..ae5a62753f1 100644
--- a/doc/classes/Label.xml
+++ b/doc/classes/Label.xml
@@ -122,7 +122,7 @@
[Color] of the text's shadow effect.
- Vertical space between lines in multiline [Label].
+ Additional vertical spacing between lines (in pixels), spacing is added to line descent. This value can be negative.
Text outline size.
diff --git a/doc/classes/Label3D.xml b/doc/classes/Label3D.xml
index ff26c5490dd..7584a1d526b 100644
--- a/doc/classes/Label3D.xml
+++ b/doc/classes/Label3D.xml
@@ -79,7 +79,7 @@
Language code used for line-breaking and text shaping algorithms, if left empty current locale is used instead.
- Vertical space between lines in multiline [Label3D].
+ Additional vertical spacing between lines (in pixels), spacing is added to line descent. This value can be negative.
Text [Color] of the [Label3D].
diff --git a/doc/classes/LabelSettings.xml b/doc/classes/LabelSettings.xml
index 8cdb30c3036..ff7b8e7b0e4 100644
--- a/doc/classes/LabelSettings.xml
+++ b/doc/classes/LabelSettings.xml
@@ -19,7 +19,7 @@
Size of the text.
- Vertical space between lines when the text is multiline.
+ Additional vertical spacing between lines (in pixels), spacing is added to line descent. This value can be negative.
The color of the outline.
diff --git a/doc/classes/RichTextLabel.xml b/doc/classes/RichTextLabel.xml
index 4a2cbbc3d8e..2ac346b5c2f 100644
--- a/doc/classes/RichTextLabel.xml
+++ b/doc/classes/RichTextLabel.xml
@@ -810,7 +810,7 @@
The default background color for odd rows.
- The vertical space between lines.
+ Additional vertical spacing between lines (in pixels), spacing is added to line descent. This value can be negative.
The size of the text outline.
diff --git a/doc/classes/TextEdit.xml b/doc/classes/TextEdit.xml
index 42558bf9929..7a4bef57479 100644
--- a/doc/classes/TextEdit.xml
+++ b/doc/classes/TextEdit.xml
@@ -1624,7 +1624,7 @@
The caret's width in pixels. Greater values can be used to improve accessibility by ensuring the caret is easily visible, or to ensure consistency with a large font size. If set to [code]0[/code] or lower, the caret width is automatically set to 1 pixel and multiplied by the display scaling factor.
- Sets the spacing between the lines.
+ Additional vertical spacing between lines (in pixels), spacing is added to line descent. This value can be negative.
The size of the text outline.
diff --git a/doc/classes/TextMesh.xml b/doc/classes/TextMesh.xml
index 898d19aed39..b6b1052af5f 100644
--- a/doc/classes/TextMesh.xml
+++ b/doc/classes/TextMesh.xml
@@ -37,7 +37,7 @@
Language code used for text shaping algorithms, if left empty current locale is used instead.
- Vertical space between lines in multiline [TextMesh].
+ Additional vertical spacing between lines (in pixels), spacing is added to line descent. This value can be negative.
The text drawing offset (in pixels).
diff --git a/doc/classes/TextParagraph.xml b/doc/classes/TextParagraph.xml
index 46197f19b88..c9ac660b8c4 100644
--- a/doc/classes/TextParagraph.xml
+++ b/doc/classes/TextParagraph.xml
@@ -280,6 +280,9 @@
Line fill alignment rules. See [enum TextServer.JustificationFlag] for more information.
+
+ Additional vertical spacing between lines (in pixels), spacing is added to line descent. This value can be negative.
+
Limits the lines of text shown.
diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp
index 0a5f2ec6c74..507e1f50684 100644
--- a/scene/gui/button.cpp
+++ b/scene/gui/button.cpp
@@ -568,6 +568,7 @@ void Button::_shape(Ref p_paragraph, String p_text) {
}
autowrap_flags = autowrap_flags | TextServer::BREAK_TRIM_EDGE_SPACES;
p_paragraph->set_break_flags(autowrap_flags);
+ p_paragraph->set_line_spacing(theme_cache.line_spacing);
if (text_direction == Control::TEXT_DIRECTION_INHERITED) {
p_paragraph->set_direction(is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR);
@@ -840,6 +841,7 @@ void Button::_bind_methods() {
BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, Button, icon_max_width);
BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, Button, align_to_largest_stylebox);
+ BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, Button, line_spacing);
}
Button::Button(const String &p_text) {
diff --git a/scene/gui/button.h b/scene/gui/button.h
index 5f4429bc1d3..e8e68f49bb0 100644
--- a/scene/gui/button.h
+++ b/scene/gui/button.h
@@ -100,6 +100,7 @@ private:
int h_separation = 0;
int icon_max_width = 0;
+ int line_spacing = 0;
} theme_cache;
void _shape(Ref p_paragraph = Ref(), String p_text = "");
diff --git a/scene/resources/text_paragraph.cpp b/scene/resources/text_paragraph.cpp
index 29a8541cb0e..2f0b626601f 100644
--- a/scene/resources/text_paragraph.cpp
+++ b/scene/resources/text_paragraph.cpp
@@ -112,6 +112,11 @@ void TextParagraph::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "max_lines_visible"), "set_max_lines_visible", "get_max_lines_visible");
+ ClassDB::bind_method(D_METHOD("set_line_spacing", "line_spacing"), &TextParagraph::set_line_spacing);
+ ClassDB::bind_method(D_METHOD("get_line_spacing"), &TextParagraph::get_line_spacing);
+
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "line_spacing"), "set_line_spacing", "get_line_spacing");
+
ClassDB::bind_method(D_METHOD("get_line_objects", "line"), &TextParagraph::get_line_objects);
ClassDB::bind_method(D_METHOD("get_line_object_rect", "line", "key"), &TextParagraph::get_line_object_rect);
ClassDB::bind_method(D_METHOD("get_line_size", "line"), &TextParagraph::get_line_size);
@@ -180,6 +185,7 @@ void TextParagraph::_shape_lines() {
for (int i = 0; i < line_breaks.size(); i = i + 2) {
RID line = TS->shaped_text_substr(rid, line_breaks[i], line_breaks[i + 1] - line_breaks[i]);
float h = (TS->shaped_text_get_orientation(line) == TextServer::ORIENTATION_HORIZONTAL) ? TS->shaped_text_get_size(line).y : TS->shaped_text_get_size(line).x;
+ h += line_spacing;
if (!tab_stops.is_empty()) {
TS->shaped_text_tab_align(line, tab_stops);
}
@@ -574,12 +580,18 @@ Size2 TextParagraph::get_size() const {
}
size.x = MAX(size.x, lsize.x);
size.y += lsize.y;
+ if (i != visible_lines - 1) {
+ size.y += line_spacing;
+ }
} else {
if (h_offset > 0 && i <= dropcap_lines) {
lsize.y += h_offset;
}
size.x += lsize.x;
size.y = MAX(size.y, lsize.y);
+ if (i != visible_lines - 1) {
+ size.x += line_spacing;
+ }
}
}
if (h_offset > 0) {
@@ -612,6 +624,19 @@ int TextParagraph::get_max_lines_visible() const {
return max_lines_visible;
}
+void TextParagraph::set_line_spacing(float p_spacing) {
+ _THREAD_SAFE_METHOD_
+
+ if (line_spacing != p_spacing) {
+ line_spacing = p_spacing;
+ lines_dirty = true;
+ }
+}
+
+float TextParagraph::get_line_spacing() const {
+ return line_spacing;
+}
+
Array TextParagraph::get_line_objects(int p_line) const {
_THREAD_SAFE_METHOD_
@@ -697,10 +722,10 @@ Rect2 TextParagraph::get_line_object_rect(int p_line, Variant p_key) const {
if (i != p_line) {
if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
ofs.x = 0.f;
- ofs.y += TS->shaped_text_get_descent(lines_rid[i]);
+ ofs.y += TS->shaped_text_get_descent(lines_rid[i]) + line_spacing;
} else {
ofs.y = 0.f;
- ofs.x += TS->shaped_text_get_descent(lines_rid[i]);
+ ofs.x += TS->shaped_text_get_descent(lines_rid[i]) + line_spacing;
}
}
}
@@ -872,10 +897,10 @@ void TextParagraph::draw(RID p_canvas, const Vector2 &p_pos, const Color &p_colo
TS->shaped_text_draw(lines_rid[i], p_canvas, ofs, clip_l, clip_l + l_width, p_color);
if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
ofs.x = p_pos.x;
- ofs.y += TS->shaped_text_get_descent(lines_rid[i]);
+ ofs.y += TS->shaped_text_get_descent(lines_rid[i]) + line_spacing;
} else {
ofs.y = p_pos.y;
- ofs.x += TS->shaped_text_get_descent(lines_rid[i]);
+ ofs.x += TS->shaped_text_get_descent(lines_rid[i]) + line_spacing;
}
}
}
@@ -974,10 +999,10 @@ void TextParagraph::draw_outline(RID p_canvas, const Vector2 &p_pos, int p_outli
TS->shaped_text_draw_outline(lines_rid[i], p_canvas, ofs, clip_l, clip_l + l_width, p_outline_size, p_color);
if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
ofs.x = p_pos.x;
- ofs.y += TS->shaped_text_get_descent(lines_rid[i]);
+ ofs.y += TS->shaped_text_get_descent(lines_rid[i]) + line_spacing;
} else {
ofs.y = p_pos.y;
- ofs.x += TS->shaped_text_get_descent(lines_rid[i]);
+ ofs.x += TS->shaped_text_get_descent(lines_rid[i]) + line_spacing;
}
}
}
@@ -1001,12 +1026,12 @@ int TextParagraph::hit_test(const Point2 &p_coords) const {
if ((p_coords.y >= ofs.y) && (p_coords.y <= ofs.y + TS->shaped_text_get_size(line_rid).y)) {
return TS->shaped_text_hit_test_position(line_rid, p_coords.x);
}
- ofs.y += TS->shaped_text_get_size(line_rid).y;
+ ofs.y += TS->shaped_text_get_size(line_rid).y + line_spacing;
} else {
if ((p_coords.x >= ofs.x) && (p_coords.x <= ofs.x + TS->shaped_text_get_size(line_rid).x)) {
return TS->shaped_text_hit_test_position(line_rid, p_coords.y);
}
- ofs.y += TS->shaped_text_get_size(line_rid).x;
+ ofs.y += TS->shaped_text_get_size(line_rid).x + line_spacing;
}
}
return TS->shaped_text_get_range(rid).y;
diff --git a/scene/resources/text_paragraph.h b/scene/resources/text_paragraph.h
index 7512955fb3b..c12fc34c042 100644
--- a/scene/resources/text_paragraph.h
+++ b/scene/resources/text_paragraph.h
@@ -51,6 +51,7 @@ private:
bool lines_dirty = true;
+ float line_spacing = 0.0;
float width = -1.0;
int max_lines_visible = -1;
@@ -122,6 +123,9 @@ public:
void set_max_lines_visible(int p_lines);
int get_max_lines_visible() const;
+ void set_line_spacing(float p_spacing);
+ float get_line_spacing() const;
+
Size2 get_non_wrapped_size() const;
Size2 get_size() const;