Drag Scroll inside Option Button, is it possible ?

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

Hello There,

I am developing a game for a german company in which they use many option buttons.

Sadly they also have a fixed resolution for the game. In one of the optionButtons, the options should show all the letters of the alphabet, meaning that it has more options then space in the screen.

Since the game should run on Android devices, im wondering how can I make the options scroll with a touch input, because I’m able to scroll using the mouse wheel but no dragging function…

I tried making a scrollcontainer for the whole scene, but the client didn’t like it.

Is there any way I can scroll or make them grow horizontaly or is there any other options I may use ?

:bust_in_silhouette: Reply From: Royerson

When you press an OptionButton it actually shows a PopupMenu, which you can access with get_popup() and then do whatever you want with.

Here’s a simple example; it lets you drag the popup with right click.

extends OptionButton

onready var popup = get_popup()

func _ready():
	popup.connect("gui_input", self, "_on_popup_gui_input")

func _on_popup_gui_input(event):
	if event is InputEventMouseMotion\
	&& Input.is_mouse_button_pressed(2):
		popup.rect_position.y += event.relative.y
:bust_in_silhouette: Reply From: LudditeGames

@Royerson’s answer got me halfway there (Thanks!), but I ran into issues with the popup erroneously selecting items when I mean to be dragging. I use Godot C# Mono, but here is my code that is working OK for me by tracking the drag state.

using Godot;
using System;

public class DraggableOptionButton : OptionButton{
    private PopupMenu popup;
    private Boolean wasJustDragging = false;
    public Boolean PopupIsVisible { get { 
        return this.popup.Visible;}}

public override void _Ready(){
    this.popup = this.GetPopup();
    this.popup.HideOnItemSelection = false;
    this.popup.HideOnCheckableItemSelection = false;
    this.popup.Connect("gui_input", this, nameof(this.OnPopupGuiInput));

public void  OnPopupGuiInput(InputEvent @event){
    this.popup.HideOnItemSelection = false;
    this.popup.HideOnCheckableItemSelection = false;
    if(@event is InputEventScreenDrag){
        var dragEvent = (InputEventScreenDrag)@event;
        this.popup.RectPosition += new Vector2(0, dragEvent.Relative.y);
        //If we're dragging, set this flag to not release popup until we're done
        this.wasJustDragging = true;}
    if(@event is InputEventMouseButton){
            //If we weren't just dragging, release the popup for normal input
            this.popup.HideOnItemSelection = true;
            this.popup.HideOnCheckableItemSelection = true;}
        this.wasJustDragging = false;}

Thanks for this, works great!

This is the GDScript version just to save somebody else some typing :stuck_out_tongue:

extends OptionButton

onready var popup = get_popup()

var was_just_dragging = false

func _ready():
	popup.hide_on_item_selection = false
	popup.hide_on_checkable_item_selection = false
	popup.connect("gui_input", self, "_on_popup_gui_input")

func _on_popup_gui_input(event):
	popup.hide_on_item_selection = false
	popup.hide_on_checkable_item_selection = false
	if event is InputEventScreenDrag:
		self.popup.rect_position += Vector2(0, event.relative.y)
		was_just_dragging = true
	if event is InputEventMouseButton:
		if not was_just_dragging:
			popup.hide_on_item_selection = true
			popup.hide_on_checkable_item_selection = true
		was_just_dragging = false

jeudyx | 2022-08-17 18:15

:bust_in_silhouette: Reply From: Maicu

I use this and it works great

extends OptionButton

onready var popup = get_popup()

var scrSz 

func _ready():
	scrSz = screenSize()
	popup.connect("gui_input", self, "_on_popup_gui_input")

func _on_popup_gui_input(event):
	if popup.rect_size.y>scrSz.y:
		if event is InputEventScreenTouch and !event.pressed:
		elif event is InputEventScreenDrag:
			popup.rect_position += Vector2(0, event.relative.y)
			popup.rect_position.y = clamp(popup.rect_position.y,scrSz.y-popup.rect_size.y,0)

func _disable_items(disable):
	if !disable: yield(get_tree().create_timer(0.1),"timeout")
	for i in popup.get_item_count():

func screenSize() -> Vector2:
	var sz := (get_viewport().get_size_override() if get_viewport().get_size_override() > Vector2(0, 0)
		else get_viewport().size)
	return sz