0 votes

I'm working on a chat app, but it is tough to figure out how to have it scroll down when there is a new message, but only when it is already scrolled down to the bottom of the chat.

Along with this, I would like to have it display a scroll down button if the user is scrolled up.

My code is as follows:

func load_messages():

if len(Messenger.messages[str(conversation_id)]["messages"]) != len(messages_container.get_children()):
    clear_messages()
    for message in Messenger.messages[conversation_id]["messages"]:
        var msg = Messenger.messages[conversation_id]["messages"][message]
        var hb = HBoxContainer.new()
        var msg_label = Label.new()

        messages_container.add_child(hb)
        hb.add_child(msg_label)

        if msg["sender_id"] == Account.account["id"]:
            msg_label.theme = load("res://resources/themes/message/message_you.tres")
            hb.alignment = BoxContainer.ALIGN_END
        else:
            msg_label.theme = load("res://resources/themes/message/message_them.tres")
            hb.alignment = BoxContainer.ALIGN_BEGIN

        msg_label.text = msg["content"]

    if scrolling:
        var sb = scroll_msg_container.get_v_scrollbar()
        scroll_msg_container.scroll_vertical = sb.max_value

The load_messages() function gets called every time there is a new message, and I'm thinking before I call this that it should check if the user is scrolled all the way down, and if they are, scroll them down again once it adds the messages, and if it is scrolled up at all, it won't and it will display a ⌄ (scroll down) button that when clicked scrolls down to the bottom.

Thanks in advance!

Godot version v3.4.4.stable.official [419e713a2]
in Engine by (193 points)

1 Answer

0 votes

Your suspicion is correct - before a new message is added check to see if you're scrolled to the bottom, then after adding the new message set the scroll position to maximum.

However there are two problems to deal with.

The first is detecting whether you're scrolled to the bottom. When scrolled all the way down, a scrollbar's value is not equal to its max_value. The max_value is closer to value plus the scroll container's height, so compare the value to max_value minus the scroll container's rect_size.y. I'm not sure if you'll have issues with margins or custom theme settings, so this might need some tweaking.

The other problem is that the scroll container is not updated on the same frame as when the message is added, so be sure to wait a frame before attempting to set the scrollbar's value. One way to do this is to wait for the SceneTree's idle_frame signal.

In the following example I have a ScrollContainer named scroll_box and inside of that a VBoxContainer named msg_container.

func add_message(message: String) -> void:
    var scroll_bar = scroll_box.get_v_scrollbar()
    var should_auto_scroll = ((scroll_bar.max_value - scroll_box.rect_size.y) - scroll_bar.value) <= 0

    var label = Label.new()
    label.text = message
    msg_container.add_child(label)

    yield(get_tree(), "idle_frame")

    if should_auto_scroll:
        scroll_bar.value = scroll_bar.max_value
by (272 points)
edited by
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.