How to follow focus when going through entries inside scroll container?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By Arc Futahito
:warning: Old Version Published before Godot 3 was released.

Structure:
ScrollContainer
– VboxContainer
----ItemEntry

When navigating through entries with keyboard, scroll container doesn’t follow the focus and you can’t see what item is selected.
As far as I can see, there is no way to enable following focus somehow. How would I make this work?

On screenshot the topmost item is the one with keyboard focus. If I press up, it just goes out of bounds.
enter image description here

:bust_in_silhouette: Reply From: jackmakesthings

I was able to get this working pretty well with this script on the VBoxContainer (though you could put the script anywhere - just update the references to get_children and get_parent so they point to the list items and the scroll container, respectively. If get_children ends up targeting more than it should, I’d suggest having all your list items belong to a node group and using get_nodes_in_group in place of get_children.

extends VBoxContainer

onready var scroll_container = get_parent()


func _ready():
  for child in get_children():
	child.connect('focus_enter', self, '_on_focus_change')
	
	
func _on_focus_change():
  var focused = get_focus_owner()
  var focus_offset = focused.get_pos().y
  var scrolled_top = scroll_container.get_v_scroll()
  var scrolled_bottom = scrolled_top + scroll_container.get_size().y - focused.get_size().y

  if focus_offset < scrolled_top or focus_offset >= scrolled_bottom:
	scroll_container.set_v_scroll(focus_offset)

Basically, whenever one of the buttons in our scroll area gains focus, we find out its vertical position and compare that to the current upper and lower bounds of the visible scrolled area. We get the upper bound with ScrollContainer.get_v_scroll; to get the lower bound, take that number and add the height of the scroll container; then we subtract one button-height from that, just to cover cases where the button’s very close to that bottom edge or partially below it.

Once we have those two numbers, we check whether our focused element is between them; if it’s not, we update the v_scroll of the container, bringing the focused element into view.

Thanks a lot, I was thinking about writing a script for this, but wasn’t sure if there’s a standard way of doing it. This seems like a pretty roundabout way of doing things since godot should handle this without scripting involved. Made an issue on github, but for now I’ll deal with it through script.

Arc Futahito | 2016-07-13 19:13

Great script!

For anybody trying to use it now, here’s an updated version (methods and signals are a bit different).

I’ve also made it so it scrolls just enough for the new entry to be visible when moving to the bottom.

extends VBoxContainer

onready var _scroll_container = get_parent()


func _ready():
	for child in get_children():
		child.connect("focus_entered", self, "_on_focus_change")


func _on_focus_change():
	var focused = get_focus_owner()
	var focus_size = focused.rect_size.y
	var focus_top = focused.rect_position.y

	var scroll_size = _scroll_container.rect_size.y
	var scroll_top = _scroll_container.get_v_scroll()
	var scroll_bottom = scroll_top + scroll_size - focus_size
	
	if focus_top < scroll_top:
	_scroll_container.set_v_scroll(focus_top)
	
	if focus_top > scroll_bottom:
		var scroll_offset = scroll_top + focus_top - scroll_bottom
		_scroll_container.set_v_scroll(scroll_offset)
    	

pouleyKetchoupp | 2020-06-23 17:23

:bust_in_silhouette: Reply From: Lightning_A

There’s now a property for this: ScrollContainer — Godot Engine (stable) documentation in English