The game I’m working on is heavily menu-based, and I’m trying to find a way to get the scroll container to scroll to the bottom of its contents via script whenever a new menu pops up. Looking at the other answers on here talks about using functions that don’t appear to exist anymore. Anything associated with .value and max_value don’t work, although max value shows up as an integer when printed. What exactly am I missing?
I’m afraid this doesn’t work either. I tried multiple ways of using ScrollContainer, including set_deferred and across multiple scripts. I either get error messages or nothing happens.
Strangely, when I attach the function to a button it works perfectly, but I can’t seem to get it to automatically go. I’ve even tried to use signals to relay the command to a custom script attached to the container, but nothing seems to do the trick.
Update: I just added a print to the signal function and it appears to be calling correctly. It’s almost as if something is overwriting the scroll value after the function is called. Considering I have no other functions attached to the scroll value, any ideas on possible culprits?
As I said, I tried set_deferred already. My function is calling properly, but something else appears to be overwriting the value after the function sets it.
I’ve come across a workaround by using a timer set for .01 seconds into runtime.
Here’s the function I used in my script to solve this problem. I’m taking a LineEdit’s submitted signal, making a new Label (of a custom extended class, CommandEntryLabel), assigning the submitted text to it, and appending it into a VBoxContainer within a ScrollContainer.
My best guess about what’s going on is that you tell it to scroll to what the maximum value is, but then a split second later it updates what that max value ACTUALLY is, which makes it appear like it didn’t really scroll to the bottom.
I hope this helps!
func _on_command_entry_text_submitted(command_text: String) -> void:
var new_entry: CommandEntryLabel = CommandEntryLabel.new()
new_entry.text = command_text
%HistoryEntries.add_child(new_entry)
%CommandEntry.text = ''
# Waiting a split second after adding the text allows the program to catch up and properly
# scroll all the way to the bottom. It's frustrating that I have to even do this hack,
# but... here we are.
get_tree().create_timer(.01).timeout.connect(
func () -> void:
%CommandHistory.scroll_vertical = %CommandHistory.get_v_scroll_bar().max_value
)
You could recursively defer call the same function here:
func scroll_to_bottom_in_n_frames(n: int):
if n <= 0:
@warning_ignore("narrowing_conversion")
scroll_vertical = get_v_scroll_bar().max_value
else:
scroll_to_bottom_in_n_frames.call_deferred(n - 1)
I ran into a similar problem described in this thread, except I had to wait for 3 frames for the sizing to propagate to my scroll container, due to how I’m using a few nested containers between the list I’m appending to and the scrolling container.
So by playing around with a few of the suggestions here (thank you all, this thread is a life-saver lol) I ended up with this for making a DMs-style chatbox:
func new_message(message) :
var message_instance = individualChats.instantiate()
message_instance.set_data(message)
$ScrollContainer/VBoxContainer.add_child(message_instance)
func update_scroll() :
var max_scroll = $ScrollContainer.get_v_scroll_bar().max_value
$ScrollContainer.set_deferred("scroll_vertical", max_scroll)
func _on_action_to_send_message() :
var msg = "Hello World"
new_message(msg)
update_scroll.call_deferred()
Not sure if this only works because it’s instantiating another scene for the individual messages, but it might be worth testing without that and seeing if something like this:
func update_scroll() :
var max_scroll = $ScrollContainer.get_v_scroll_bar().max_value
$ScrollContainer.set_deferred("scroll_vertical", max_scroll)
func menu_popup() :
# insert code here
update_scroll.call_deferred()
Is this a bug? If yes, has anyone reported it yet? This feels like a really weird and hacky solution for something as basic as “scrolling to bottom in a node specifically designed for scrolling”
For future reference… here’s one of the solutions from above but in C#: