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.