How do you force a ScrollContainer to update its scrollbars? Neither update() nor queue_sort() have any effect.

In my case, I have a TextureRect as a child of a ScrollContainer. Whatever size the TextureRect happens to be at the time of the ScrollContainer's _ready() callback is accommodated correctly; scrollbars are generated and allow panning over the area covered by that TextureRect child as expected.

However, if the size of the TextureRect changes, the scrollbars do not update to match the new area. If the TextureRect starts out smaller than the ScrollContainer, no scrollbars ever appear even after being resized to be larger than the ScrollContainer. If the TextureRect starts out at a given size that is larger than the ScrollContainer, the scrollbars appear and correctly match that size but ignore subsequent changes.

Is there a way to force a ScrollContainer to update?

If there is not, my next idea is to "prime" the container with an erroneous extra-large size to ensure the scrollbars are generated, and then manually adjust the parameters of the scrollbars themselves: however I don't really understand what the scrollbar's parameter's do. The Docs imply that the page property needs to be changed but to what and if anything else needs to be changed, I don't know. How does page and the other range properties work for scrollbars?

Unfortunately, it appears this is a known issue that currently does not have a fix.

The solution in this post on the forums might help. Outside of that I don’t know right off. You might be able to use the fit_child_in_rect function, though I have never seen it used so it might not work.

I’ll keep an eye out for any potential solutions.

@TwistedTwigleg said: Unfortunately, it appears this is a known issue

No, that is a different issue. That one is referring to container behavior in the editor.

I am referring to container behavior while running the project.

The other post you linked is one I already read while looking for an answer to this, and is indeed why I also asked about how the range properties of ScrollBars work so that I can manually change them to where they should be if a simpler solution doesn't exist.

I find that the texturerect is a rather unpredictable(and possibly buggy?) thing in and of itself and doesn't play well with containers anyways. Could you maybe try some other nodes such as sprite or panel(with a shader you should be able to add an image to it no problem).

Could someone please just explain to me how the page property works?

Could you link it, I can't find anything in scrollcontainer docs about pages.

edit: Looking at the scrollcontainer in remotes in a test project the container seems to be getting reset, it is not respecting any of the default scroll values I set on it. Odd.

As for the page property, I guess you meant the run-time generated/scroll container child scrollbar. That thing is meant to be controlled by the container. if you want to manually control it you might be better off creating your own custom scroll container with a panel and a scrollbar.

Then write script logic to control it according to your own needs, this way you don't have to fight the container logic for control over the properties you want to set.

edit2: seems none of the scrollcontainer signals work either, safe to say the whole scrollcontainer is bugged. I've added a comment about the mouse_entered and mouse_exited signals to this issue: https://github.com/godotengine/godot/issues/22936

edit3: ah, about the previous edit - make sure you don't have any other nodes drawing in front of the scroll container, and if you do, make sure their mouse filters are set appropriately or they will block the mouse interactions.

In as far as the page property of the scrollbar goes, I recon my previous point still stands, though one thought that occurs is that maybe you could try firing the scrollcontainers _ready signal to see if that updates it?

and finally edit4: I manually changed the min_size of a child node through editor remote and the scrollcontainer updated just fine. So it still comes down to texturerect being a buggy mess.

@Ephemeral said:

@TwistedTwigleg said: Unfortunately, it appears this is a known issue

No, that is a different issue.

Ah, my apologizes then. Looking through the source code, it looks like there is a function, update_scrollbars, that updates the scrollbars on a ScrollContainer node, but it is not exposed to GDScript.

