Browse Source

Merge pull request #98036 from bruvzg/para_btn_spacing

[TextParagraph/Button] Add support for line spacing.
Thaddeus Crews 8 months ago
parent
commit
6bbc1cb6a9

+ 3 - 0
doc/classes/Button.xml

@@ -127,6 +127,9 @@
 		<theme_item name="icon_max_width" data_type="constant" type="int" default="0">
 		<theme_item name="icon_max_width" data_type="constant" type="int" default="0">
 			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.
 			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.
 		</theme_item>
 		</theme_item>
+		<theme_item name="line_spacing" data_type="constant" type="int" default="0">
+			Additional vertical spacing between lines (in pixels), spacing is added to line descent. This value can be negative.
+		</theme_item>
 		<theme_item name="outline_size" data_type="constant" type="int" default="0">
 		<theme_item name="outline_size" data_type="constant" type="int" default="0">
 			The size of the text outline.
 			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.
 			[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.

+ 1 - 1
doc/classes/Label.xml

@@ -122,7 +122,7 @@
 			[Color] of the text's shadow effect.
 			[Color] of the text's shadow effect.
 		</theme_item>
 		</theme_item>
 		<theme_item name="line_spacing" data_type="constant" type="int" default="3">
 		<theme_item name="line_spacing" data_type="constant" type="int" default="3">
-			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.
 		</theme_item>
 		</theme_item>
 		<theme_item name="outline_size" data_type="constant" type="int" default="0">
 		<theme_item name="outline_size" data_type="constant" type="int" default="0">
 			Text outline size.
 			Text outline size.

+ 1 - 1
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.
 			Language code used for line-breaking and text shaping algorithms, if left empty current locale is used instead.
 		</member>
 		</member>
 		<member name="line_spacing" type="float" setter="set_line_spacing" getter="get_line_spacing" default="0.0">
 		<member name="line_spacing" type="float" setter="set_line_spacing" getter="get_line_spacing" default="0.0">
-			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.
 		</member>
 		</member>
 		<member name="modulate" type="Color" setter="set_modulate" getter="get_modulate" default="Color(1, 1, 1, 1)" keywords="color, colour">
 		<member name="modulate" type="Color" setter="set_modulate" getter="get_modulate" default="Color(1, 1, 1, 1)" keywords="color, colour">
 			Text [Color] of the [Label3D].
 			Text [Color] of the [Label3D].

+ 1 - 1
doc/classes/LabelSettings.xml

@@ -19,7 +19,7 @@
 			Size of the text.
 			Size of the text.
 		</member>
 		</member>
 		<member name="line_spacing" type="float" setter="set_line_spacing" getter="get_line_spacing" default="3.0">
 		<member name="line_spacing" type="float" setter="set_line_spacing" getter="get_line_spacing" default="3.0">
-			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.
 		</member>
 		</member>
 		<member name="outline_color" type="Color" setter="set_outline_color" getter="get_outline_color" default="Color(1, 1, 1, 1)">
 		<member name="outline_color" type="Color" setter="set_outline_color" getter="get_outline_color" default="Color(1, 1, 1, 1)">
 			The color of the outline.
 			The color of the outline.

+ 1 - 1
doc/classes/RichTextLabel.xml

@@ -810,7 +810,7 @@
 			The default background color for odd rows.
 			The default background color for odd rows.
 		</theme_item>
 		</theme_item>
 		<theme_item name="line_separation" data_type="constant" type="int" default="0">
 		<theme_item name="line_separation" data_type="constant" type="int" default="0">
-			The vertical space between lines.
+			Additional vertical spacing between lines (in pixels), spacing is added to line descent. This value can be negative.
 		</theme_item>
 		</theme_item>
 		<theme_item name="outline_size" data_type="constant" type="int" default="0">
 		<theme_item name="outline_size" data_type="constant" type="int" default="0">
 			The size of the text outline.
 			The size of the text outline.

+ 1 - 1
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.
 			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.
 		</theme_item>
 		</theme_item>
 		<theme_item name="line_spacing" data_type="constant" type="int" default="4">
 		<theme_item name="line_spacing" data_type="constant" type="int" default="4">
-			Sets the spacing between the lines.
+			Additional vertical spacing between lines (in pixels), spacing is added to line descent. This value can be negative.
 		</theme_item>
 		</theme_item>
 		<theme_item name="outline_size" data_type="constant" type="int" default="0">
 		<theme_item name="outline_size" data_type="constant" type="int" default="0">
 			The size of the text outline.
 			The size of the text outline.

+ 1 - 1
doc/classes/TextMesh.xml

@@ -37,7 +37,7 @@
 			Language code used for text shaping algorithms, if left empty current locale is used instead.
 			Language code used for text shaping algorithms, if left empty current locale is used instead.
 		</member>
 		</member>
 		<member name="line_spacing" type="float" setter="set_line_spacing" getter="get_line_spacing" default="0.0">
 		<member name="line_spacing" type="float" setter="set_line_spacing" getter="get_line_spacing" default="0.0">
-			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.
 		</member>
 		</member>
 		<member name="offset" type="Vector2" setter="set_offset" getter="get_offset" default="Vector2(0, 0)">
 		<member name="offset" type="Vector2" setter="set_offset" getter="get_offset" default="Vector2(0, 0)">
 			The text drawing offset (in pixels).
 			The text drawing offset (in pixels).

+ 3 - 0
doc/classes/TextParagraph.xml

@@ -280,6 +280,9 @@
 		<member name="justification_flags" type="int" setter="set_justification_flags" getter="get_justification_flags" enum="TextServer.JustificationFlag" is_bitfield="true" default="163">
 		<member name="justification_flags" type="int" setter="set_justification_flags" getter="get_justification_flags" enum="TextServer.JustificationFlag" is_bitfield="true" default="163">
 			Line fill alignment rules. See [enum TextServer.JustificationFlag] for more information.
 			Line fill alignment rules. See [enum TextServer.JustificationFlag] for more information.
 		</member>
 		</member>
+		<member name="line_spacing" type="float" setter="set_line_spacing" getter="get_line_spacing" default="0.0">
+			Additional vertical spacing between lines (in pixels), spacing is added to line descent. This value can be negative.
+		</member>
 		<member name="max_lines_visible" type="int" setter="set_max_lines_visible" getter="get_max_lines_visible" default="-1">
 		<member name="max_lines_visible" type="int" setter="set_max_lines_visible" getter="get_max_lines_visible" default="-1">
 			Limits the lines of text shown.
 			Limits the lines of text shown.
 		</member>
 		</member>

+ 2 - 0
scene/gui/button.cpp

@@ -561,6 +561,7 @@ void Button::_shape(Ref<TextParagraph> p_paragraph, String p_text) {
 	}
 	}
 	autowrap_flags = autowrap_flags | TextServer::BREAK_TRIM_EDGE_SPACES;
 	autowrap_flags = autowrap_flags | TextServer::BREAK_TRIM_EDGE_SPACES;
 	p_paragraph->set_break_flags(autowrap_flags);
 	p_paragraph->set_break_flags(autowrap_flags);
