Godot version:
3.1.alpha.official (did a similar code in 3.0.6)
Minimal reproduction project:
request_textbestfit.zip
This is a Feature Request for controls with text, like Labels.
I was looking for a way to reduce the font size of a Label depending on the text that is automatically loaded by the TranslationServer. As we know, a text in a certain language will probably have a different length comparing with others:
This is a single line of text in a Label with cliptext and wordwrap options checked, so this node doesn't resize to a bigger rect automatically
'pt_BR' locale:
Essa é uma única linha de texto em um Rótulo com as opções corte de texto e quebra de linhas marcadas, para que essa node não se redimensione para um retângulo maior automaticamente
So I created a script (TextHelper.gd) and attached it to a Label with a Custom Font + Dynamic Font, so it runs the following function when it enters the tree (automatically) and when the text changes (manually):
export(int) var max_font_size : int = 40 #setget max_font_size_change
export(String) var textx : String = "" #setget text_change (tool and setters only necessary if testing the actual text string and not the id of the TranslationServer)func _enter_tree():
thg.add(self) #TextHelperGlobal Singleton, explained below#Runs the check function entering the tree so the font size is changed
#when we run the game and the text loaded by the TranslationServer is already there
best_fit_check()func best_fit_check():
print_debug("best_fit_check")
var font = get("custom_fonts/font")
if font == null:
returnfont.set("size", max_font_size)
var font_size = max_font_size
print_debug("best_fit_check " + str(get_visible_line_count()) + " " +
str(get_line_count()) )
while get_visible_line_count() < get_line_count():
font.set("size", font_size - 1)
font_size = font.get("size")
pass
This is how it looks in the Editor:

And this is what happens when the best_fit_check() runs entering the tree:

Font size reduces from 38 to 30.
So, I think this essential for a fixed layout with fixed label sizes, and probably should be an option on every Text control.
As we don't have a way to easily check all Label controls (unlike the TranslationServer that has a list hidden somewhere), I create an Autoload Singleton to hold a list of these labels:
extends Node
var labels = []
func _ready():
pass # Replace with function body.func add(label):
if !labels.has(label):
labels.append(label)func remove(label):
if labels.has(label):
labels.erase(label)func best_fit_check():
for node in labels:
if is_instance_valid(node):
node.best_fit_check()
To test changing the language in Runtime, I attached this little script to the main node (not the label):
func _ready():
yield(get_tree().create_timer(5.0), "timeout")
TranslationServer.set_locale("pt_BR")
thg.best_fit_check()
And what happens:

The TranslationServer automatically changes the text based on it's hidden list of label nodes, and the Singleton uses it's own list to run the best_fit_check() and resize the font size if necessary.
​
So, this works, but... We need the script attached to every Label (or adapt it and attach to a child of the label... you know the drill) and we need the Singleton. What I think it could be done in the engine is:
Add a 'best fit' option in the text control. With both cliptext and wordwrap, it should automatically resize the Dynamic Font size when the text is changed
Expose the list of label nodes in the TranslationServer
Make the TranslationServer auto resize the label's fonts when we call set_locale, assuming the 'best fit' is implemented.
​Thanks.
I think we need two modes of best fit (drop down menu to select mode would be great). One mode of best fit will scale text to fit in text field meanwhile second one will scale text field to not clip text. I'm sure both modes will be very useful in certain situations.
-Garrom
In the interim would adding the recent class_name functionality to your script be helpful for you (as you could then add a, say, TranslatedLabel node directly to a scene)?
Hey, I'm interested in this! Is there any update on the issue?
This is my workaround for a BestFit in C#
This is a critical feature and is impossible to handle really localized games with large texts and dialogs without it. I really wish someone take a look on it soon as possible but this can be done in the meanwhile.
public override void _EnterTree(){
font = Get("custom_fonts/font") as DynamicFont;
originalSize = font.Size;
}
public void FitOnBox(){
font.Size = originalSize;
if(Autowrap){ // MultiLine Mode check the count of visible lines and real lines
while(GetVisibleLineCount() < GetLineCount()){
font.Size -=1;
if(font.Size <= minSize){
font.Size = minSize;
break;
}
}
}
else{ // Single Line Check for the string size at this font
string text = Tr(Text);
rectSize = font.GetStringSize(text);
while(rectSize.y > RectSize.y || rectSize.x > RectSize.x ){
text = Tr(Text);
rectSize = font.GetStringSize(text);
font.Size -=1;
if(font.Size <= minSize){
font.Size = minSize;
break;
}
}
}
}
@vitorbalbio thank you for that piece of code, works great for my purposes. Just a reminder if other people want to use it. Since that code modifies the font resource itself all other labels using that font will also change. Got some rather unexpected results on my first try :-D
@vitorbalbio thank you for that piece of code, works great for my purposes. Just a reminder if other people want to use it. Since that code modifies the font resource itself all other labels using that font will also change. Got some rather unexpected results on my first try :-D
Yeah. Usually i make all labels with unique Font Data using the "Make Unique" function since there's no local font size.