Maybe open a feature request on the GitHub repository (assuming this hasn't been requested already) and/or a pull request exposing the function?

Exposing the function to GDScript should be as simple as adding ClassDB::bind_method(D_METHOD("update_scrollbars"), &ScrollContainer::update_scrollbars); to the _bind_methods function in ScrollContainer.cpp.

Could someone please just explain to me how the page property works?

From what I can gather from the source code linked above, it looks like the page property is the size of the content it is scrolling. It appears that the page property is intended to be set to the size of the content, as in the source code linked above the page property is set to the height or width of the ScrollContainer node.

That said, I have not looked at the source code for ScrollBar nodes, so it may be that how it works is different from how it is used in ScrollContainer.cpp.

Thanks, that's informative. (page is in pixels I assume?)

When all and under what conditions does the engine call the non-exposed update_scrollbars? (besides, demonstrably, during _ready)

@Ephemeral said: Thanks, that's informative. (page is in pixels I assume?)

I believe so, as the size property, where the height and width is gotten from, is in pixels.

When all and under what conditions does the engine call the non-exposed update_scrollbars? (besides, demonstrably, during _ready)

I do not know right off, but a quick webpage search on ScrollContainer.cpp shows that it is only used on the following line:

It seems that is the only location the function is called for ScrollContainer.cpp. Knowing this, you might be able to get the scroll bars to update by calling ScrollContainerNode.update(), which should force the ScrollContainer node to redraw itself.

If you want to know how update_scrollbars is used within the Godot, you should probably take a look through the source code on GitHub. I only did a quick pass, but a more thorough look might yield additional uses.

@TwistedTwigleg said: From what I can gather from the source code linked above, it looks like the page property is the size of the content it is scrolling.

After SIGNIFICANT experimentation, I've manage to determine that this is WRONG.

I finally just printed every Range property every frame to see what was happening, and after pouring over the ream of output text, no. page is most certainly not the size of the content. It is the size of the window, which in retrospect is obvious if it is getting its value from the size of the ScrollContainer rather than from the size of the ScrollContainer's child node. The variable that matches the size, in pixels, of the content, is max_value, not page.

Now's where things get weird.

Manually setting get_h_scrollbar().max_value and get_v_scrollbar().max_value to the new size of the TextureRect actually causes the scrollbars to visually update, with the movable handle shrinking or growing to match the TextureRect's change!

HOWEVER, the moment I actually scroll/resize the window/etc. the scrollbars snap back to their previous states.

This implied that the ScrollContainer itself was keeping the starting max values and overriding my change as soon as it updated. To text this, I added ScrollContainer.update() on the next line of code. The result was that changing the max values no longer had a visible effect, confirming that ScrollContainer is remembering the original max values and resetting the scrollbar's properties to those original values when it updates.

update_scrollbars is the problem here, not the solution

In conclusion, the scrollbars's properties are being overridden by a property of the ScrollContainer upon the ScrollContainer's update, a property which holds the size that ScrollContainer's child node was on ready. WHAT IS THIS PROPERTY? It is not listed in the ScrollContainer class, nor the Container class, nor the Control class.

@Megalomaniak said: maybe you could try firing the scrollcontainers _ready signal to see if that updates it?

How do I do that? I assumed just calling ScrollContainer._ready() would break something.

@Ephemeral said:

@Megalomaniak said: maybe you could try firing the scrollcontainers _ready signal to see if that updates it?

How do I do that? I assumed just calling ScrollContainer._ready() would break something.

Er yeah, sorry, that's what I meant. If you do so in the right place, it should be fine.

I would ask what the right place is, but.

rect_min_size

For some reason I cannot fathom, ScrollContainer reacts to, and only to, its child node's rect_min_size on update but not during ready. During ready it responds to rect_size instead. Now, TextureRect is also weird in that update()ing it does not update its 'rect_size', but that wasn't even what was wrong with ScrollContainer.

And I have a guess as to why this is, also. Which is that ScrollContainer ignores a child node with Shrink Center. So the only way to center something in a ScrollContainer is with Expand. Hooray for this functionality being merely mixed into a paste instead of broken entirely? (If this was GMS2, not only would it not work at all but it would delete all your project files and send profane emails to your mother. Still no regrets, switching to Godot.)

THE WORK AROUND IS TO JUST IGNORE EVERYTHING ELSE AND SET THE CHILD NODE's rect_min_size.

Now excuse me while I put my head through my desk.

Strange, I would not have through rect_min_size is what was needed. I'm glad you were able to find a solution though!

It might be worth opening a GitHub issue about it on the documentation repository and/or the engine repository if there isn't an issue about this already. That way, hopefully those in the future won't have to go through the same process.

3 years later