Going trough entries inside a scroll container via Mousewheel

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

Hello,

currently i am trying to scroll trough a list of entries via mousewheel up or down. First i looked for code that would go through the different buttons focus and in the end i found this https://forum.godotengine.org/5990/follow-focus-going-through-entries-inside-scroll-container . Now i can go through the different buttons with the tab key or shitf+tab. But i want to cycle trough them with the mousewheel. I found out that you can simulate a keypress via code https://forum.godotengine.org/53622/how-to-sumalate-a-key-press
The problem here now is that the key can only be pressed once and then never again.
Next problem is that i can’t combine they KEY_SHIFT + KEY_TAB keys so i go to the previous focus.

How can i simulate the keypress so i can use the mousewheel? How do i combine the different keys? Is this even the right approach?


Scene Setup

Container
    -ScrollContainer
        -VboxContainer
            -Button1
            -Button2
            -Button3

Code

extends VBoxContainer



onready var _scroll_container = get_parent()
onready var button_1 = $Button


func _ready():
	button_1.grab_focus()
	for child in get_children():
		child.connect("focus_entered", self, "_on_focus_change")
		
func _input(event):
	if event is InputEventMouseButton and event.pressed:
		if event.button_index == BUTTON_WHEEL_UP :
			simulate_keypress_tab_shift()
		elif event.button_index == BUTTON_WHEEL_DOWN:
			simulate_keypress_tab()
			

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)
		
func simulate_keypress_tab():
	var a = InputEventKey.new()
	a.scancode = KEY_TAB
	a.pressed = true # change to false to simulate a key release
	Input.parse_input_event(a)
	
func simulate_keypress_tab_shift():
	var a = InputEventKey.new()
	a.scancode = KEY_SHIFT + KEY_TAB 
	a.pressed = true # change to false to simulate a key release
	Input.parse_input_event(a)
:bust_in_silhouette: Reply From: Lopy

You can map the mousewheel to the ui_up and ui_down Actions. To do that, go to your project settings, second tab (InputMap), locate ui_up and down, and add the right entries with the +.

Alternatively, if you only want the mousewheel to behave like that in some circumstances. You could make two new inputs, Fruit_up and Fruit_down, map them to the wheel, and then listen to them :
func _unhandled_input(event):
. if event.is_action_pressed("Fruit_up") && time_is_right:
. . get_tree().set_input_as_handled() //stop event propagation
. . var action := InputEventAction.new()
. . action.action = "ui_up"
. . action.pressed = true
. . Input.parse_input_event(action)
. elif event.is_action_pressed("Fruit_down") && time_is_right:
. . get_tree().set_input_as_handled()
. . var action := InputEventAction.new()
. . action.action = "ui_down"
. . action.pressed = true
. . Input.parse_input_event(action)
With time_is_right being a variable telling you if you want the scrollwheel to handle focus.

i tried binding the mouswheel up/down to the ui_up/down actions at first but sadly that does not work because the mousewheel is never really being pressed. This gets explained in this post i found Wheel Up and Wheel Down "buttons" apparently not working in InputMap · Issue #3340 · godotengine/godot · GitHub
this seem especiall weird to me since you are even allowed to assign the mousewheel up/down to any action but it does not work in the end


the problem more lies in this part of the code. and i don’t know how to do that multiple times. because right now if i press mousewheel down it only executes this func once. what i mean by that is that i can set the focus from button1 to button2 but not from buttton2 to button3. it stops at button2

func simulate_keypress_tab():
    var a = InputEventKey.new()
    a.scancode = KEY_TAB
    a.pressed = true # change to false to simulate a key release
    Input.parse_input_event(a)

Fruitdude | 2020-12-23 22:42

:bust_in_silhouette: Reply From: Fruitdude

Well i got it to work now. Was again way simpler then i thought it should be. I now can scroll through my entries of the vboxcontainer via mousewheel

extends VBoxContainer


onready var _scroll_container = get_parent()
onready var button_1 = $Button
onready var label_1 = get_parent().get_parent().get_node("Label")

var input = InputEventAction.new()

var scroll_up : bool = false
var scroll_down : bool = false


func _ready():
	button_1.grab_focus()
	for child in get_children():
		child.connect("focus_entered", self, "_on_focus_change")
	
	
func _input(event):
	if event is InputEventMouseButton:
		if event.button_index == BUTTON_WHEEL_UP :
			scroll_up = true
		elif event.button_index == BUTTON_WHEEL_DOWN:
			scroll_down = true


func _process(_delta):
	if scroll_up:
		input.action = "ui_focus_prev"
		input.pressed = true
		scroll_up = false
	elif scroll_down:
		input.action = "ui_focus_next"
		input.pressed = true
		scroll_down = false
	else:
		input.pressed = false
	Input.parse_input_event(input)
		
		
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)