+	p_paragraph->set_line_spacing(theme_cache.line_spacing);
 
 
 	if (text_direction == Control::TEXT_DIRECTION_INHERITED) {
 	if (text_direction == Control::TEXT_DIRECTION_INHERITED) {
 		p_paragraph->set_direction(is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR);
 		p_paragraph->set_direction(is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR);
@@ -833,6 +834,7 @@ void Button::_bind_methods() {
 	BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, Button, icon_max_width);
 	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, align_to_largest_stylebox);
+	BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, Button, line_spacing);
 }
 }
 
 
 Button::Button(const String &p_text) {
 Button::Button(const String &p_text) {

+ 1 - 0
scene/gui/button.h

@@ -100,6 +100,7 @@ private:
 
 
 		int h_separation = 0;
 		int h_separation = 0;
 		int icon_max_width = 0;
 		int icon_max_width = 0;
+		int line_spacing = 0;
 	} theme_cache;
 	} theme_cache;
 
 
 	void _shape(Ref<TextParagraph> p_paragraph = Ref<TextParagraph>(), String p_text = "");
 	void _shape(Ref<TextParagraph> p_paragraph = Ref<TextParagraph>(), String p_text = "");

+ 33 - 8
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");
 	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_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_object_rect", "line", "key"), &TextParagraph::get_line_object_rect);
 	ClassDB::bind_method(D_METHOD("get_line_size", "line"), &TextParagraph::get_line_size);
 	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) {
 			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]);
 				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;
 				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()) {
 				if (!tab_stops.is_empty()) {
 					TS->shaped_text_tab_align(line, tab_stops);
 					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.x = MAX(size.x, lsize.x);
 			size.y += lsize.y;
 			size.y += lsize.y;
+			if (i != visible_lines - 1) {
+				size.y += line_spacing;
+			}
 		} else {
 		} else {
 			if (h_offset > 0 && i <= dropcap_lines) {
 			if (h_offset > 0 && i <= dropcap_lines) {
 				lsize.y += h_offset;
 				lsize.y += h_offset;
 			}
 			}
 			size.x += lsize.x;
 			size.x += lsize.x;
 			size.y = MAX(size.y, lsize.y);
 			size.y = MAX(size.y, lsize.y);
+			if (i != visible_lines - 1) {
+				size.x += line_spacing;
+			}
 		}
 		}
 	}
 	}
 	if (h_offset > 0) {
 	if (h_offset > 0) {
@@ -612,6 +624,19 @@ int TextParagraph::get_max_lines_visible() const {
 	return max_lines_visible;
 	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 {
 Array TextParagraph::get_line_objects(int p_line) const {
 	_THREAD_SAFE_METHOD_
 	_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 (i != p_line) {
 			if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
 			if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
 				ofs.x = 0.f;
 				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 {
 			} else {
 				ofs.y = 0.f;
 				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);
 		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) {
 		if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
 			ofs.x = p_pos.x;
 			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 {
 		} else {
 			ofs.y = p_pos.y;
 			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);
 		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) {
 		if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
 			ofs.x = p_pos.x;
 			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 {
 		} else {
 			ofs.y = p_pos.y;
 			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)) {
 			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);
 				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 {
 		} else {
 			if ((p_coords.x >= ofs.x) && (p_coords.x <= ofs.x + TS->shaped_text_get_size(line_rid).x)) {
 			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);
 				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;
 	return TS->shaped_text_get_range(rid).y;

+ 4 - 0
scene/resources/text_paragraph.h

@@ -51,6 +51,7 @@ private:
 
 
 	bool lines_dirty = true;
 	bool lines_dirty = true;
 
 
+	float line_spacing = 0.0;
 	float width = -1.0;
 	float width = -1.0;
 	int max_lines_visible = -1;
 	int max_lines_visible = -1;
 
 
@@ -122,6 +123,9 @@ public:
 	void set_max_lines_visible(int p_lines);
 	void set_max_lines_visible(int p_lines);
 	int get_max_lines_visible() const;
 	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_non_wrapped_size() const;
 
 
 	Size2 get_size() const;
 	Size2 get_size() const;