How to scale text dynamically?

Godot Version

v4.5.1.stable.official [f62fdbde1]

Question

I have a Label node “DisplayWord” that shows the word the user typed. Therefore it could be anything from ‘Cat’ to ‘Asphyxiation’.

I want DisplayWord to scale dynamically and occupy the entire canvas to fit whatever word is typed without clipping.
I placed DisplayWord in a PanelContainer node and scale the container dynamically. This does work as expected functionally, but the font looks ugly after scaling to a different size. There seem to be a lot of interpolation artifacts.
I want this to look crisp after scaling, much like “Nearest” interpolation for texture images.

Here’s how it looks after the dynamic scaling.

I have tried .ttf, .otf and .fnt files, but no luck

Turn on Multi-channel signed distance field (MSDF) for the default font in the Project Settings.

1 Like

I have heard that text only scales well with the font size in multiples of 8, so maybe try snapping the font size to the nearest multiple of 8.

1 Like

Scaling the Label node won’t give you good results. You can use Container.pre_sort_children to calculate the final font size of the string using Font.get_string_size() like:

extends Container


func _ready() -> void:
	# Whenever pre_sort_children is emitted
	pre_sort_children.connect(func():
		for child in get_children():
			# Find all the Label chidren
			if not child is Label:
				continue

			child = child as Label

			# Get the font from the theme
			var font = child.get_theme_font("font")
			# And start from the size of the container as its font_size
			var font_size = floor(size.y)
			# Keep the final text size here
			var text_size = Vector2.ZERO

			# In an infinite loop
			while true:
				# Get the size of the string from the font
				text_size = font.get_string_size(child.text, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size)
				if text_size.x > size.x:
					# And if it's bigger, lower the font size and try again
					font_size -= 5
				else:
					# If not, then exit the loop
					break

			# Change their font_size
			child.add_theme_font_size_override("font_size", font_size)
			# And fit them into the container
			fit_child_in_rect(child, Rect2(Vector2.ZERO, size))
	)

Result:

1 Like

Thank you!
This worked well for the default font and is great for prototyping.
However since I want to use a themed font in production, I would like to test out @mrcdk ‘s solution.

1 Like

I couldn’t quite get this working. I tried anchoring the Label in the container in different ways too.
Could you please share your scene tree and any layout settings you may have changed to get this running?

Here’s a self-contained tscn file:

resize_label_example.tscn
[gd_scene load_steps=3 format=3 uid="uid://6m2sxb85ivd3"]

[sub_resource type="GDScript" id="GDScript_gtqu1"]
script/source = "extends VBoxContainer


@onready var line_edit: LineEdit = $LineEdit
@onready var label: Label = $Container/Label


func _ready() -> void:
	line_edit.text_changed.connect(func(new_text: String):
		label.text = new_text
	)
"

[sub_resource type="GDScript" id="GDScript_6s3pd"]
script/source = "extends Container


func _ready() -> void:
	# Whenever pre_sort_children is emitted
	pre_sort_children.connect(func():
		for child in get_children():
			# Find all the Label chidren
			if not child is Label:
				continue

			child = child as Label

			# Get the font from the theme
			var font = child.get_theme_font(\"font\")
			# And start from the size of the container as its font_size
			var font_size = floor(size.y)
			# Keep the final text size here
			var text_size = Vector2.ZERO

			# In an infinite loop
			while true:
				# Get the size of the string from the font
				text_size = font.get_string_size(child.text, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size)
				if text_size.x > size.x:
					# And if it's bigger, lower the font size and try again
					font_size -= 5
				else:
					# If not, then exit the loop
					break

			# Change their font_size
			child.add_theme_font_size_override(\"font_size\", font_size)
			# And fit them into the container
			fit_child_in_rect(child, Rect2(Vector2.ZERO, size))
	)
"

[node name="ResizeLabelExample" type="VBoxContainer"]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
theme_override_constants/separation = 16
script = SubResource("GDScript_gtqu1")

[node name="Container" type="Container" parent="."]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
script = SubResource("GDScript_6s3pd")

[node name="Label" type="Label" parent="Container"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 6
theme_override_font_sizes/font_size = 16
horizontal_alignment = 1
vertical_alignment = 1

[node name="LineEdit" type="LineEdit" parent="."]
layout_mode = 2
2 Likes

Thanks, works great!