Is it possible to arrange nodes in such a way that a RichTextLabel would expand both horizontally and vertically? Essentially, I’d like to create chat bubbles that match the text length horizontally when they have just a single line, but then expand vertically when they have multiple lines.
I could definitely do it by code, but I’d prefer to use the correct container nodes to achieve this if it is possible.
Then you’ll need to set the RichTextLabel or its container Anchors preset under Layout to Custom and manually configure the Anchor Points and Anchor Offsets, and set its Grow DirectionVertical and Horizontal to Both
With this setup, I can make the RichTextLabel expand horizontally, but I cannot make it grow vertically, no matter what I do. Can you give specific examples of the Anchor Points and Anchor Offsets? Or a screenshot of the setup.
Changing the Fit Content and Autowrap Mode values I can only ever make it either expand horizontally or vertically, not both with a set max width.
I can replicate that behaviour. However, what I wanted is to have the same behaviour without forced line breaks. First, grow horizontally until a specific width is reached, then grow vertically with automatic line breaks. Is that possible with just nodes and their parameters or do I have to add forced line breaks in code to achieve this?
If not, the easiest way to achieve this is probably inserting forced line breaks via code. Just a bit messy as not all characters are the same width so it’s not super simple to calculate where to accurately place those line breaks.
It’s only possible by adding line breaks. As long as you don’t use BBCode then it’s possible to add them automatically by per-calculating them before showing the text on screen.
Example:
extends Node
@onready var panel_container: PanelContainer = $PanelContainer
@onready var rich_text_label: RichTextLabel = $PanelContainer/RichTextLabel
func _ready() -> void:
await get_tree().create_timer(5).timeout
await message("""Donec felis augue, bibendum fermentum lectus in, sagittis commodo risus. Aliquam vestibulum mauris vitae rhoncus fermentum. Aliquam pulvinar posuere nulla. Ut vel imperdiet orci. Aliquam ac leo pretium, auctor dui et, fringilla ex. Fusce id volutpat velit, nec tincidunt quam. Suspendisse in mattis justo. Nullam venenatis at nisl in venenatis. Cras eleifend congue dictum. Vestibulum pharetra laoreet sapien sit amet luctus. Phasellus viverra enim sit amet imperdiet rhoncus. Aenean eget dolor a mi porttitor efficitur eu sit amet libero.""")
await get_tree().create_timer(2).timeout
await message("""Fusce vitae ornare felis. Phasellus mattis pulvinar commodo. In hac habitasse platea dictumst. Suspendisse auctor id mi a aliquet. Quisque ut tortor maximus, eleifend sapien a, semper nibh. In libero libero, cursus in nisi ut, fringilla bibendum purus. In semper quam eget purus sodales euismod. Vivamus hendrerit, ipsum vel tempus ultricies, turpis dui tempus dolor, eget accumsan mi erat et dui. Sed turpis metus, pretium ut nulla eu, lobortis varius purus. Nulla vehicula ex vitae libero aliquet elementum. Nam tempor dui scelerisque sollicitudin dapibus.""")
func message(text:String) -> void:
# Pre-calculate the final text lines
panel_container.modulate.a = 0
panel_container.custom_minimum_size.x = 500
rich_text_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
rich_text_label.text = text
# Await a couple frames so everything is correctly setup
await get_tree().process_frame
await get_tree().process_frame
# Get the lines and create the new text by appending a line break
var new_text = ""
for line in rich_text_label.get_line_count():
var range = rich_text_label.get_line_range(line)
new_text += text.substr(range.x, range.y-range.x) + "\n"
# Disable autowrap and set the new text
rich_text_label.autowrap_mode = TextServer.AUTOWRAP_OFF
rich_text_label.text = new_text
# Set the visible ration to 0
rich_text_label.visible_ratio = 0.0
# Position and reset the size of the panel container
var vp_size = get_viewport().size
panel_container.position = vp_size / 2.0
panel_container.grow_horizontal = Control.GROW_DIRECTION_BOTH
panel_container.grow_vertical = Control.GROW_DIRECTION_BOTH
panel_container.custom_minimum_size = Vector2.ZERO
panel_container.size = Vector2.ZERO
# Tween the visible ratio
var tween = create_tween()
tween.tween_property(panel_container, ^"modulate:a", 1.0, 0.15)
tween.parallel().tween_property(rich_text_label, ^"visible_ratio", 1.0, 5.0)
await tween.finished
If you are using bbcode then it’s not possible to add them easily via code.
I hear you and I raise you my solution without adding line breaks
You can ignore the _type_letter() function later, this is just for filling in the label.
extends RichTextLabel
const MAX_WIDTH: float = 500.0
func _ready() -> void:
resized.connect(_on_resized)
$"../Timer".timeout.connect(_type_letter)
text = ""
func _type_letter() -> void:
text += char(randi_range(97, 123))
text += " " if randf() > 0.7 else ""
func _on_resized() -> void:
if size.x > MAX_WIDTH:
autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
size.x = MAX_WIDTH