+1 vote

Hi everyone,

I have a VboxContainer as a child of a ScrollContainer and some text and buttons in it – a long list I can scoll through by finger-swiping on a touchscreen.

The code (based on https://github.com/godotengine/godot/issues/21137#issuecomment-414959543):

extends ScrollContainer

var swiping = false
var swipe_start
var swipe_mouse_start
var swipe_mouse_times = []
var swipe_mouse_positions = []

signal finger_is_swiping

func _ready():
    mouse_filter = Control.MOUSE_FILTER_IGNORE

func _input(ev):
    if ev is InputEventMouseButton:
        if ev.pressed:
            swiping = true
            swipe_start = Vector2(get_h_scroll(), get_v_scroll())
            swipe_mouse_start = ev.position
            swipe_mouse_times = [OS.get_ticks_msec()]
            swipe_mouse_positions = [swipe_mouse_start]
        else:
            swipe_mouse_times.append(OS.get_ticks_msec())
            swipe_mouse_positions.append(ev.position)
            var source = Vector2(get_h_scroll(), get_v_scroll())
            var idx = swipe_mouse_times.size() - 1
            var now = OS.get_ticks_msec()
            var cutoff = now - 100
            for i in range(swipe_mouse_times.size() - 1, -1, -1):
                if swipe_mouse_times[i] >= cutoff: idx = i
                else: break
            var flick_start = swipe_mouse_positions[idx]
            var flick_dur = min(0.3, (ev.position - flick_start).length() / 1000)
            if flick_dur > 0.0:
                var tween = Tween.new()
                add_child(tween)
                var delta = ev.position - flick_start
                var target = source - delta * flick_dur * 8.0
                #tween.interpolate_method(self, 'set_h_scroll', source.x, target.x, flick_dur, Tween.TRANS_QUAD, Tween.EASE_OUT)
                tween.interpolate_method(self, 'set_v_scroll', source.y, target.y, flick_dur, Tween.TRANS_QUAD, Tween.EASE_OUT)
                tween.interpolate_callback(tween, flick_dur, 'queue_free')
                tween.start()
            swiping = false
    elif swiping and ev is InputEventMouseMotion:
        var delta = ev.position - swipe_mouse_start
        set_h_scroll(swipe_start.x - delta.x)
        set_v_scroll(swipe_start.y - delta.y)
        swipe_mouse_times.append(OS.get_ticks_msec())
        swipe_mouse_positions.append(ev.position)
        emit_signal("finger_is_swiping")
        print ("finger is swiping")

It works quite nicely, but there are three hurdles I need help with:

  1. While swiping, the print "finger is swiping" is being printed all the time, many times, and I therefore assume that the signal "fingerisswiping" is being emitted as often as well. How can I have the signal being emitted only once per swipe? (Or is that a problem at all?)

  2. How can I set a signal when my finger stops swiping (so when it's lifted from the touchscreen)?

  3. I'd like the swipe-movement not to cause scrolling at all when touching a button in the list first. (My buttons work in a way so I can hold them down and make them not trigger by dragging the finger off... in such a case I'd like the list not to scroll despite the dragging.) I'd assume that I could have the button emit a signal which causes some "ignore swipe" or set the swipe speed to 0 maybe?
    EDIT:
    Yes, a simple swiping = false does this trick!

Any suggestions are much appreciated!

in Engine by (365 points)
edited by

Hope you don't have too much bad experience in that regard on online forums...

I have to share here that I found a solution!

I added a var tween_running = false and put tween_running = true right under tween.start(). At the beginning of func _input(ev): I added

if ev.is_action_pressed("mousebuttonclick"):
  if tween_running:
    tween.stop(self)

The only issue is that the line add_child(tween) now throws the error

add_child: Can't add child to 'ScrollContainer', already has a parent 'ScrollContainer'.

It doesn't crash the program at least. But still, I'd appreciate any idea how I could get rid of the error!

...and of course, minutes later I found the easy fix.

Place a "real" Tween Node as a child of the ScrollContainer.
Get rid of var tween = Tween.new() and add_child(tweenG).
Replace all tween with $Tween.

The whole thing works like a charm (for now ;) ...

Wow , I am glad you found the solution, the authentic one. Yea I had some rough forum experience at the start, but it's okay, there are always people like you who are nice and respectful to the OP. (By the way I think I shouldn't have ranted all that).

I'm surprised I found a solution! ; D

Then again, to really finish this: with the help of golddotasksquestions on reddit I figured that the ScrollContainer actually features swipe-scrolling from the get-go, no code needed at all.
That's fine for what it is, but the hard-coded "sensitivity" of the scrolling might be far too speedy for some and there's currently no way to brake things down a little other than by coding as, for example, shown above. That's why a request was proposed: https://github.com/godotengine/godot-proposals/issues/2873
Fingers crossed that this will develop!

(And by the way, I think it's perfectly alright to exchange thoughts about the forum on the forum.)

Umm, after reading the issue on GitHub , after you post the link, it seems like Godot handles the scroll widget just like the hackish way, of course behind the curtains. And it will develop because Godot is open sourced , and many pro coders work on it every day to improve it and minor to major bug fixes (I haven't done it btw) and I am also a noob (you can say that)

Please log in or register to answer this question.

Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read How to use this Q&A? before posting your first questions.
Social login is currently unavailable. If you've previously logged in with a Facebook or GitHub account, use the I forgot my password link in the login box to set a password for your account. If you still can't access your account, send an email to webmaster@godotengine.org with your username.