Scrollable list module

I got in the zone today coding a little module to handle scrollable lists. I’m quite happy with it so figured I’d share.

All it does is scroll over a list of things, and can handle a list that either loops or has a beginning and end.

Here is how it looks with a list of three “scenarios” in my game, with a list that has a beginning and an end:

Here is the full module:

--[[

A one dimensional scrollable list. Each step in the list contains a payload - a single value or a table.
The list can either loop infinitely or go forward and back.

Example usage:
    scrollable_list.new_list("test_list", {1,2,3}, false)

    scrollable_list.scroll("test_list", scrollable_list.FORWARD)

    print(scrollable_list.get_payload("test_list")) --2

    scrollable_list.scroll("test_list", scrollable_list.FORWARD)
    scrollable_list.scroll("test_list", scrollable_list.FORWARD)

    print(scrollable_list.get_payload("test_list")) --3 (looping not allowed)

    scrollable_list.remove_list("test_list")
--]]

local scrollable_list = {}

scrollable_list.FORWARD = hash("forward")
scrollable_list.BACK = hash("back")

scrollable_list.lists = {}

---payloads is a table of values/tables that are the purpose of the scrollable list.
function scrollable_list.new_list(id, payloads, allow_loop)

    scrollable_list.lists[id] = {index = 1, length = #payloads, payloads = payloads, allow_loop = allow_loop}
end

function scrollable_list.remove_list(id)

    scrollable_list.lists[id] = nil
end

function scrollable_list.wrap(id, direction)

    local list = scrollable_list.lists[id]

    if not list then
        return false
    end

    local index = list.index

    if direction == scrollable_list.FORWARD then

        index = index + 1

        if index > list.length then
            if list.allow_loop then
                index = 1
            else
                index = list.length
            end
        end

    elseif direction == scrollable_list.BACK then

        index = index - 1

        if index < 1 then
            if list.allow_loop then
                list.index = list.length
            else
                index = 1
            end
        end

    end

    return index
end

function scrollable_list.scroll(id, direction)

    local list = scrollable_list.lists[id]

    if not list then
        return false
    end

    list.index = scrollable_list.wrap(id, direction)
    
end

---returns the payload of a list, if no offset (FORWARD/BACK) specified then will return current payload
function scrollable_list.get_payload(id, offset)

    local list = scrollable_list.lists[id]

    if not list then
        return nil
    end

    if not offset then
        return list.payloads[list.index]
    else
        return list.payloads[scrollable_list.wrap(id, offset)]
    end
    
end

---returns true if the given list can scroll in a given direction (i.e. if allow_loop is true or the list is not at either end depending on the direction)
function scrollable_list.can_scroll(id, direction)
    local list = scrollable_list.lists[id]
    if not list then
        return nil
    end
    return list.allow_loop == true or ((direction == scrollable_list.BACK and list.index > 1) or (direction == scrollable_list.FORWARD and list.index < list.length))
end

return scrollable_list
11 Likes