Is there auto font size like in Unity

Here is an improved, iterative algorithm relying on the same method, just using the divide and conquer approach. This approach results in us getting between 1-9 iterations at any time, with any size value and any text size length, while also not having a constant iteration factor like 20 in the previous comment. It also supports a minimum font size.

I used RichTextLabel here, but you quite literally only need to change the extends RichTextLabel to extends Label and you would be ready to go.


AutoSizer.gd:

# AutoSizer is just a class to remove the hassle of code duplication. Feel free to remove it, if you only need one type of Label.
class_name AutoSizer

static func update_font_size_label(label: AutoSizeLabel) -> void:
	_update_font_size(label, "font", "font_size", Vector2i(label.min_font_size, label.max_font_size), label.text)

static func update_font_size_richlabel(label: AutoSizeRichLabel) -> void:
	_update_font_size(label, "normal_font", "normal_font_size", Vector2i(label.min_font_size, label.max_font_size), label.text)

static func _update_font_size(label: Control, font_name: StringName, font_style_name: StringName, font_size_range: Vector2i, text: String) -> void:
	var font := label.get_theme_font(font_name)

	var line := TextLine.new()
	line.direction = label.text_direction as TextServer.Direction
	line.flags = TextServer.JUSTIFICATION_NONE
	line.alignment = HORIZONTAL_ALIGNMENT_LEFT
	
	while true:
		line.clear()
		
		var mid_font_size := font_size_range.x + roundi((font_size_range.y - font_size_range.x) * 0.5)
		if !line.add_string(text, font, mid_font_size):
			push_warning("Could not create a string!")
			return
		
		var text_width := line.get_line_width()
		if text_width >= floori(label.size.x):
			if font_size_range.y == mid_font_size:
				break
			
			font_size_range.y = mid_font_size
		
		if text_width < floori(label.size.x):
			if font_size_range.x == mid_font_size:
				break
			
			font_size_range.x = mid_font_size
	
	label.add_theme_font_size_override(font_style_name, font_size_range.x)



AutoSizeLabel.gd:

@tool
class_name AutoSizeLabel extends Label

@export var min_font_size := 8 :
	set(v):
		min_font_size = clampi(v, 1, max_font_size)
		update()

@export var max_font_size := 56 :
	set(v):
		max_font_size = clampi(v, min_font_size, 191)
		update()

func _ready() -> void:
	clip_text = true
	item_rect_changed.connect(update)

func _set(property: StringName, value: Variant) -> bool:
	# Listen for changes to text
	if property == "text":
		text = value
		update()
		return true
	
	return false

func update() -> void:
	return AutoSizer.update_font_size_label(self)



AutoSizeRichLabel.gd:

@tool
class_name AutoSizeRichLabel extends RichTextLabel

@export var min_font_size := 8 :
	set(v):
		min_font_size = clampi(v, 1, max_font_size)
		update()

@export var max_font_size := 56 :
	set(v):
		max_font_size = clampi(v, min_font_size, 191)
		update()

func _ready() -> void:
	item_rect_changed.connect(update)

func _set(property: StringName, value: Variant) -> bool:
	# Listen for changes to text
	if property == "text":
		text = value
		update()
		return true
	
	return false

func update() -> void:
	return AutoSizer.update_font_size_richlabel(self)



PS: Sorry for necroposting, but searching on Google for “Godot Font Autosize” returns this back as the top result and I wanted to share a better solution.

2 Likes