I’m currently messing around with a UI element that I’d like to display float values, but always have them display two decimal places, i.e 17.50. The only problem I seem to be left with is for values like 17.50, it displays 17.5, and I don’t see a clear way to override this behavior. SpinBox does have get_line_edit(), but setting the .text property of that doesn’t seem to do anything, so I’m out of ideas for now.
I’d really prefer not to have to implement something directly based on Range just to have it always display to two decimal places. I guess at that point I’d abandon most of the features and just have it be a LineEdit that does a little validation like I do now, but I like the arrows and prefix and other affordances that come for free with it.
Oh wow, interesting! First off, thanks for your help. That does have an effect, and does work… right up until I click back in the SpinBox, at which point the decimal point goes away again (and stays away until I input a different value – re-entering the same one doesn’t make it come back).
I tried to apply your solution and add a deferred signal connection to focus_entered with the same function, but that didn’t do the trick unfortunately.
The more I mess with things the more I see I’m trying to hack at something that doesn’t want to be hacked.
So once again, thank you for your help. I’m probably going to poke at this some more (and/or give up soon), and if I figure out all the things, I’ll post back here. I noticed that manually modifying the LineEdit messes with the prefix I set as well (hence my first comment).
Thank you so much for helping a stranger on the internet try to figure out this random thing. The two paths seem to be to use trial-and-error to mess with SpinBox until I figure it out, or build my own from LineEdit or Range. Either one feels like a lot of work for something relatively minor, so I’ll probably put it off for a while.
Okay @mrcdk thank you, you helped me solve it. What seems to get it to work is three connections, to value_changed, get_line_edit().focus_entered, and get_line_edit().focus_exited. With those three, alongside ensuring I manually reset the prefix of the SpinBox, I seem to get my desired behavior! They do all seem to need to be CONNECT_DEFERRED, and I’m not quite sure why, but I’m happy with the result! If I clean it up perhaps I’ll post a short sample here.
For anyone coming after me, here’s this. Not sure this warrants a whole asset, but hopefully this helps someone else!
class_name FormatSpinBox extends SpinBox
## A format field for numbers that allows for more control than [SpinBox].
##
## This can be used to, for example, always display values with a specific precision.
## [code]%.2f[/code] will display floats with two decimal places.
## A format string with a single specifier to display the box value.
## See [method String.format] for more information on format strings.
@export var format_string: String = "%s"
## Convenience initializer that sets [member format_string] to [param fstring] if set.
func _init(fstring: String = "") -> void:
if not fstring.is_empty():
format_string = fstring
# Connect the signal handlers required to make this work seamlessly.
func _ready() -> void:
value_changed.connect(_on_value_changed, CONNECT_DEFERRED)
get_line_edit().focus_entered.connect(_on_focus_entered, CONNECT_DEFERRED)
get_line_edit().focus_exited.connect(_on_focus_exited, CONNECT_DEFERRED)
# Also, set this up initially
get_line_edit().text = (prefix + format_string + suffix) % value
# This handler ensures the format is maintained while editing the field.
func _on_value_changed(value: float) -> void:
get_line_edit().text = (prefix + format_string + suffix) % value
# This handler ensures the format is retained when entering the field.
func _on_focus_entered() -> void:
get_line_edit().text = format_string % value
# This handler ensures the format is retained when exiting the field.
func _on_focus_exited() -> void:
get_line_edit().text = (prefix + format_string + suffix) % value