Scroll a scroll container to the last added element in C#

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By jahu00

I have a ScrollContainer with a VBoxContainer inside. I add new nodes to the VBoxContainer from code and I want the ScrollContainer to scroll to the last one from the code. This proved to be surprisingly complicated, mainly due to the fact that GUI gets updated on the next frame, so I cannot manipulate the scrollbar in the same place that I’ve added the node;

I tried using GrabFocus() and :

ScrollContainer.ScrollVertical = (int)ScrollBar.MaxValue;

but the first causes the scrollbar to scroll to the top and second to scroll to the previous last position.

I’ve read that in GDScript I can do this before further manipulating the GUI:

yield(get_tree(), "idle_frame")

and the C# equivalent would be:

await ToSignal(GetTree(), "idle_frame");

but it appears to block execution of the code forever.

How to properly await idle_frame from C# or at least wait for the GUI to have been updated (the right way).

:bust_in_silhouette: Reply From: jahu00

It seems I can achieve this by connecting to a tree_entered signal of the added child node. By the time tree_entered signal gets emitted, GUI is already updated.

Edit: This worked when I was adding just one child node from the code, but after I started adding multiple nodes at the same time, the engine got confused and started scrolling to the top again. I’ll have to figure out a better solution.

Edit: I ended up scrolling the ScrollContainer in the _Process method when a bool flag is set to true and I set this flag to true, every time I add a child node. After scrolling the ScrollContainer, I set the flag back to false. I don’t know if it’s the proper way to solve this problem, but at least it works.

:bust_in_silhouette: Reply From: miskotam

You could use the ScrollBar’s AllowGreater property:

var button = new Button {Text = $"{index++}"};

// Disable 'AllowGreater' to get the proper 'MaxValue'.
_scrollContainer.GetVScrollbar().AllowGreater = false;
var maxValue = _scrollContainer.GetVScrollbar().MaxValue;
_scrollContainer.ScrollVertical = (int) maxValue;

var itemSpacing = _vBoxContainer.GetConstant("separation");
var offset = button.RectSize.y + itemSpacing;

// Increase the scroll position with the inserted item's height
// and with the itemspacing.
_scrollContainer.GetVScrollbar().AllowGreater = true;
_scrollContainer.ScrollVertical += (int) offset;

enter image description here

But you need to take about the AllowGreater flag. If it is enabled, it can result in unwanted scrolling behaviors (e.g.: you will be able to scroll out from the list).