• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

TasButtonList

Introduction

TasButtonList is a UI-Frame Resource, it displays data to a fixed amount of clickable buttons. The idea was to create a System, you feed with data and behaviour, while the UI-Part one has to do (as mapper) is quite low.​

Features

Create any amount of TasButtonList at the same Time.​
Builtin in are 3 Variations of a ButtonList (1 per row, 2 and 3 per Row)​
The currently displayed content can be scrolled.​
With search by text one can change the current relevant fraction of data.​
customizeable to adapt to custom data.​
one can set a custom filter additional to the search by text​

How to Install

First you have to export and import the 2 Files in the demo map (using the export all Files function can bug in older Editor Versions).​
war3mapImported\TasButtonList.fdf​
war3mapImported\TasButtonList.toc​

Now you copy TasButtonList (trigger editor) into your map​
if all worked, you can now use it.​

Lua

Jass


Examples

Creates a ButtonList with 8 Buttons, using Unit-RawCodes as data. Which are on default quite well supported.
When a player clicks a Button the data currently pointed at by that Button is created for the clicking Player at the map center.
Lua:
do
    local realFunc = MarkGameStarted
    function MarkGameStarted()
    realFunc()
    xpcall(function()
    -- create an empty Frame that will be the ButtonLists parent. if you want to hide/move the ButtonList hide/move the parent.
    local frame = BlzCreateFrameByType("FRAME", "", BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI, 0), "",0)
    BlzFrameSetSize(frame, 0.23, 0.001)
    BlzFrameSetAbsPoint(frame, FRAMEPOINT_TOP, 0.6, 0.5)
    -- create a new ButtonList with 8 Rows, frame is the parent and define the action when clicking the Button
    local object = CreateTasButtonList(8, frame, function(data, buttonListObject, dataIndex)
        -- create an unit for the clicking player at 0/0 with facing 0
         CreateUnit(GetTriggerPlayer(), data, 0, 0, 0)
    end)
    -- add various data
    TasButtonListAddData(object, "Hpal")
    TasButtonListAddData(object, "Hblm")
    TasButtonListAddData(object, "hfoo")
    TasButtonListAddData(object, "hkni")
    TasButtonListAddData(object, "hdhw")
    TasButtonListAddData(object, "hmpr")
    TasButtonListAddData(object, "hsor")
    TasButtonListAddData(object, "hrif")
    TasButtonListAddData(object, "Ofar")
    TasButtonListAddData(object, "ogru")
    TasButtonListAddData(object, "orai")
    -- force an update
--    UpdateTasButtonList(object)
    BlzFrameSetValue(object.Slider, 999999)
    print("done")
    end, print)
    end
end
displaying custom Data.
Lua:
do
    local realFunc = MarkGameStarted
    function MarkGameStarted()
    realFunc()
    xpcall(function()
        -- create an empty Frame that will be the ButtonLists parent. if you want to hide/move the ButtonList hide/move the parent.
        local frame = BlzCreateFrameByType("FRAME", "", BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI, 0), "",0)
        BlzFrameSetSize(frame, 0.23, 0.001)
        BlzFrameSetAbsPoint(frame, FRAMEPOINT_TOP, 0.6, 0.5)
        -- create a new ButtonList with 4 Rows, frame is the parent and define the action when clicking the Button
        local object = CreateTasButtonList(4, frame, function(data, buttonListObject, dataIndex)
            print(data.Icon, data.Text)
        end,
        function(frameObject, data)
            BlzFrameSetTexture(frameObject.Icon, data.Icon, 0, false)
            BlzFrameSetText(frameObject.Text, data.Text)
 
            BlzFrameSetTexture(frameObject.ToolTipFrameIcon, data.Icon, 0, false)
            BlzFrameSetText(frameObject.ToolTipFrameName, data.Text)
        end)
 
 
        -- add various data
        TasButtonListAddData(object, {Icon = "ReplaceableTextures\\CommandButtons\\BTNPriest", Text = "Custom A"})
        TasButtonListAddData(object, {Icon = "ReplaceableTextures\\CommandButtons\\BTNSlow", Text = "Custom B"})
        TasButtonListAddData(object, {Icon = "ReplaceableTextures\\CommandButtons\\BTNInvisibility", Text = "Custom C"})
        TasButtonListAddData(object, {Icon = "ReplaceableTextures\\CommandButtons\\BTNBanish", Text = "Custom D"})
        TasButtonListAddData(object, {Icon = "ReplaceableTextures\\CommandButtons\\BTNCrystalBall", Text = "Custom E"})
        TasButtonListAddData(object, {Icon = "ReplaceableTextures\\CommandButtons\\BTNBoots", Text = "Custom F"})
        TasButtonListAddData(object, {Icon = "ReplaceableTextures\\CommandButtons\\BTNTalisman", Text = "Custom G"})
 
        -- force an update
    --    UpdateTasButtonList(object)
        BlzFrameSetValue(object.Slider, 999999)
 
        print("done")
    end, print)
    end
end
There are more examples but for them you need to download the demo map.

Source code
Lua:
--[[
TasButtonList11 by Tasyen
    TasButtonList is a higher Level UI-Component to search, filter and select data using a fixed amount of buttons.
    The UI-API part one has to do (as mapper using this system) is quite small.
    Provides a built in Tooltip-Box
    There can be many TasButtonList at the same Time.
    Each player can have a different dataPool inside a TasButtonList.
    Can differ between right click & left click (optional)
    Supports differnt Buttons (they have to be defined in fdf)
    ObjectEditor lists are handled with the default Actions, only need to define buttonAction in such a case.

function CreateTasButtonListEx(buttonName, cols, rows, parent, buttonAction[, rightClickAction, updateAction, searchAction, filterAction, asyncButtonAction, asyncRightClickAction, colGap, rowGap])
 create a new List
 parent is the container of this Frame it will attach itself to its TOP.
 buttonAction is the function that executes when an option is clicked. args: (clickedData, buttonListObject, dataIndex)
 rightClickAction is the function that executes when an option is rightClicked. args: (clickedData, buttonListObject, dataIndex)
 when your data are object Editor object-RawCodes (but not buffs) then updateAction & searchAction use a default one handling them.
 updateAction runs for each Button and is used to set the diplayed content. args:(frameObject, data)
    frameObject.Button
    frameObject.ToolTipFrame
    frameObject.ToolTipFrameIcon
    frameObject.ToolTipFrameName
    frameObject.ToolTipFrameSeperator
    frameObject.ToolTipFrameText

    frameObject.Icon
    frameObject.Text
    frameObject.IconGold
    frameObject.TextGold
    frameObject.IconLumber
    frameObject.TextLumber
    TasButtonList[frameObject] => buttonListObject

    data is one entry of the TasButtonLists Data-Array.

 searchAction is a function that returns true if the current data matches the searchText. Args: (data, searchedText, buttonListObject)
 filterAction is meant to be used when one wants an addtional non text based filtering, with returning true allowing data or false rejecting it. Args: (data, buttonListObject, isTextSearching)
 searchAction , udateAction & filterAction are async this functions should not do anything that alters the game state/flow.
function CreateTasButtonList(buttonCount, parent, buttonAction[, updateAction, searchAction, filterAction])
    wrapper for CreateTasButtonListEx, 1 col, buttonCount rows.
function CreateTasButtonListV2(rowCount, parent, buttonAction[, updateAction, searchAction, filterAction])
    wrapper for CreateTasButtonListEx, 2 Buttons each Row, takes more Height then the other Versions
function CreateTasButtonListV3(rowCount, parent, buttonAction[, updateAction, searchAction, filterAction])
    wrapper for CreateTasButtonListEx, 3 Buttons each Row, only Icon, and Costs

Wrapper Creator for Lists having only Text in a Box
Default update & search: exepect data to be either a number (object Editor rawCode), a string or a table(title, text, icon)
function CreateTasButtonBoxedTextList(rowCount, colCount, parent, buttonAction[, updateAction, searchAction, filterAction])
function CreateTasButtonBoxedTextListBig(rowCount, colCount, parent, buttonAction[, updateAction, searchAction, filterAction])

Wrapper Creator for Lists having only Text
Default update & search: exepect data to be either a number (object Editor rawCode), a string or a table(title, text, icon)
function CreateTasButtonTextList(rowCount, colCount, parent, buttonAction[, updateAction, searchAction, filterAction])
function CreateTasButtonTextListBig(rowCount, colCount, parent, buttonAction[, updateAction, searchAction, filterAction])

function TasButtonListClearData(buttonListObject[, player])
    remove all data
function TasButtonListRemoveData(buttonListObject, data[, player])
    search for data and remove it
function TasButtonListAddData(buttonListObject, data[, player])
    add data for one Button
function TasButtonListAddDataBatch(buttonListObject, player, ...)
   calls TasButtonListAddData for each given arg after player
   nil for player will add it for all players
   you should not use FourCC in this, TasButtonList will do that for your
function TasButtonListAddDataBatchEx(buttonListObject, ...)
    TasButtonListAddDataBatch with player nil (all players)
function TasButtonListCopyData(writeObject, readObject[, player])
    writeObject uses the same data as readObject and calls UpdateButtonList.
    The copier writeObject still has an own filtering and searching.
function UpdateTasButtonList(buttonListObject)
    update the displayed Content should be done after Data was added or removed was used.
TasButtonListSearch(buttonListObject[, text])
    The buttonList will search it's data for the given text, if nil is given as text it will search for what the user currently has in its box.
    This will also update the buttonList
--]]

TasButtonList = {
    -- TasButtonListAddData will FourCC given 4 digit strings?
    Interpret4DigitString = true
}
do
    local function reload()
        BlzLoadTOCFile("war3mapimported\\TasButtonList.toc")
    end
    local realFunc = InitBlizzard
    function InitBlizzard()
        realFunc()
        reload()
        -- fix a save&Load bug in 1.31.1 and upto 1.32.10 (currently) which does not save&load frame-API actions
        if FrameLoaderAdd then FrameLoaderAdd(reload) end

        TasButtonList.SyncTrigger = CreateTrigger()
        TasButtonList.SyncTriggerAction = TriggerAddAction(TasButtonList.SyncTrigger, function()
            xpcall(function()
            local buttonListObject = TasButtonList[BlzGetTriggerFrame()]
            local dataIndex = math.tointeger(BlzGetTriggerFrameValue())

            if buttonListObject.ButtonAction then
                -- call the wanted action, 1 the current Data
                buttonListObject.ButtonAction(buttonListObject.Data[GetTriggerPlayer()][dataIndex], buttonListObject, dataIndex)
            end
            UpdateTasButtonList(buttonListObject)
            end,print)
        end)

        -- do this only if the function IsRightClick exists
        if IsRightClick then
            TasButtonList.SyncTriggerRightClick = CreateTrigger()
            TasButtonList.SyncTriggerRightClickAction = TriggerAddAction(TasButtonList.SyncTriggerRightClick, function()
                xpcall(function()
                local buttonListObject = TasButtonList[BlzGetTriggerFrame()]
                local dataIndex = math.tointeger(BlzGetTriggerFrameValue())

                if buttonListObject.RightClickAction then
                    -- call the wanted action, 1 the current Data
                    buttonListObject.RightClickAction(buttonListObject.Data[GetTriggerPlayer()][dataIndex], buttonListObject, dataIndex)
                end
                UpdateTasButtonList(buttonListObject)
                end,print)
            end)
        end

        TasButtonList.RightClickSound = CreateSound("Sound\\Interface\\MouseClick1.wav", false, false, false, 10, 10, "")
        SetSoundParamsFromLabel(TasButtonList.RightClickSound, "InterfaceClick")
        SetSoundDuration(TasButtonList.RightClickSound, 239)

        -- handles the clicking
        TasButtonList.ButtonTrigger = CreateTrigger()
        TasButtonList.ButtonTriggerAction = TriggerAddAction(TasButtonList.ButtonTrigger, function()
            local frame = BlzGetTriggerFrame()
            local buttonIndex = TasButtonList[frame].Index
            local buttonListObject = TasButtonList[TasButtonList[frame]]
            local dataIndex = buttonListObject.DataFiltered[buttonListObject.ViewPoint + buttonIndex]
            BlzFrameSetEnable(frame, false)
            BlzFrameSetEnable(frame, true)
            if GetLocalPlayer() == GetTriggerPlayer() then
                if buttonListObject.AsyncButtonAction then
                    buttonListObject.AsyncButtonAction(buttonListObject, buttonListObject.Data[GetLocalPlayer()][R2I(dataIndex)], frame)
                end
                BlzFrameSetValue(buttonListObject.SyncFrame, dataIndex)
            end
        end)

        -- do this only if the function IsRightClick exists
        if IsRightClick then
            -- handles the clicking
            TasButtonList.ButtonTriggerRightClick = CreateTrigger()
            TasButtonList.ButtonTriggerRightClickAction = TriggerAddAction(TasButtonList.ButtonTriggerRightClick, function()
               
                local frame = BlzGetTriggerFrame()
                local buttonListObject = TasButtonList[TasButtonList[frame]]
                -- if there is no RightClick Action for this Buttonlist skip other actions
                if not buttonListObject.RightClickAction and not buttonListObject.AsyncRightClickAction then return end

                local buttonIndex = TasButtonList[frame].Index
                local dataIndex = buttonListObject.DataFiltered[buttonListObject.ViewPoint + buttonIndex]
                if IsRightClick(GetTriggerPlayer()) and GetLocalPlayer() == GetTriggerPlayer() then
                    if buttonListObject.AsyncRightClickAction then
                        buttonListObject.AsyncRightClickAction(buttonListObject, buttonListObject.Data[GetLocalPlayer()][R2I(dataIndex)], frame)
                    end
                    StartSound(TasButtonList.RightClickSound)
                    BlzFrameSetValue(buttonListObject.SyncFrameRightClick, dataIndex)
                end
            end)
        end

        TasButtonList.SearchTrigger = CreateTrigger()
        TasButtonList.SearchTriggerAction = TriggerAddAction(TasButtonList.SearchTrigger, function()
            TasButtonListSearch(TasButtonList[BlzGetTriggerFrame()], BlzFrameGetText(BlzGetTriggerFrame()))
        end)

        -- scrolling while pointing on Buttons
        TasButtonList.ButtonScrollTrigger = CreateTrigger()
        TasButtonList.ButtonScrollTriggerAction = TriggerAddAction(TasButtonList.ButtonScrollTrigger, function()
            local buttonListObject = TasButtonList[TasButtonList[BlzGetTriggerFrame()]]
            local frame = buttonListObject.Slider
            if GetLocalPlayer() == GetTriggerPlayer() then
                if BlzGetTriggerFrameValue() > 0 then
                    BlzFrameSetValue(frame, BlzFrameGetValue(frame) + buttonListObject.SliderStep)
                else
                    BlzFrameSetValue(frame, BlzFrameGetValue(frame) - buttonListObject.SliderStep)
                end
            end
        end)

        -- scrolling while pointing on slider aswell as calling
        TasButtonList.SliderTrigger = CreateTrigger()
        TasButtonList.SliderTriggerAction = TriggerAddAction(TasButtonList.SliderTrigger, function()
            local buttonListObject = TasButtonList[BlzGetTriggerFrame()]
            local frame = BlzGetTriggerFrame()
            if GetLocalPlayer() == GetTriggerPlayer() then
                if BlzGetTriggerFrameEvent() == FRAMEEVENT_MOUSE_WHEEL then
                    if BlzGetTriggerFrameValue() > 0 then
                        BlzFrameSetValue(frame, BlzFrameGetValue(frame) + buttonListObject.SliderStep)
                    else
                        BlzFrameSetValue(frame, BlzFrameGetValue(frame) - buttonListObject.SliderStep)
                    end
                else
                    -- when there is enough data use viewPoint. the Viewpoint is reduced from the data to make top being top.
                    if buttonListObject.DataFiltered.Count > buttonListObject.Frames.Count then
                        buttonListObject.ViewPoint = buttonListObject.DataFiltered.Count - math.tointeger(BlzGetTriggerFrameValue())
                    else
                        buttonListObject.ViewPoint = 0
                    end
                    UpdateTasButtonList(buttonListObject)
                end
                if buttonListObject.SliderText then
                    local min = math.tointeger(buttonListObject.DataFiltered.Count - BlzFrameGetValue(frame))
                    local max = math.tointeger(buttonListObject.DataFiltered.Count - buttonListObject.Frames.Count)
                    BlzFrameSetText(buttonListObject.SliderText, min .. "/" .. max )
                end
            end
        end)
    end
end
--runs once for each button shown
function UpdateTasButtonListDefaultObject(frameObject, data)
    BlzFrameSetTexture(frameObject.Icon, BlzGetAbilityIcon(data), 0, false)
    BlzFrameSetText(frameObject.Text, GetObjectName(data))

    BlzFrameSetTexture(frameObject.ToolTipFrameIcon, BlzGetAbilityIcon(data), 0, false)
    BlzFrameSetText(frameObject.ToolTipFrameName, GetObjectName(data))     
--        frameObject.ToolTipFrameSeperator
    BlzFrameSetText(frameObject.ToolTipFrameText, BlzGetAbilityExtendedTooltip(data, 0))

    if not IsUnitIdType(data, UNIT_TYPE_HERO) then
        local lumber = GetUnitWoodCost(data)
        local gold = GetUnitGoldCost(data)
        if GetPlayerState(GetLocalPlayer(), PLAYER_STATE_RESOURCE_GOLD) >= gold then
            BlzFrameSetText(frameObject.TextGold, GetUnitGoldCost(data))
        else
            BlzFrameSetText(frameObject.TextGold, "|cffff2010"..GetUnitGoldCost(data))
        end   
       
        if GetPlayerState(GetLocalPlayer(), PLAYER_STATE_RESOURCE_LUMBER) >= lumber then
            BlzFrameSetText(frameObject.TextLumber, GetUnitWoodCost(data))
        else
            BlzFrameSetText(frameObject.TextLumber, "|cffff2010"..GetUnitWoodCost(data))
        end
    else
        BlzFrameSetText(frameObject.TextLumber, 0)
        BlzFrameSetText(frameObject.TextGold, 0)
    end
end

--runs once for each button shown
function UpdateTasButtonListDefaultText(frameObject, data)
   
    if type(data) == "string" then
        BlzFrameSetTexture(frameObject.ToolTipFrameIcon, "UI/Widgets/EscMenu/Human/blank-background", 0, true)
        BlzFrameSetText(frameObject.Text, GetLocalizedString(data))
   
        BlzFrameSetText(frameObject.ToolTipFrameName, GetLocalizedString(data))
--        frameObject.ToolTipFrameSeperator
        BlzFrameSetText(frameObject.ToolTipFrameText, GetLocalizedString(data))
    elseif type(data) == "number" then
        UpdateTasButtonListDefaultObject(frameObject, data)
    elseif type(data) == "table" then
        BlzFrameSetText(frameObject.Text, data[1])   
        BlzFrameSetText(frameObject.ToolTipFrameName, GetLocalizedString(data[1]))
--        frameObject.ToolTipFrameSeperator
        BlzFrameSetText(frameObject.ToolTipFrameText, GetLocalizedString(data[2]))

        -- have icon data?
        if data[3] then
            BlzFrameSetTexture(frameObject.ToolTipFrameIcon, data[3], 0, true)
        else
            BlzFrameSetTexture(frameObject.ToolTipFrameIcon, "UI/Widgets/EscMenu/Human/blank-background", 0, true)
        end

    end
end

function SearchTasButtonListDefaultObject(data, searchedText, buttonListObject)
    --return BlzGetAbilityTooltip(data, 0)
    --return GetObjectName(data, 0)
    --return string.find(GetObjectName(data), searchedText)
    return string.find(string.lower(GetObjectName(data)), string.lower(searchedText))
end

function SearchTasButtonListDefaultText(data, searchedText, buttonListObject)
    if type(data) == "number" then
        return string.find(string.lower(GetObjectName(data)), string.lower(searchedText))
    elseif type(data) == "string" then
        return string.find(string.lower(GetLocalizedString(data)), string.lower(searchedText))
    elseif type(data) == "table" then
        return string.find(string.lower(GetLocalizedString(data[1])), string.lower(searchedText)) or string.find(string.lower(GetLocalizedString(data[2])), string.lower(searchedText))
    else
        return true
    end
end

-- update the shown content
function UpdateTasButtonList(buttonListObject)
    xpcall(function()
    local data = buttonListObject.Data[GetLocalPlayer()]
    BlzFrameSetVisible(buttonListObject.Slider, buttonListObject.DataFiltered.Count > buttonListObject.Frames.Count)
    for int = 1, buttonListObject.Frames.Count do
        local frameObject = buttonListObject.Frames[int]
        if buttonListObject.DataFiltered.Count >= int  then
            buttonListObject.UpdateAction(frameObject, data[buttonListObject.DataFiltered[int + buttonListObject.ViewPoint]])
            BlzFrameSetVisible(frameObject.Button, true)
        else
            BlzFrameSetVisible(frameObject.Button, false)
        end
    end
end, print)
end

-- for backwards compatibility rightClickAction is the last argument
function InitTasButtonListObject(parent, buttonAction, updateAction, searchAction, filterAction, rightClickAction, asyncButtonAction, asyncRightClickAction)
    local object = {
        Data = {}, --an array each slot is the user data
        DataFiltered = {Count = 0}, -- indexes of Data fitting the current search
        ViewPoint = 0,
        Frames = {},
        Parent = parent
    }
    for index = 0, bj_MAX_PLAYER_SLOTS - 1 do
        object.Data[Player(index)] = {Count = 0}
    end
    object.ButtonAction = buttonAction --call this inside the SyncAction after a button is clicked
    object.RightClickAction = rightClickAction -- this inside a SyncAction when the button is right clicked.
    object.UpdateAction = updateAction --function defining how to display stuff (async)
    object.SearchAction = searchAction --function to return the searched Text (async)
    object.FilterAction = filterAction --
    object.AsyncButtonAction = asyncButtonAction -- happens in the clicking event inside a LocalPlayer Block
    object.AsyncRightClickAction = asyncRightClickAction -- happens in the clicking event inside a LocalPlayer Block
    if not updateAction then object.UpdateAction = UpdateTasButtonListDefaultObject end
    if not searchAction then object.SearchAction = SearchTasButtonListDefaultObject end
    table.insert(TasButtonList, object) --index to TasButtonList
    TasButtonList[object] = #TasButtonList -- TasButtonList to Index

    object.SyncFrame = BlzCreateFrameByType("SLIDER", "", parent, "", 0)
    BlzFrameSetMinMaxValue(object.SyncFrame, 0, 9999999)
    BlzFrameSetStepSize(object.SyncFrame, 1.0)
    BlzTriggerRegisterFrameEvent(TasButtonList.SyncTrigger, object.SyncFrame, FRAMEEVENT_SLIDER_VALUE_CHANGED)
    BlzFrameSetVisible(object.SyncFrame, false)
    TasButtonList[object.SyncFrame] = object

    -- do this only if the function IsRightClick exists
    if IsRightClick then
        object.SyncFrameRightClick = BlzCreateFrameByType("SLIDER", "", parent, "", 0)
        BlzFrameSetMinMaxValue(object.SyncFrameRightClick, 0, 9999999)
        BlzFrameSetStepSize(object.SyncFrameRightClick, 1.0)
        BlzTriggerRegisterFrameEvent(TasButtonList.SyncTriggerRightClick, object.SyncFrameRightClick, FRAMEEVENT_SLIDER_VALUE_CHANGED)
        BlzFrameSetVisible(object.SyncFrameRightClick, false)
        TasButtonList[object.SyncFrameRightClick] = object
    end

    object.InputFrame = BlzCreateFrame("TasEditBox", parent, 0, 0)
    BlzTriggerRegisterFrameEvent(TasButtonList.SearchTrigger, object.InputFrame, FRAMEEVENT_EDITBOX_TEXT_CHANGED)
    BlzFrameSetPoint(object.InputFrame, FRAMEPOINT_TOPRIGHT, parent, FRAMEPOINT_TOPRIGHT, 0, 0)
    TasButtonList[object.InputFrame] = object

    return object
end

function InitTasButtonListSlider(object, stepSize, rowCount, colGap, rowGap)
    if not colGap then colGap = 0 end
    if not rowGap then rowGap = 0 end
    object.Slider = BlzCreateFrameByType("SLIDER", "FrameListSlider", object.Parent, "QuestMainListScrollBar", 0)
    TasButtonList[object.Slider] = object -- the slider nows the TasButtonListobject
    object.SliderStep = stepSize
    BlzFrameSetStepSize(object.Slider, stepSize)
    BlzFrameClearAllPoints(object.Slider)
    BlzFrameSetVisible(object.Slider, true)
    BlzFrameSetMinMaxValue(object.Slider, 0, 0)
    BlzFrameSetPoint(object.Slider, FRAMEPOINT_TOPLEFT, object.Frames[stepSize].Button, FRAMEPOINT_TOPRIGHT, 0, 0)
    BlzFrameSetSize(object.Slider, 0.012, BlzFrameGetHeight(object.Frames[1].Button) * rowCount +  rowGap * (rowCount - 1))
    BlzTriggerRegisterFrameEvent(TasButtonList.SliderTrigger, object.Slider , FRAMEEVENT_SLIDER_VALUE_CHANGED)
    BlzTriggerRegisterFrameEvent(TasButtonList.SliderTrigger, object.Slider , FRAMEEVENT_MOUSE_WHEEL)


    -- if the function CreateSimpleTooltip exists, create a Text displaying current Position in the list
    if CreateSimpleTooltip then
        object.SliderText = CreateSimpleTooltip(object.Slider, "1000/1000")
        BlzFrameClearAllPoints(object.SliderText)
        BlzFrameSetPoint(object.SliderText, FRAMEPOINT_BOTTOMRIGHT, object.Slider, FRAMEPOINT_TOPLEFT, 0, 0)
    end
end

function TasButtonListAddDataEx(buttonListObject, data, player)
    local oData = buttonListObject.Data[player]
    local oDataFil = buttonListObject.DataFiltered

    -- convert 'Hpal' into the number
--    print(TasButtonList.Interpret4DigitString) print( data) print(type(data)) print(string.len(data))
    if TasButtonList.Interpret4DigitString and type(data) == "string" and string.len(data) == 4 then data = FourCC(data) end
   
    oData.Count = oData.Count + 1
    oData[oData.Count] = data
    if GetLocalPlayer() == player then
        -- filterData is a local thing
        oDataFil.Count = oDataFil.Count + 1
        oDataFil[oDataFil.Count] = oData.Count
        BlzFrameSetMinMaxValue(buttonListObject.Slider, buttonListObject.Frames.Count, oDataFil.Count)
    end
end

function TasButtonListAddData(buttonListObject, data, player)
    -- only add for one player?
    if player and type(player) == "userdata" then
        TasButtonListAddDataEx(buttonListObject, data, player)
    else
        -- no player -> add for all Players
        for i = 0, bj_MAX_PLAYER_SLOTS - 1 do
            TasButtonListAddDataEx(buttonListObject, data, Player(i))
        end
    end
end

function TasButtonListAddDataBatch(buttonListObject, player, ...)
    for _, k in ipairs({...}) do
        print(k)
        TasButtonListAddData(buttonListObject, k, player)
    end
end
function TasButtonListAddDataBatchEx(buttonListObject, ...)
    TasButtonListAddDataBatch(buttonListObject, nil, ...)
end

function TasButtonListRemoveDataEx(buttonListObject, data, player)
    local oData = buttonListObject.Data[player]
    for index = 1, oData.Count do
        value = oData[index]
        if value == data then
            oData[index] = oData[oData.Count]
            oData.Count = oData.Count - 1
            break
        end
    end
    if GetLocalPlayer() == player then
        BlzFrameSetMinMaxValue(buttonListObject.Slider, buttonListObject.Frames.Count, oData.Count)
    end
end

function TasButtonListRemoveData(buttonListObject, data, player)
    if player and type(player) == "userdata" then
        TasButtonListRemoveDataEx(buttonListObject, data, player)
    else
        for i = 0, bj_MAX_PLAYER_SLOTS - 1 do
            TasButtonListRemoveDataEx(buttonListObject, data, Player(i))
        end
    end
end

function TasButtonListClearDataEx(buttonListObject, player)
    buttonListObject.Data[player].Count = 0
    if GetLocalPlayer() == player then
        buttonListObject.DataFiltered.Count = 0
        BlzFrameSetMinMaxValue(buttonListObject.Slider, 0, 0)
    end
end

function TasButtonListClearData(buttonListObject, player)
    if player and type(player) == "userdata" then
        TasButtonListClearDataEx(buttonListObject, player)
    else
        for i = 0, bj_MAX_PLAYER_SLOTS - 1 do
            TasButtonListClearDataEx(buttonListObject, Player(i))
        end
    end
end

function TasButtonListCopyDataEx(writeObject, readObject, player)
    writeObject.Data[player] = readObject.Data[player]
    for index = 1, readObject.DataFiltered.Count do writeObject.DataFiltered[index] = readObject.DataFiltered[index] end
    writeObject.DataFiltered.Count = readObject.DataFiltered.Count
    if GetLocalPlayer() == player then
        BlzFrameSetMinMaxValue(writeObject.Slider, writeObject.Frames.Count, writeObject.Data[player].Count)
        BlzFrameSetValue(writeObject.Slider,999999)
    end
   
end

function TasButtonListCopyData(writeObject, readObject, player)
    if player and type(player) == "userdata" then
        TasButtonListCopyDataEx(writeObject, readObject, player)
    else
        for i = 0, bj_MAX_PLAYER_SLOTS - 1 do
            TasButtonListCopyDataEx(writeObject, readObject, Player(i))
        end
    end
end

function TasButtonListSearch(buttonListObject, text)
    if not text then text = BlzFrameGetText(buttonListObject.InputFrame) end
    local filteredData = buttonListObject.DataFiltered
    local oData = buttonListObject.Data[GetLocalPlayer()]
    local value
    if GetLocalPlayer() == GetTriggerPlayer() then
        filteredData.Count = 0
        if text ~= "" then
            for index = 1, oData.Count do
                value = oData[index]
                if buttonListObject.SearchAction(value, text, buttonListObject) and (not buttonListObject.FilterAction or buttonListObject.FilterAction(value, buttonListObject, true)) then
                    filteredData.Count = filteredData.Count + 1
                    filteredData[filteredData.Count] = index
                end
            end
           
        else
            for index = 1, oData.Count do
                value = oData[index]
                if not buttonListObject.FilterAction or buttonListObject.FilterAction(value, buttonListObject, false) then
                    filteredData.Count = filteredData.Count + 1
                    filteredData[filteredData.Count] = index
                end
            end
        end
        --table.sort(filteredData, function(a, b) return GetObjectName(buttonListObject.Data[a]) < GetObjectName(buttonListObject.Data[b]) end  )
        --update Slider, with that also update
        BlzFrameSetMinMaxValue(buttonListObject.Slider, buttonListObject.Frames.Count, math.max(filteredData.Count,0))
        BlzFrameSetValue(buttonListObject.Slider, 999999)
       
    end
end

-- demo Creators
function CreateTasButtonTooltip(frameObject, parent)
    -- create an empty FRAME parent for the box BACKDROP, otherwise it can happen that it gets limited to the 4:3 Screen.
    frameObject.ToolTipFrameFrame = BlzCreateFrame("TasButtonListTooltipBoxFrame", frameObject.Button, 0, 0)
    if GetHandleId(frameObject.ToolTipFrameFrame) == 0 then print("Error function CreateTasButtonTooltip Creating TasButtonListTooltipBoxFrame") end
    frameObject.ToolTipFrame = BlzGetFrameByName("TasButtonListTooltipBox", 0)
    frameObject.ToolTipFrameIcon = BlzGetFrameByName("TasButtonListTooltipIcon", 0)
    frameObject.ToolTipFrameName = BlzGetFrameByName("TasButtonListTooltipName", 0)
    frameObject.ToolTipFrameSeperator = BlzGetFrameByName("TasButtonListTooltipSeperator", 0)
    frameObject.ToolTipFrameText = BlzGetFrameByName("TasButtonListTooltipText", 0)   
    BlzFrameSetPoint(frameObject.ToolTipFrameText, FRAMEPOINT_TOPRIGHT, parent, FRAMEPOINT_TOPLEFT, -0.001, -0.052)
    BlzFrameSetPoint(frameObject.ToolTipFrame, FRAMEPOINT_TOPLEFT, frameObject.ToolTipFrameIcon, FRAMEPOINT_TOPLEFT, -0.005, 0.005)
    BlzFrameSetPoint(frameObject.ToolTipFrame, FRAMEPOINT_BOTTOMRIGHT, frameObject.ToolTipFrameText, FRAMEPOINT_BOTTOMRIGHT, 0.005, -0.005)
    BlzFrameSetTooltip(frameObject.Button, frameObject.ToolTipFrameFrame)
end

function CreateTasButtonListEx(buttonName, cols, rows, parent, buttonAction, rightClickAction, updateAction, searchAction, filterAction, asyncButtonAction, asyncRightClickAction, colGap, rowGap)
    if not rowGap then rowGap = 0.0 end
    if not colGap then colGap = 0.0 end
    local buttonCount = rows*cols
    local object = InitTasButtonListObject(parent, buttonAction, updateAction, searchAction, filterAction, rightClickAction, asyncButtonAction, asyncRightClickAction)
    object.Frames.Count = buttonCount
    local rowRemain = cols
    for int = 1, buttonCount do
        local frameObject = {}
        frameObject.Index = int
        frameObject.Button = BlzCreateFrame(buttonName, parent, 0, 0)
        if GetHandleId(frameObject.Button) == 0 then print("TasButtonList - Error - can't create:", buttonName) end
        CreateTasButtonTooltip(frameObject, parent)

        frameObject.Icon = BlzGetFrameByName("TasButtonIcon", 0)
        frameObject.Text = BlzGetFrameByName("TasButtonText", 0)
        frameObject.IconGold = BlzGetFrameByName("TasButtonIconGold", 0)
        frameObject.TextGold = BlzGetFrameByName("TasButtonTextGold", 0)
        frameObject.IconLumber = BlzGetFrameByName("TasButtonIconLumber", 0)
        frameObject.TextLumber = BlzGetFrameByName("TasButtonTextLumber", 0)
        TasButtonList[frameObject.Button] = frameObject
        TasButtonList[frameObject] = object
        object.Frames[int] = frameObject
        BlzTriggerRegisterFrameEvent(TasButtonList.ButtonTrigger, frameObject.Button, FRAMEEVENT_CONTROL_CLICK)
        -- do this only if the function IsRightClick exists
        if IsRightClick then
            BlzTriggerRegisterFrameEvent(TasButtonList.ButtonTriggerRightClick, frameObject.Button, FRAMEEVENT_MOUSE_UP)
        end
        BlzTriggerRegisterFrameEvent(TasButtonList.ButtonScrollTrigger, frameObject.Button, FRAMEEVENT_MOUSE_WHEEL)
       
        if int > 1 then
            if rowRemain == 0 then
                BlzFrameSetPoint(frameObject.Button, FRAMEPOINT_TOP, object.Frames[int - cols].Button, FRAMEPOINT_BOTTOM, 0, -rowGap)
                rowRemain = cols
            else
                BlzFrameSetPoint(frameObject.Button, FRAMEPOINT_LEFT, object.Frames[int - 1].Button, FRAMEPOINT_RIGHT, colGap, 0)
            end
        else
            --print(-BlzFrameGetWidth(frameObject.Button)*cols - colGap*(cols-1))
            if cols > 1 then
                BlzFrameSetPoint(frameObject.Button, FRAMEPOINT_TOPRIGHT, object.InputFrame, FRAMEPOINT_BOTTOMRIGHT, -BlzFrameGetWidth(frameObject.Button)*(cols-1) - colGap*(cols-1), 0)
            else
                BlzFrameSetPoint(frameObject.Button, FRAMEPOINT_TOPRIGHT, object.InputFrame, FRAMEPOINT_BOTTOMRIGHT, 0, 0)
            end
        end
        rowRemain = rowRemain - 1
    end
    InitTasButtonListSlider(object, cols, rows, colGap, rowGap)

    return object
end

-- wrapper creators, they dont have async stuff
function CreateTasButtonList(buttonCount, parent, buttonAction, updateAction, searchAction, filterAction)
    return CreateTasButtonListEx("TasButton", 1, buttonCount, parent, buttonAction, nil, updateAction, searchAction, filterAction)
end

function CreateTasButtonListV2(rowCount, parent, buttonAction, updateAction, searchAction, filterAction)
    return CreateTasButtonListEx("TasButtonSmall", 2, rowCount, parent, buttonAction, nil, updateAction, searchAction, filterAction)
end

function CreateTasButtonListV3(rowCount, parent, buttonAction, updateAction, searchAction, filterAction)
    return CreateTasButtonListEx("TasButtonGrid", 3, rowCount, parent, buttonAction, nil, updateAction, searchAction, filterAction)
end

function CreateTasButtonBoxedTextList(rowCount, colCount, parent, buttonAction, updateAction, searchAction, filterAction)
    return CreateTasButtonListEx("TasBoxedTextButtonSmall", colCount, rowCount, parent, buttonAction, nil, updateAction or UpdateTasButtonListDefaultText, searchAction or SearchTasButtonListDefaultText, filterAction)
end
function CreateTasButtonBoxedTextListBig(rowCount, colCount, parent, buttonAction, updateAction, searchAction, filterAction)
    return CreateTasButtonListEx("TasBoxedTextButton", colCount, rowCount, parent, buttonAction, nil, updateAction or UpdateTasButtonListDefaultText, searchAction or SearchTasButtonListDefaultText, filterAction)
end

function CreateTasButtonTextList(rowCount, colCount, parent, buttonAction, updateAction, searchAction, filterAction)
    return CreateTasButtonListEx("TasTextButtonSmall", colCount, rowCount, parent, buttonAction, nil, updateAction or UpdateTasButtonListDefaultText, searchAction or SearchTasButtonListDefaultText, filterAction)
end
function CreateTasButtonTextListBig(rowCount, colCount, parent, buttonAction, updateAction, searchAction, filterAction)
    return CreateTasButtonListEx("TasTextButton", colCount, rowCount, parent, buttonAction, nil, updateAction or UpdateTasButtonListDefaultText, searchAction or SearchTasButtonListDefaultText, filterAction)
end

The jass version uses library String from MaskedPoptart.

Example:
Creates a ButtonList with 8 Buttons, using Unit-RawCodes as data. Which are on default quite well supported.
When a player clicks a Button the data currently pointed at by that Button is created for the clicking Player at the map center.
JASS:
library SomeTest initializer init_function requires TasButtonList
private function OnClick takes nothing returns nothing
        // create an unit for the clicking player at 0/0 with facing 0
        call CreateUnit(GetTriggerPlayer(), TasButtonListData, 0, 0, 0)
endfunction
private function test takes nothing returns nothing
    // create an empty Frame that will be the ButtonLists parent. if you want to hide/move the ButtonList hide/move the parent.
    local framehandle frame = BlzCreateFrameByType("FRAME", "", BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI, 0), "",0)
    local integer objectIndex = CreateTasButtonList(8, frame, function OnClick, null,null,null)
    call BlzFrameSetSize(frame, 0.23, 0.001)
    call BlzFrameSetAbsPoint(frame, FRAMEPOINT_TOP, 0.6, 0.5)
 
    // add various data
    call TasButtonListAddData(objectIndex, 'Hpal')
    call TasButtonListAddData(objectIndex, 'Hblm')
    call TasButtonListAddData(objectIndex, 'hfoo')
    call TasButtonListAddData(objectIndex, 'hkni')
    call TasButtonListAddData(objectIndex, 'hdhw')
    call TasButtonListAddData(objectIndex, 'hmpr')
    call TasButtonListAddData(objectIndex, 'hsor')
    call TasButtonListAddData(objectIndex, 'hrif')
    call TasButtonListAddData(objectIndex, 'Ofar')
    call TasButtonListAddData(objectIndex, 'ogru')
    call TasButtonListAddData(objectIndex, 'orai')
    // force an update
    call UpdateTasButtonList(objectIndex)
 
    call DestroyTimer(GetExpiredTimer())
endfunction
    private function init_function takes nothing returns nothing
       call TimerStart(CreateTimer(), 0.0, false, function test)
    endfunction
endlibrary
Shows how one could handle custom Data in the (v)jass version. The data is stored in arrays then one adds the indexes of the wanted data into the ButtonList.
when using custom data one also needs to set the update & search action, additional to the buttonAction.
Without the update function the buttons will show nonsense and without a search action the user can not search the data.
JASS:
library SomeTest initializer init_function requires TasButtonList
globals
 
    private string array Icon
    private string array Text
endglobals
private function OnClick takes nothing returns nothing
    call BJDebugMsg(I2S(TasButtonListData) + " "+ Text[TasButtonListData])
endfunction
private function OnSearch takes nothing returns boolean
    return FindIndex(Text[TasButtonListData], TasButtonListText) >= 0
endfunction
private function OnUpdate takes nothing returns nothing
    local integer dataIndex = TasButtonListData
    local integer frameHandle = GetHandleId(TasButtonListFrame)
    call BlzFrameSetTexture(LoadFrameHandle(TasButtonList_Hash, frameHandle, StringHash("Icon")), Icon[dataIndex], 0, false)
    call BlzFrameSetText(LoadFrameHandle(TasButtonList_Hash, frameHandle, StringHash("Text")), Text[dataIndex])
    call BlzFrameSetTexture(LoadFrameHandle(TasButtonList_Hash, frameHandle, StringHash("ToolTipFrameIcon")), Icon[dataIndex], 0, false)
    call BlzFrameSetText(LoadFrameHandle(TasButtonList_Hash, frameHandle, StringHash("ToolTipFrameName")), Text[dataIndex])
endfunction

private function test takes nothing returns nothing
    // create an empty Frame that will be the ButtonLists parent. if you want to hide/move the ButtonList hide/move the parent.
 
    local framehandle frame = BlzCreateFrameByType("FRAME", "", BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI, 0), "",0)
 
    local integer objectIndex = CreateTasButtonList(3, frame, function OnClick, function OnUpdate, function OnSearch, null)
    call BlzFrameSetSize(frame, 0.23, 0.001)
    call BlzFrameSetAbsPoint(frame, FRAMEPOINT_TOP, 0.6, 0.5)
        set Icon[1] = "ReplaceableTextures\\CommandButtons\\BTNPriest"
        set Text[1] = "Custom A"
        set Icon[2] = "ReplaceableTextures\\CommandButtons\\BTNSlow"
        set Text[2] = "Custom B"
        set Icon[3] = "ReplaceableTextures\\CommandButtons\\BTNInvisibility"
        set Text[3] = "Custom C"
        set Icon[4] = "ReplaceableTextures\\CommandButtons\\BTNBanish"
        set Text[4] = "Custom D"
        set Icon[5] = "ReplaceableTextures\\CommandButtons\\BTNCrystalBall"
        set Text[5] = "Custom E"
        set Icon[6] = "ReplaceableTextures\\CommandButtons\\BTNBoots"
        set Text[6] = "Custom F"
        set Icon[7] = "ReplaceableTextures\\CommandButtons\\BTNTalisman"
        set Text[7] = "Custom G"
        call TasButtonListAddData(objectIndex, 1)
        call TasButtonListAddData(objectIndex, 2)
        call TasButtonListAddData(objectIndex, 3)
        call TasButtonListAddData(objectIndex, 4)
        call TasButtonListAddData(objectIndex, 5)
        call TasButtonListAddData(objectIndex, 6)
 
    // force an update
    call UpdateTasButtonList(objectIndex)
 
    call DestroyTimer(GetExpiredTimer())
 
endfunction
    private function init_function takes nothing returns nothing
 
       call TimerStart(CreateTimer(), 0.0, false, function test)
         // this adds an checkbox, when the checkbox is checked only heroes are valid search targets
 
 
    endfunction
endlibrary
JASS:
native GetUnitGoldCost takes integer rawCode returns integer
native GetUnitWoodCost takes integer rawCode returns integer

library TasButtonList initializer Init uses String, IsRightClick
// TasButtonList10a by Tasyen

//function CreateTasButtonList10 takes string buttonName, integer cols, integer rows, framehandle parent, code buttonAction, code rightClickAction, code updateAction, code searchAction, code filterAction, code asyncAction, code asyncRigthAction, real colGap, real rowGap returns integer
//function CreateTasButtonListEx takes string buttonName, integer cols, integer rows, framehandle parent, code buttonAction, code rightClickAction, code updateAction, code searchAction, code filterAction returns integer
 //create a new List
 //parent is the container of this Frame it will attach itself to its TOP.
 //the given functions are called over Triggers
 //buttonAction is the function that executes when an option is clicked.
 //when your data are unit-RawCodes then you can skip updateAction & searchAction.
 //updateAction runs for each Button and is used to set the diplayed content.

 //searchAction is a function that returns true if the current data matches the searchText.
 //filterAction is meant to be used when one wants an addtional non text based filtering, with returning true allowing data or false rejecting it.
 //searchAction , udateAction & filterAction are async this functions should not do anything that alters the game state/flow.

//function CreateTasButtonList takes integer buttonCount, framehandle parent, code buttonAction, code updateAction, code searchAction, code filterAction returns integer
  // wrapper for CreateTasButtonListEx, 1 col, buttonCount rows.
//function CreateTasButtonListV2 takes integer rowCount, framehandle parent, code buttonAction, code updateAction, code searchAction, code filterAction returns integer
  //  2 Buttons each Row, takes more Height then the other Versions
//function CreateTasButtonListV3 takes integer rowCount, framehandle parent, code buttonAction, code updateAction, code searchAction, code filterAction returns integer
  //  3 Buttons each Row, only Icon, and Costs

//function TasButtonListClearDataEx takes integer listIndex, integer playerIndex returns nothing
//function TasButtonListClearData takes integer listIndex returns nothing
  //  remove all data
//function TasButtonListRemoveDataEx takes integer listIndex, integer data, integer playerIndex returns nothing
//function TasButtonListRemoveData takes integer listIndex, integer data returns nothing
  //  search for data and remove it
//function TasButtonListAddDataEx takes integer listIndex, integer data, integer playerIndex returns nothing
//function TasButtonListAddData takes integer listIndex, integer data returns nothing
  //  add data for one Button
//function TasButtonListCopyDataEx takes integer writeObject, integer readObject, integer playerIndex returns nothing
//function TasButtonListCopyData takes integer writeObject, integer readObject returns nothing
  //  writeObject uses the same data as readObject and calls UpdateButtonList.
//function UpdateTasButtonList takes integer listIndex returns nothing
  //  update the displayed Content should be done after Data was added or removed was used.
//function TasButtonListSearch takes integer listIndex, string text returns nothing
    // The buttonList will search it's data for the given text, if nil is given as text it will search for what the user currently has in its box.
    // This will also update the buttonList

globals
    //args for custom user actions
    integer TasButtonListData = 0
    string TasButtonListText = ""
    boolean TasButtonListIsSearching = false
    integer TasButtonListIndex = 0
    framehandle TasButtonListFrame = null

    // System
    public hashtable Hash = InitHashtable()
    private integer Counter = 0 //amount of Lists created, each index is one List
    private trigger SyncTrigger = CreateTrigger()
    private trigger ButtonTrigger = CreateTrigger()   
    private trigger SearchTrigger = CreateTrigger()
    private trigger ButtonScrollTrigger = CreateTrigger()
    private trigger SliderTrigger = CreateTrigger()
    private trigger SyncRightTrigger = CreateTrigger()
    private trigger ButtonRightTrigger = CreateTrigger()
    private integer HASH_TOOL_TIP = StringHash("ToolTipFrame")
    private integer HASH_TOOL_TIP_ICON = StringHash("ToolTipFrameIcon")
    private integer HASH_TOOL_TIP_NAME = StringHash("ToolTipFrameName")
    private integer HASH_TOOL_TIP_SEP = StringHash("ToolTipFrameSeperator")
    private integer HASH_TOOL_TIP_TEXT = StringHash("ToolTipFrameText")
    private integer HASH_ICON = StringHash("Icon")
    private integer HASH_TEXT = StringHash("Text")
    private integer HASH_ICON_GOLD = StringHash("IconGold")
    private integer HASH_TEXT_GOLD = StringHash("TextGold")
    private integer HASH_ICON_LUMBER = StringHash("IconLumber")
    private integer HASH_TEXT_LUMBER = StringHash("TextLumber")
    private sound RightClickSound

    // ButtonLists
    framehandle array TasButtonListSlider
    framehandle array TasButtonListParent
    framehandle array TasButtonListInputFrame
    framehandle array TasButtonListSyncFrame
    framehandle array TasButtonListSyncFrameRight
    integer array TasButtonListButtonCount
    integer array TasButtonListStepSize
    trigger array TasButtonListButtonAction
    trigger array TasButtonListRightAction
    trigger array TasButtonListUpdateAction
    trigger array TasButtonListSearchAction
    trigger array TasButtonListFilterAction
    trigger array TasButtonListAsyncAction
    trigger array TasButtonListAsyncRightAction
   
    integer array TasButtonListViewPoint
    location array TasButtonListDataList
    location array TasButtonListDataListFiltered
    location array TasButtonListFrameList

    real TasButtonListGapCol = 0.0
    real TasButtonListGapRow = 0.0

endglobals


// update the shown content
function UpdateTasButtonList takes integer listIndex returns nothing
    local integer dataHash = GetHandleId(LoadLocationHandle(Hash, GetHandleId(TasButtonListDataList[listIndex]), GetPlayerId(GetLocalPlayer())))
    local integer frameListHash = GetHandleId(TasButtonListFrameList[listIndex])
    local integer filteredDataHash = GetHandleId(TasButtonListDataListFiltered[listIndex])
    local integer dataFilteredCount = LoadInteger(Hash, filteredDataHash, 0)
   
    local integer i = 1

    call BlzFrameSetVisible(TasButtonListSlider[listIndex], dataFilteredCount > TasButtonListButtonCount[listIndex])
    loop
        exitwhen i > TasButtonListButtonCount[listIndex]
        set TasButtonListFrame = LoadFrameHandle(Hash, frameListHash, i)
       
        if dataFilteredCount >= i  then
            set TasButtonListData = LoadInteger(Hash, dataHash, LoadInteger(Hash, filteredDataHash, i + TasButtonListViewPoint[listIndex]))
            call TriggerEvaluate(TasButtonListUpdateAction[listIndex])
            call BlzFrameSetVisible(TasButtonListFrame, true)
        else
            call BlzFrameSetVisible(TasButtonListFrame, false)
        endif
        set i = i + 1
    endloop

endfunction

function TasButtonListSearch takes integer listIndex, string text returns nothing
    local integer filteredDataHash = GetHandleId(TasButtonListDataListFiltered[listIndex])
    local integer dataHash = GetHandleId(LoadLocationHandle(Hash, GetHandleId(TasButtonListDataList[listIndex]), GetPlayerId(GetLocalPlayer())))
    local integer filteredDataCount

    local integer i
    local integer iEnd
    if text == null or text == "" then
        set text = BlzFrameGetText(TasButtonListInputFrame[listIndex])
    endif
    if GetLocalPlayer() == GetTriggerPlayer() then
        set TasButtonListText = text
        set TasButtonListIndex = listIndex
        call FlushChildHashtable(Hash, filteredDataHash)
        set filteredDataCount = 0
        if text != "" then
            set TasButtonListIsSearching = true
            set iEnd = LoadInteger(Hash, dataHash, 0)
            set i = 1
            loop
                exitwhen i > iEnd
                set TasButtonListData = LoadInteger(Hash, dataHash, i)
                if TriggerEvaluate(TasButtonListSearchAction[listIndex]) and TriggerEvaluate(TasButtonListFilterAction[listIndex]) then
                    set filteredDataCount = filteredDataCount + 1
                    call SaveInteger(Hash, filteredDataHash, filteredDataCount, i)
                endif
                set i = i + 1
            endloop
            call SaveInteger(Hash, filteredDataHash, 0, filteredDataCount)
        else
            set TasButtonListIsSearching = false
            set iEnd = LoadInteger(Hash, dataHash, 0)
            set i = 1
            loop
                exitwhen i > iEnd
                set TasButtonListData = LoadInteger(Hash, dataHash, i)
                if TriggerEvaluate(TasButtonListFilterAction[listIndex]) then
                    set filteredDataCount = filteredDataCount + 1
                    call SaveInteger(Hash, filteredDataHash, filteredDataCount, i)
                endif
                set i = i + 1
            endloop
            call SaveInteger(Hash, filteredDataHash, 0, filteredDataCount)
        endif
       

        //update Slider, with that also update
        call BlzFrameSetMinMaxValue(TasButtonListSlider[listIndex], TasButtonListButtonCount[listIndex], filteredDataCount)
        call BlzFrameSetValue(TasButtonListSlider[listIndex], 999999)
    endif
endfunction

function TasButtonListTriggerActionSync takes nothing returns nothing
    local integer listIndex = LoadInteger(Hash, GetHandleId(BlzGetTriggerFrame()), 0)
    local integer dataIndex = R2I(BlzGetTriggerFrameValue() + 0.5)

    set TasButtonListData = LoadInteger(Hash, GetHandleId(LoadLocationHandle(Hash, GetHandleId(TasButtonListDataList[listIndex]), GetPlayerId(GetTriggerPlayer()))), dataIndex)
    set TasButtonListIndex = listIndex
    call TriggerExecute(TasButtonListButtonAction[listIndex])

    call UpdateTasButtonList(listIndex)
endfunction

function TasButtonListTriggerActionButton takes nothing returns nothing
    local framehandle frame = BlzGetTriggerFrame()
    local integer buttonIndex = LoadInteger(Hash, GetHandleId(frame), 1)
    local integer listIndex = LoadInteger(Hash, GetHandleId(frame), 0)
    local integer dataIndex = LoadInteger(Hash, GetHandleId(TasButtonListDataListFiltered[listIndex]), buttonIndex + TasButtonListViewPoint[listIndex])
    local integer data = LoadInteger(Hash, GetHandleId(LoadLocationHandle(Hash, GetHandleId(TasButtonListDataList[listIndex]), GetPlayerId(GetTriggerPlayer()))), dataIndex)
    call BlzFrameSetEnable(frame, false)
    call BlzFrameSetEnable(frame, true)
    set TasButtonListData = data
    set TasButtonListIndex = listIndex
    set TasButtonListFrame = frame
    if GetLocalPlayer() == GetTriggerPlayer() then
       call TriggerEvaluate(TasButtonListAsyncAction[listIndex])
       call BlzFrameSetValue(TasButtonListSyncFrame[listIndex], dataIndex)
    endif
    set frame = null
endfunction

function TasButtonListTriggerActionSearch takes nothing returns nothing
    call TasButtonListSearch(LoadInteger(Hash, GetHandleId(BlzGetTriggerFrame()), 0), null)
endfunction

// scrolling while pointing on Buttons
function TasButtonListTriggerActionButtonScroll takes nothing returns nothing
    local integer listIndex = LoadInteger(Hash, GetHandleId(BlzGetTriggerFrame()), 0)
    local framehandle frame = TasButtonListSlider[listIndex]

    if GetLocalPlayer() == GetTriggerPlayer() then
        if BlzGetTriggerFrameValue() > 0 then
            call BlzFrameSetValue(frame, BlzFrameGetValue(frame) + TasButtonListStepSize[listIndex])
        else
            call BlzFrameSetValue(frame, BlzFrameGetValue(frame) - TasButtonListStepSize[listIndex])
        endif
    endif
    set frame = null
endfunction

// scrolling while pointing on slider aswell as calling
function TasButtonListTriggerActionSlider takes nothing returns nothing
    local integer listIndex = LoadInteger(Hash, GetHandleId(BlzGetTriggerFrame()), 0)
    local integer filteredDataHash = GetHandleId(TasButtonListDataListFiltered[listIndex])
    local integer dataFilteredCount = LoadInteger(Hash, filteredDataHash, 0)
    local framehandle frame = BlzGetTriggerFrame()
    if GetLocalPlayer() == GetTriggerPlayer() then
        if BlzGetTriggerFrameEvent() == FRAMEEVENT_MOUSE_WHEEL then
            if BlzGetTriggerFrameValue() > 0 then
                call BlzFrameSetValue(frame, BlzFrameGetValue(frame) + TasButtonListStepSize[listIndex])
            else
                call BlzFrameSetValue(frame, BlzFrameGetValue(frame) - TasButtonListStepSize[listIndex])
            endif
        else
            // when there is enough data use viewPoint. the Viewpoint is reduced from the data to make top being top.
            if dataFilteredCount > TasButtonListButtonCount[listIndex] then
                set TasButtonListViewPoint[listIndex] = dataFilteredCount - R2I(BlzGetTriggerFrameValue())
            else
                set TasButtonListViewPoint[listIndex] = 0
            endif
            call UpdateTasButtonList(listIndex)
        endif
    endif
    set frame = null
endfunction

// runs once for each button shown
function UpdateTasButtonListDefaultObject takes nothing returns nothing
//TasButtonListFrame
//TasButtonListData
//TasButtonListIndex
    local integer frameHandle = GetHandleId(TasButtonListFrame)
    local integer data = TasButtonListData
    local integer lumber
    local integer gold
 
    call BlzFrameSetTexture(LoadFrameHandle(Hash, frameHandle, HASH_ICON),  BlzGetAbilityIcon(data), 0, false)
    call BlzFrameSetText(LoadFrameHandle(Hash, frameHandle, HASH_TEXT), GetObjectName(data))

    call BlzFrameSetTexture(LoadFrameHandle(Hash, frameHandle, HASH_TOOL_TIP_ICON), BlzGetAbilityIcon(data), 0, false)
    call BlzFrameSetText(LoadFrameHandle(Hash, frameHandle, HASH_TOOL_TIP_NAME), GetObjectName(data))
    call BlzFrameSetText(LoadFrameHandle(Hash, frameHandle, HASH_TOOL_TIP_TEXT), BlzGetAbilityExtendedTooltip(data, 0))

    if not IsUnitIdType(data, UNIT_TYPE_HERO) then
        // GetUnitWoodCost GetUnitGoldCost CRASH with heroes
        set lumber = GetUnitWoodCost(data)
        set gold = GetUnitGoldCost(data)
        if GetPlayerState(GetLocalPlayer(), PLAYER_STATE_RESOURCE_GOLD) >= gold then
            call BlzFrameSetText(LoadFrameHandle(Hash, frameHandle, HASH_TEXT_GOLD), I2S(GetUnitGoldCost(data)))
        else
            call BlzFrameSetText(LoadFrameHandle(Hash, frameHandle, HASH_TEXT_GOLD), "|cffff2010" + I2S(GetUnitGoldCost(data)))
        endif
       
        if GetPlayerState(GetLocalPlayer(), PLAYER_STATE_RESOURCE_LUMBER) >= lumber then
           call BlzFrameSetText(LoadFrameHandle(Hash, frameHandle, HASH_TEXT_LUMBER), I2S(GetUnitWoodCost(data)))
        else
           call BlzFrameSetText(LoadFrameHandle(Hash, frameHandle, HASH_TEXT_LUMBER), "|cffff2010" + I2S(GetUnitWoodCost(data)))
        endif
    else
        call BlzFrameSetText(LoadFrameHandle(Hash, frameHandle, HASH_TEXT_LUMBER), "0")
        call BlzFrameSetText(LoadFrameHandle(Hash, frameHandle, HASH_TEXT_GOLD), "0")
    endif
endfunction

function SearchTasButtonListDefaultObject takes nothing returns boolean
//TasButtonListText
//TasButtonListData
//TasButtonListIndex
    return FindIndex(GetObjectName(TasButtonListData), TasButtonListText) >= 0
endfunction

function InitTasButtonListObject8c takes framehandle parent, code buttonAction, code rightClickAction, code updateAction, code searchAction, code filterAction, code asyncAction, code asyncRigthAction returns integer
    local framehandle frame
    local integer playerIndex = 0
    set Counter = Counter + 1
    // the locations are created to have an unique slot in the hash which are used as something like a Lua table.
    set TasButtonListDataList[Counter] = Location(0, 0) //
    // each player also got an own list
    loop
        call SaveLocationHandle(Hash, GetHandleId(TasButtonListDataList[Counter]), playerIndex, Location(0,0))
        set playerIndex = playerIndex + 1
        exitwhen playerIndex == bj_MAX_PLAYER_SLOTS
    endloop
    set TasButtonListDataListFiltered[Counter] = Location(0, 0) //
    set TasButtonListFrameList[Counter] = Location(0, 0) //
    set TasButtonListParent[Counter] = parent
    set TasButtonListViewPoint[Counter] = 0

    set TasButtonListButtonAction[Counter] = CreateTrigger() //call this inside the SyncAction after a button is clicked
    set TasButtonListUpdateAction[Counter] = CreateTrigger() //function defining how to display stuff (async)
    set TasButtonListFilterAction[Counter] = CreateTrigger() //function to return the searched Text (async)
    set TasButtonListSearchAction[Counter] = CreateTrigger()
    set TasButtonListRightAction[Counter] = CreateTrigger()
    set TasButtonListAsyncAction[Counter] = CreateTrigger()
    set TasButtonListAsyncRightAction[Counter] = CreateTrigger()
   
    call TriggerAddAction(TasButtonListButtonAction[Counter], buttonAction)
    if rightClickAction != null then
        call TriggerAddAction(TasButtonListRightAction[Counter], rightClickAction)
    endif
   
    // update is a condition with it can be run with TriggerEvaluate in localPlayer code. TriggerExecute would desync
    if updateAction == null then
        call TriggerAddCondition(TasButtonListUpdateAction[Counter], Filter(function UpdateTasButtonListDefaultObject))
    else
        call TriggerAddCondition(TasButtonListUpdateAction[Counter], Filter(updateAction))
    endif

    if searchAction == null then
        call TriggerAddCondition(TasButtonListSearchAction[Counter], Filter(function SearchTasButtonListDefaultObject))
    else
        call TriggerAddCondition(TasButtonListSearchAction[Counter], Filter(searchAction))
    endif
    if filterAction != null then
        call TriggerAddCondition(TasButtonListFilterAction[Counter], Filter(filterAction))
    endif

    if asyncAction != null then
        call TriggerAddCondition(TasButtonListAsyncAction[Counter], Filter(asyncAction))
    endif

    if asyncRigthAction != null then
        call TriggerAddCondition(TasButtonListAsyncRightAction[Counter], Filter(asyncRigthAction))
    endif
   
    set frame = BlzCreateFrameByType("SLIDER", "", parent, "", 0)
    set TasButtonListSyncFrame[Counter] = frame
    call BlzFrameSetMinMaxValue(frame, 0, 9999999)
    call BlzFrameSetStepSize(frame, 1.0)
    call BlzTriggerRegisterFrameEvent(SyncTrigger, frame, FRAMEEVENT_SLIDER_VALUE_CHANGED)
    call BlzFrameSetVisible(frame, false)
    call SaveInteger(Hash, GetHandleId(frame), 0, Counter)

    set frame = BlzCreateFrameByType("SLIDER", "", parent, "", 0)
    set TasButtonListSyncFrameRight[Counter] = frame
    call BlzFrameSetMinMaxValue(frame, 0, 9999999)
    call BlzFrameSetStepSize(frame, 1.0)
    call BlzTriggerRegisterFrameEvent(SyncRightTrigger, frame, FRAMEEVENT_SLIDER_VALUE_CHANGED)
    call BlzFrameSetVisible(frame, false)
    call SaveInteger(Hash, GetHandleId(frame), 0, Counter)

    set frame = BlzCreateFrame("TasEditBox", parent, 0, 0)
    set TasButtonListInputFrame[Counter] = frame
    call BlzTriggerRegisterFrameEvent(SearchTrigger, frame, FRAMEEVENT_EDITBOX_TEXT_CHANGED)
    call BlzFrameSetPoint(frame, FRAMEPOINT_TOPRIGHT, parent, FRAMEPOINT_TOPRIGHT, 0, 0)
    call SaveInteger(Hash, GetHandleId(frame), 0, Counter)

    set frame = null
    return Counter
endfunction

// this should have beend called InitTasButtonListObject8
function InitTasButtonListObjectEx takes framehandle parent, code buttonAction, code rightClickAction, code updateAction, code searchAction, code filterAction returns integer
    return InitTasButtonListObject8c(parent, buttonAction, rightClickAction, updateAction, searchAction, filterAction, null, null)
endfunction

function InitTasButtonListObject takes framehandle parent, code buttonAction, code updateAction, code searchAction, code filterAction returns integer
    return InitTasButtonListObjectEx(parent, buttonAction, null, updateAction, searchAction, filterAction)
endfunction

function InitTasButtonListSlider takes integer listIndex, integer stepSize, integer rowCount returns nothing
    local framehandle frame = BlzCreateFrameByType("SLIDER", "FrameListSlider", TasButtonListParent[listIndex], "QuestMainListScrollBar", 0)
    local framehandle buttonFrame = LoadFrameHandle(Hash, GetHandleId(TasButtonListFrameList[listIndex]), 1)
    set TasButtonListSlider[listIndex] = frame
    call SaveInteger(Hash, GetHandleId(frame), 0, listIndex) // the slider nows the TasButtonListobject
    set TasButtonListStepSize[listIndex] = stepSize
   
    call BlzFrameSetStepSize(frame, stepSize)
    call BlzFrameClearAllPoints(frame)
    call BlzFrameSetVisible(frame, true)
    call BlzFrameSetMinMaxValue(frame, 0, 0)
    call BlzFrameSetPoint(frame, FRAMEPOINT_TOPLEFT, buttonFrame, FRAMEPOINT_TOPRIGHT, 0, 0)
    call BlzFrameSetSize(frame, 0.012, BlzFrameGetHeight(buttonFrame) * rowCount)
    call BlzTriggerRegisterFrameEvent(SliderTrigger, frame , FRAMEEVENT_SLIDER_VALUE_CHANGED)
    call BlzTriggerRegisterFrameEvent(SliderTrigger, frame , FRAMEEVENT_MOUSE_WHEEL)
endfunction

//Demo Creators
function CreateTasButtonTooltip takes integer frameHandle, framehandle frameButton, framehandle parent returns nothing
    local framehandle frame = BlzCreateFrame("TasButtonListTooltipBox", frameButton, 0, 0)
    local framehandle frameText = BlzGetFrameByName("TasButtonListTooltipText", 0)
    call SaveFrameHandle(Hash, frameHandle, HASH_TOOL_TIP, frame)
    call SaveFrameHandle(Hash, frameHandle, HASH_TOOL_TIP_ICON, BlzGetFrameByName("TasButtonListTooltipIcon", 0))
    call SaveFrameHandle(Hash, frameHandle, HASH_TOOL_TIP_NAME, BlzGetFrameByName("TasButtonListTooltipName", 0))
    call SaveFrameHandle(Hash, frameHandle, HASH_TOOL_TIP_SEP, BlzGetFrameByName("TasButtonListTooltipSeperator", 0))
    call SaveFrameHandle(Hash, frameHandle, HASH_TOOL_TIP_TEXT, frameText)

    call BlzFrameSetTooltip(frameButton, frame)

    call BlzFrameSetPoint(frameText, FRAMEPOINT_TOPRIGHT, parent, FRAMEPOINT_TOPLEFT, -0.001, -0.052)
    call BlzFrameSetPoint(frame, FRAMEPOINT_TOPLEFT, BlzGetFrameByName("TasButtonListTooltipIcon", 0), FRAMEPOINT_TOPLEFT, -0.005, 0.005)
    call BlzFrameSetPoint(frame, FRAMEPOINT_BOTTOMRIGHT, frameText, FRAMEPOINT_BOTTOMRIGHT, 0.005, -0.005)
endfunction

function CreateTasButtonList10 takes string buttonName, integer cols, integer rows, framehandle parent, code buttonAction, code rightClickAction, code updateAction, code searchAction, code filterAction, code asyncAction, code asyncRigthAction, real colGap, real rowGap returns integer
    local integer buttonCount = rows*cols
    local integer listIndex = InitTasButtonListObject8c(parent, buttonAction, rightClickAction, updateAction, searchAction, filterAction, asyncAction, asyncRigthAction)
    local integer i = 1
    local integer frameListHash = GetHandleId(TasButtonListFrameList[listIndex])
    local framehandle frame
    local framehandle frameButton
    local integer frameHandle

    local integer rowRemain = cols
    set TasButtonListButtonCount[listIndex] = buttonCount
    loop
        exitwhen i > buttonCount
       
        set frame = BlzCreateFrame(buttonName, parent, 0, 0)
        set frameHandle = GetHandleId(frame)
        if frameHandle == 0 then
            call BJDebugMsg("TasButtonList - Error - can't create Button:" + buttonName)
        endif
        call SaveFrameHandle(Hash, frameListHash, i, frame)
        call SaveInteger(Hash, frameHandle, 0, listIndex) // the button knows the TasButtonListIndex
        call SaveInteger(Hash, frameHandle, 1, i) // the button knows its index in the frameList
        call BlzTriggerRegisterFrameEvent(ButtonTrigger, frame, FRAMEEVENT_CONTROL_CLICK)
        call BlzTriggerRegisterFrameEvent(ButtonRightTrigger, frame, FRAMEEVENT_MOUSE_UP)
        call BlzTriggerRegisterFrameEvent(ButtonScrollTrigger, frame, FRAMEEVENT_MOUSE_WHEEL)
        set frameButton = frame

        call SaveFrameHandle(Hash, frameHandle, HASH_ICON, BlzGetFrameByName("TasButtonIcon", 0))
        call SaveFrameHandle(Hash, frameHandle, HASH_TEXT, BlzGetFrameByName("TasButtonText", 0))
        call SaveFrameHandle(Hash, frameHandle, HASH_ICON_GOLD, BlzGetFrameByName("TasButtonIconGold", 0))
        call SaveFrameHandle(Hash, frameHandle, HASH_TEXT_GOLD, BlzGetFrameByName("TasButtonTextGold", 0))
        call SaveFrameHandle(Hash, frameHandle, HASH_ICON_LUMBER, BlzGetFrameByName("TasButtonIconLumber", 0))
        call SaveFrameHandle(Hash, frameHandle, HASH_TEXT_LUMBER, BlzGetFrameByName("TasButtonTextLumber", 0))

        call CreateTasButtonTooltip(frameHandle, frameButton, parent)

        if i > 1 then
            if rowRemain == 0 then
                call BlzFrameSetPoint(frameButton, FRAMEPOINT_TOP, LoadFrameHandle(Hash, frameListHash, i - cols), FRAMEPOINT_BOTTOM, 0, -rowGap)
                set rowRemain = cols
            else
                call BlzFrameSetPoint(frameButton, FRAMEPOINT_RIGHT, LoadFrameHandle(Hash, frameListHash, i - 1), FRAMEPOINT_LEFT, -colGap, 0)
            endif
        else
            call BlzFrameSetPoint(frameButton, FRAMEPOINT_TOPRIGHT, TasButtonListInputFrame[listIndex], FRAMEPOINT_BOTTOMRIGHT, 0, 0)
        endif
        set rowRemain = rowRemain - 1


        set i = i + 1
    endloop
    call InitTasButtonListSlider(listIndex, cols, rows)
    set frame = null
    set frameButton = null
    return listIndex
endfunction

function CreateTasButtonList8c takes string buttonName, integer cols, integer rows, framehandle parent, code buttonAction, code rightClickAction, code updateAction, code searchAction, code filterAction, code asyncAction, code asyncRigthAction returns integer
    return CreateTasButtonList10(buttonName, cols, rows, parent, buttonAction, rightClickAction, updateAction, searchAction, filterAction, asyncAction, asyncRigthAction, TasButtonListGapCol, TasButtonListGapRow)
endfunction


function CreateTasButtonListEx takes string buttonName, integer cols, integer rows, framehandle parent, code buttonAction, code rightClickAction, code updateAction, code searchAction, code filterAction returns integer
    return CreateTasButtonList8c(buttonName, cols, rows, parent, buttonAction, rightClickAction, updateAction, searchAction, filterAction, null, null)
endfunction

function CreateTasButtonList takes integer buttonCount, framehandle parent, code buttonAction, code updateAction, code searchAction, code filterAction returns integer
    return CreateTasButtonListEx("TasButton", 1, buttonCount, parent, buttonAction, null, updateAction, searchAction, filterAction)
endfunction

function CreateTasButtonListV2 takes integer rowCount, framehandle parent, code buttonAction, code updateAction, code searchAction, code filterAction returns integer
    return CreateTasButtonListEx("TasButtonSmall", 2, rowCount, parent, buttonAction, null, updateAction, searchAction, filterAction)
endfunction

function CreateTasButtonListV3 takes integer rowCount, framehandle parent, code buttonAction, code updateAction, code searchAction, code filterAction returns integer
    return CreateTasButtonListEx("TasButtonGrid", 3, rowCount, parent, buttonAction, null, updateAction, searchAction, filterAction)
endfunction

function TasButtonListAddDataEx takes integer listIndex, integer data, integer playerIndex returns nothing
    local integer listHandle = GetHandleId(LoadLocationHandle(Hash, GetHandleId(TasButtonListDataList[listIndex]), playerIndex))
    local integer index = LoadInteger(Hash, listHandle, 0) + 1
    call SaveInteger(Hash, listHandle, 0, index)
    call SaveInteger(Hash, listHandle, index, data)
    if GetLocalPlayer() == Player(playerIndex) then
        // add to current filtered for local player only
        set listHandle = GetHandleId(TasButtonListDataListFiltered[listIndex])
        call SaveInteger(Hash, listHandle, 0, index)
        call SaveInteger(Hash, listHandle, index, index)
        call BlzFrameSetMinMaxValue(TasButtonListSlider[listIndex], TasButtonListButtonCount[listIndex], index)
    endif
endfunction

function TasButtonListAddData takes integer listIndex, integer data returns nothing
    local integer playerIndex = 0
    loop
        call TasButtonListAddDataEx(listIndex, data, playerIndex)
        set playerIndex = playerIndex + 1
        exitwhen playerIndex == bj_MAX_PLAYER_SLOTS
    endloop
endfunction

function TasButtonListCopyDataEx takes integer writeObject, integer readObject, integer playerIndex returns nothing
    local integer i = LoadInteger(Hash, GetHandleId(TasButtonListDataListFiltered[readObject]), 0)
    local integer listHandleRead = GetHandleId(LoadLocationHandle(Hash, GetHandleId(TasButtonListDataList[readObject]), playerIndex))
    local integer listHandleWrite = GetHandleId(LoadLocationHandle(Hash, GetHandleId(TasButtonListDataList[writeObject]), playerIndex))
    call FlushChildHashtable(Hash, listHandleWrite)
    call RemoveLocation(TasButtonListDataList[writeObject])

    loop
        exitwhen i < 0
        call SaveInteger(Hash, listHandleWrite, i, LoadInteger(Hash, listHandleRead, i))
        set i = i -1
    endloop
    if GetLocalPlayer() == Player(playerIndex) then
        call BlzFrameSetMinMaxValue(TasButtonListSlider[writeObject], TasButtonListButtonCount[writeObject], LoadInteger(Hash, listHandleRead, 0))
        call UpdateTasButtonList(writeObject)
    endif
endfunction

function TasButtonListCopyData takes integer writeObject, integer readObject returns nothing
    local integer playerIndex = 0
    loop
        call TasButtonListCopyDataEx(writeObject, readObject, playerIndex)
        set playerIndex = playerIndex + 1
        exitwhen playerIndex == bj_MAX_PLAYER_SLOTS
    endloop
endfunction

function TasButtonListRemoveDataEx takes integer listIndex, integer data, integer playerIndex returns nothing
    local integer listHandle = GetHandleId(LoadLocationHandle(Hash, GetHandleId(TasButtonListDataList[listIndex]), playerIndex))
    local integer i = LoadInteger(Hash, listHandle, 0)
    local integer max = LoadInteger(Hash, listHandle, 0)
    local integer temp
    loop
        exitwhen i <= 0
        if LoadInteger(Hash, listHandle, 0) == data then
            call SaveInteger(Hash, listHandle, i, LoadInteger(Hash, listHandle, max))
            call SaveInteger(Hash, listHandle, 0, max - 1)
            call RemoveSavedInteger(Hash, listHandle, max)
            exitwhen true
        endif
        set i = i - 1
    endloop
    if GetLocalPlayer() == Player(playerIndex) then
        call BlzFrameSetMinMaxValue(TasButtonListSlider[listIndex], TasButtonListButtonCount[listIndex], LoadInteger(Hash, listHandle, 0))
    endif
endfunction

function TasButtonListRemoveData takes integer listIndex, integer data returns nothing
    local integer playerIndex = 0
    loop
        call TasButtonListRemoveDataEx(listIndex, data, playerIndex)
        set playerIndex = playerIndex + 1
        exitwhen playerIndex == bj_MAX_PLAYER_SLOTS
    endloop
endfunction

function TasButtonListClearDataEx takes integer listIndex, integer playerIndex returns nothing
    call FlushChildHashtable(Hash, GetHandleId(LoadLocationHandle(Hash, GetHandleId(TasButtonListDataList[listIndex]), playerIndex)))
    if GetLocalPlayer() == Player(playerIndex) then
        call FlushChildHashtable(Hash, GetHandleId(TasButtonListDataListFiltered[listIndex]))
        call BlzFrameSetMinMaxValue(TasButtonListSlider[listIndex], 0, 0)
    endif   
endfunction

function TasButtonListClearData takes integer listIndex returns nothing
    local integer playerIndex = 0
    loop
        call TasButtonListClearDataEx(listIndex, playerIndex)
        set playerIndex = playerIndex + 1
        exitwhen playerIndex == bj_MAX_PLAYER_SLOTS
    endloop
endfunction

    private function SyncRightTriggerAction takes nothing returns nothing 
        local integer listIndex = LoadInteger(Hash, GetHandleId(BlzGetTriggerFrame()), 0)
        local integer dataIndex = R2I(BlzGetTriggerFrameValue() + 0.5)
        set TasButtonListData = LoadInteger(Hash, GetHandleId(LoadLocationHandle(Hash, GetHandleId(TasButtonListDataList[listIndex]), GetPlayerId(GetTriggerPlayer()))), dataIndex)
        set TasButtonListIndex = listIndex
        call TriggerExecute(TasButtonListRightAction[listIndex])

        call UpdateTasButtonList(listIndex)
    endfunction
    private function ButtonRightClickTriggerAction takes nothing returns nothing
    local framehandle frame = BlzGetTriggerFrame()
    local integer buttonIndex = LoadInteger(Hash, GetHandleId(frame), 1)
    local integer listIndex = LoadInteger(Hash, GetHandleId(frame), 0)
    local integer dataIndex = LoadInteger(Hash, GetHandleId(TasButtonListDataListFiltered[listIndex]), buttonIndex + TasButtonListViewPoint[listIndex])
    local integer data = LoadInteger(Hash, GetHandleId(LoadLocationHandle(Hash, GetHandleId(TasButtonListDataList[listIndex]), GetPlayerId(GetTriggerPlayer()))), dataIndex)

    set TasButtonListData = data
    set TasButtonListIndex = listIndex
    set TasButtonListFrame = frame

        if IsRightClick(GetTriggerPlayer()) and GetLocalPlayer() == GetTriggerPlayer() then           
            call TriggerEvaluate(TasButtonListAsyncRightAction[listIndex])
            call StartSound(RightClickSound)
            call BlzFrameSetValue(TasButtonListSyncFrameRight[listIndex], dataIndex)
        endif
    endfunction

 private function LoadToc takes nothing returns nothing   
    call BlzLoadTOCFile("war3mapimported\\TasButtonList.toc") 
 endfunction
 private function Init takes nothing returns nothing   
    local integer loopA = 0
   
    call LoadToc()
    call TriggerAddAction(SyncTrigger, function TasButtonListTriggerActionSync)
    call TriggerAddAction(ButtonTrigger, function TasButtonListTriggerActionButton)
    call TriggerAddAction(SearchTrigger, function TasButtonListTriggerActionSearch)
    call TriggerAddAction(ButtonScrollTrigger, function TasButtonListTriggerActionButtonScroll)
    call TriggerAddAction(SliderTrigger, function TasButtonListTriggerActionSlider)
    call TriggerAddAction(SyncRightTrigger, function SyncRightTriggerAction)
    call TriggerAddAction(ButtonRightTrigger, function ButtonRightClickTriggerAction)

    set RightClickSound = CreateSound("Sound\\Interface\\MouseClick1.wav", false, false, false, 10, 10, "")
    call SetSoundParamsFromLabel(RightClickSound, "InterfaceClick")
    call SetSoundDuration(RightClickSound, 239)

endfunction

endlibrary

There are some differences between the (v)jass & Lua version.​
in the jass version the mapper's actions are run over triggers, hence there are globals instead of args.​
"" """""" The Button is the frameObject instead of being a member of it.​
""""" every TasButtonList is one Index in an array.​
""""" data used in TasButtonList is only integer.​
"""" accessing the Frames is ugly.​

Changelog

V11 (Lua)​
Fixed a missplacing with 1 col ButtonLists​
Added Simple Text ButtonLists​
TasButtonListAddData will FourCC given 4 digit strings (can be turned off/on with TasButtonList.Interpret4DigitString = true/false)​
added function TasButtonListAddDataBatch and TasButtonListAddDataBatchEx to feed a list with an moderate amount of data.​
new fdf​
the IsRightClick library is now optional, without it the RightClick features will not work.​
Added an description & features of the System at the top of the code comment​
V10c (jass)​
Fixed: TasButtonListAddDataEx did save wrong data into the current filtered list.​
V10a (jass)​
Rightclick was not aware of the the new Player based Data​

V10​
One can now add additional space between cols & rows. (jass) (old api calls can use this without changing their api by globals: TasButtonListGapCol & TasButtonListGapRow)​
Changed the used textures for the Buttons. The bordertexture were broken (Reforged and 1.31), in a more closer look.​


V9a​
(Lua) TasButtonListClearData cleared the FilterData List for all players.​

V9​
seperated&Changed the rightclick detection from the system.​
added optional AsyncButtonAction and AsyncRightClickAction. They are called directly inside the Click-Events for the clicking Player only.​
Each Player can now have it's own Data in one TasButtonList.​
Some fixes.​
TasButtons change color while they are pushed.​

V8​
A ButtonList can now have a RightClick Action​
Improved the Included Demo Creators​

V7c Lua only​
default-search ignores char case​
creates Triggers&Actions pre map init by hooking into InitBlizzard​

V7B - Fix​
Changed demo Map Code to stop desync in the Lua version.​

V7B​
some optimiziation in Lua version​
New Fdf​
Removed the unique name Part of children of TasButtonXxx​
The Tooltip Box will extend/shrink to fit the shown Text (Extended Tooltip).​
Pushed the TasButtonList Creators to the bottom of the Lua code (they are not so much tied to the system itself more an example of usage)​

V7A​
Slider's top position is now the data's top position​

V7​
One can now force a list to search & filter​
The Slider should now only show when there is more data than buttons​

V6​
replaced BlzSendSyncData with a SLIDER​
removed some wrong arguments or when they were to much mostly GetObjectName and BlzFrameCreateByType.​
Added a (v)jass version​

V5 First Release​
Contents

TasButtonListV10c jass (Map)

TasButtonListV11 (Map)

Reviews
MyPad
This can be very useful for mappers. Approved
Level 3
Joined
Aug 18, 2016
Messages
42
Brilliant! Super helpful. I got it to work too! Thank you Tasyen! Now I just need to study it and learn how you made it.
 
Level 3
Joined
Aug 18, 2016
Messages
42
Question: how to i get the Button List to close out and then Open a different Button List depending on which button I click on?

Example:

TasButtonListAddData(object, {Icon = "ReplaceableTextures\\CommandButtons\\PASHolyNovaHD.dds", Text = "Wizard"})
TasButtonListAddData(object, {Icon = "ReplaceableTextures\\CommandButtons\\BTNSlow", Text = "Custom B"})
TasButtonListAddData(object, {Icon = "ReplaceableTextures\\CommandButtons\\BTNInvisibility", Text = "Custom C"})
TasButtonListAddData(object, {Icon = "ReplaceableTextures\\CommandButtons\\BTNBanish", Text = "Custom D"})
TasButtonListAddData(object, {Icon = "ReplaceableTextures\\CommandButtons\\BTNCrystalBall", Text = "Custom E"})
TasButtonListAddData(object, {Icon = "ReplaceableTextures\\CommandButtons\\BTNBoots", Text = "Custom F"})
TasButtonListAddData(object, {Icon = "ReplaceableTextures\\CommandButtons\\BTNTalisman", Text = "Custom G"})

If I click on the Wizard button I want to go to the Units under that category.
 
To show hide a TasButtonList one uses BlzFrameSetVisible onto the ButtonLists Parent, at least it is meant to be handled that way. The buttonslist knows their parent: can be done like that BlzFrameSetVisible(buttonListObject.Parent, false)

Now you have to set the buttonAction as wanted when creating the ButtonList:
inside the buttonAction somehow know that the wanted Button showing that data is clicked, here I check for data.Text == "Wizard" then show/hide the parents of the buttonsLists for the triggering player only.
Lua:
local object = CreateTasButtonList(4, frame, function(data, buttonListObject, dataIndex)
        if data.Text == "Wizard" and GetTriggerPlayer() == GetLocalPlayer() then
            BlzFrameSetVisible(buttonListObject.Parent, false)
            BlzFrameSetVisible(otherButtonList.Parent, true)
        end
    end)
This example will shows only the concept of showing hiding, might be easier to understand than the now longer example below:

Lua:
do
    local real = MarkGameStarted
    function MarkGameStarted()
        real()
    xpcall(function()
    -- create an empty Frame that will be the ButtonLists parent. if you want to hide/move the ButtonList hide/move the parent.
    local frame = BlzCreateFrameByType("FRAME", "", BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI, 0), 0,0)
    BlzFrameSetSize(frame, 0.23, 0.001)
    BlzFrameSetAbsPoint(frame, FRAMEPOINT_TOP, 0.6, 0.5)
    -- create a new ButtonList with 4 Rows, frame is the parent and define the action when clicking the Button
    local object = CreateTasButtonList(3, frame, function(data, buttonListObject, dataIndex)
        if data.Text == "Wizard" and GetTriggerPlayer() == GetLocalPlayer() then
            BlzFrameSetVisible(buttonListObject.Parent, false)
            BlzFrameSetVisible(WizardButtonList.Parent, true)
        end
    end,
    function(frameObject, data)
        BlzFrameSetTexture(frameObject.Icon, data.Icon, 0, false)
        BlzFrameSetText(frameObject.Text, data.Text)

        BlzFrameSetTexture(frameObject.ToolTipFrameIcon, data.Icon, 0, false)
        BlzFrameSetText(frameObject.ToolTipFrameName, data.Text)
    end)
    -- add various data
    TasButtonListAddData(object, {Icon = "ReplaceableTextures\\CommandButtons\\BTNPriest", Text = "Wizard"})
    TasButtonListAddData(object, {Icon = "ReplaceableTextures\\CommandButtons\\BTNSlow", Text = "Custom B"})
    TasButtonListAddData(object, {Icon = "ReplaceableTextures\\CommandButtons\\BTNInvisibility", Text = "Custom C"})

    MainList = object
    UpdateTasButtonList(object)
 
    local frame = BlzCreateFrameByType("FRAME", "", BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI, 0), 0,0)
    BlzFrameSetSize(frame, 0.23, 0.001)
    BlzFrameSetAbsPoint(frame, FRAMEPOINT_TOP, 0.6, 0.5)
    BlzFrameSetVisible(frame, false)
    -- create a new ButtonList with 4 Rows, frame is the parent and define the action when clicking the Button
    local object = CreateTasButtonList(2, frame, function(data, buttonListObject, dataIndex)
        if data.Text == "Back" and GetTriggerPlayer() == GetLocalPlayer() then
            BlzFrameSetVisible(buttonListObject.Parent, false)
            BlzFrameSetVisible(MainList.Parent, true)
        elseif  data.Text == "Close" and GetTriggerPlayer() == GetLocalPlayer() then
            BlzFrameSetVisible(buttonListObject.Parent, false)
        end
    end,
    function(frameObject, data)
        BlzFrameSetTexture(frameObject.Icon, data.Icon, 0, false)
        BlzFrameSetText(frameObject.Text, data.Text)

        BlzFrameSetTexture(frameObject.ToolTipFrameIcon, data.Icon, 0, false)
        BlzFrameSetText(frameObject.ToolTipFrameName, data.Text)
    end)
    WizardButtonList = object
    TasButtonListAddData(object, {Icon = "ReplaceableTextures\\CommandButtons\\BTNTalisman", Text = "Back"})
    TasButtonListAddData(object, {Icon = "ReplaceableTextures\\CommandButtons\\BTNTalisman", Text = "Close"})

    -- force an update
    UpdateTasButtonList(object)

    print("done")
end, print)
end
end

Good that this system is useful to you.

Edited: Replaced selfexecution with a less problematic approach
 
Last edited:
Level 3
Joined
Aug 18, 2016
Messages
42
To show hide a TasButtonList one uses BlzFrameSetVisible onto the ButtonLists Parent, at least it is meant to be handled that way. The buttonslist knows their parent: can be done like that BlzFrameSetVisible(buttonListObject.Parent, false)

Now you have to set the buttonAction as wanted when creating the ButtonList:
inside the buttonAction somehow know that the wanted Button showing that data is clicked, here I check for data.Text == "Wizard" then show/hide the parents of the buttonsLists for the triggering player only.
Lua:
local object = CreateTasButtonList(4, frame, function(data, buttonListObject, dataIndex)
        if data.Text == "Wizard" and GetTriggerPlayer() == GetLocalPlayer() then
            BlzFrameSetVisible(buttonListObject.Parent, false)
            BlzFrameSetVisible(otherButtonList.Parent, true)
        end
    end)
This example will shows only the concept of showing hiding, might be easier to understand than the now longer example below:

Lua:
TimerStart(CreateTimer(), 0.0, false, function()
    xpcall(function()
    -- create an empty Frame that will be the ButtonLists parent. if you want to hide/move the ButtonList hide/move the parent.
    local frame = BlzCreateFrameByType("FRAME", "", BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI, 0), 0,0)
    BlzFrameSetSize(frame, 0.23, 0.001)
    BlzFrameSetAbsPoint(frame, FRAMEPOINT_TOP, 0.6, 0.5)
    -- create a new ButtonList with 4 Rows, frame is the parent and define the action when clicking the Button
    local object = CreateTasButtonList(3, frame, function(data, buttonListObject, dataIndex)
        if data.Text == "Wizard" and GetTriggerPlayer() == GetLocalPlayer() then
            BlzFrameSetVisible(buttonListObject.Parent, false)
            BlzFrameSetVisible(WizardButtonList.Parent, true)
        end
    end,
    function(frameObject, data)
        BlzFrameSetTexture(frameObject.Icon, data.Icon, 0, false)
        BlzFrameSetText(frameObject.Text, data.Text)

        BlzFrameSetTexture(frameObject.ToolTipFrameIcon, data.Icon, 0, false)
        BlzFrameSetText(frameObject.ToolTipFrameName, data.Text)
    end)
    -- add various data
    TasButtonListAddData(object, {Icon = "ReplaceableTextures\\CommandButtons\\BTNPriest", Text = "Wizard"})
    TasButtonListAddData(object, {Icon = "ReplaceableTextures\\CommandButtons\\BTNSlow", Text = "Custom B"})
    TasButtonListAddData(object, {Icon = "ReplaceableTextures\\CommandButtons\\BTNInvisibility", Text = "Custom C"})

    MainList = object
    UpdateTasButtonList(object)
 
    local frame = BlzCreateFrameByType("FRAME", "", BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI, 0), 0,0)
    BlzFrameSetSize(frame, 0.23, 0.001)
    BlzFrameSetAbsPoint(frame, FRAMEPOINT_TOP, 0.6, 0.5)
    BlzFrameSetVisible(frame, false)
    -- create a new ButtonList with 4 Rows, frame is the parent and define the action when clicking the Button
    local object = CreateTasButtonList(2, frame, function(data, buttonListObject, dataIndex)
        if data.Text == "Back" and GetTriggerPlayer() == GetLocalPlayer() then
            BlzFrameSetVisible(buttonListObject.Parent, false)
            BlzFrameSetVisible(MainList.Parent, true)
        elseif  data.Text == "Close" and GetTriggerPlayer() == GetLocalPlayer() then
            BlzFrameSetVisible(buttonListObject.Parent, false)
        end
    end,
    function(frameObject, data)
        BlzFrameSetTexture(frameObject.Icon, data.Icon, 0, false)
        BlzFrameSetText(frameObject.Text, data.Text)

        BlzFrameSetTexture(frameObject.ToolTipFrameIcon, data.Icon, 0, false)
        BlzFrameSetText(frameObject.ToolTipFrameName, data.Text)
    end)
    WizardButtonList = object
    TasButtonListAddData(object, {Icon = "ReplaceableTextures\\CommandButtons\\BTNTalisman", Text = "Back"})
    TasButtonListAddData(object, {Icon = "ReplaceableTextures\\CommandButtons\\BTNTalisman", Text = "Close"})

    -- force an update
    UpdateTasButtonList(object)

    print("done")
    DestroyTimer(GetExpiredTimer())
end, print)
end)

Good that this system is useful to you.

Thank you again.
 
Level 3
Joined
Aug 18, 2016
Messages
42
I am still having trouble creating categories. I think I almost have all the code I need, but there is still stuff I don't understand. I am attaching my map file to this post. Maybe if you look at what I'm trying to do you will be able to give me instructions. I was able to make the 1st category but not all the others. Imek Amtgard map 05-31-2020 | HIVE

Question: Can you please tell me how to finish adding the categories to my map? I need 13 different categories for different units. The 1st category has Goose and Fox in it.
 
Level 3
Joined
Aug 18, 2016
Messages
42
Never mind I figured out how to it works. I added all the categories. Thank you again Tasyen!
 
Level 3
Joined
Aug 18, 2016
Messages
42
So I have this code below and I need to UpdateTasButtonList(object) for the --add various data section of the code. But I can't get the icon with the value H001 to update and show up in the menu. Can some one please help me do this?

--******SUMMON UNITS For WIZARD CLASS****************************************************************
local frame = BlzCreateFrameByType("FRAME", "", BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI, 0), 0,0)
BlzFrameSetSize(frame, 0.23, 0.001)
BlzFrameSetAbsPoint(frame, FRAMEPOINT_TOP, 0.4, 0.5)
BlzFrameSetVisible(frame, false)

-- create a new ButtonList with 10 Rows, frame is the parent and define the action when clicking the Button
local object = CreateTasButtonList(10, frame, function(data, buttonListObject, dataIndex)
if data.Text == "Back" and GetTriggerPlayer() == GetLocalPlayer() then
BlzFrameSetVisible(buttonListObject.Parent, false)
BlzFrameSetVisible(MainList.Parent, true)
elseif data.Text == "Close" and GetTriggerPlayer() == GetLocalPlayer() then
BlzFrameSetVisible(buttonListObject.Parent, false)
end

if data.Text == "Atta" and GetTriggerPlayer() == GetLocalPlayer() then
BlzFrameSetVisible(buttonListObject.Parent, false)
local frame = BlzCreateFrameByType("FRAME", "", BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI, 0), 0,0)
BlzFrameSetSize(frame, 0.23, 0.001)
BlzFrameSetAbsPoint(frame, FRAMEPOINT_TOP, 0.4, 0.5)
-- create a new ButtonList with 1 Rows, frame is the parent and define the action when clicking the Button
local object = CreateTasButtonList(1, frame, function(data, buttonListObject, dataIndex)
-- create an unit for the clicking player at 0/0 with facing 0
CreateUnit(GetTriggerPlayer(), data, 0, 0, 0)
BlzFrameSetVisible(buttonListObject.Parent, false)
end)

-- add various data
TasButtonListAddData(object, FourCC("H001"))

end
 
I am curious if a Jass version is something you plan on doing?

Regardless, this seems neat as usual. +rep
I was expecting this, hence I already have thinkered about a way to create this in (v)jass. I am not against it. Probably do it in some time, but don't expect great (v)jass.
 
Last edited:
Updated to Version6 which includes some small changes/fixes aswell as a (v)jass version.

The (v)jass versions was built out of the Lua version hence it is also quite similar.
There are still some differences between the (v)jass & Lua version.
in the jass version the mapper's actions are run over triggers, hence there are globals instead of args.
"" """""" The Button is the frameObject instead of being a member of it.
""""" every TasButtonList is one Index in an array.
""""" data used in TasButtonList is only integer.
"""" accessing the Frames is ugly.​
JASS:
    //args for custom user actions
    integer TasButtonListData = 0
    string TasButtonListText = ""
    boolean TasButtonListIsSearching = false
    integer TasButtonListIndex = 0
    framehandle TasButtonListFrame = null

The (v)jass versions did desync at first when i was using TriggerAction + TriggerExecute for the updateAction. In the released version this was replaced with TriggerCondition + TriggerEvaluate. With this change there were no desyncs during testing in Warcraft 3 1.31.1.
 
Last edited:
Updated to V7 a small update.
the searchTriggerAction code was pushed into a call able function, making it more easy/faster to reapply filter/search.
The scrollFrame is now only shown when there is more filtered Data then buttons (it was also shown with an equal amount).
 
Last edited:
upload_2020-8-10_17-7-18.png
upload_2020-8-10_17-7-42.png
upload_2020-8-10_17-8-10.png
upload_2020-8-10_17-8-24.png

Visually speaking, the list of items displayed when the slider is at the top should be A-B-C-D. Instead, the slider is at the bottom. Likewise, when displaying D-E-F-G, the slider should be at the bottom, but is situated at the top.

This was tested on the lua version of the map, with a slight positional modification to accommodate for the pictures.
 
Updated to 7a:
The value of the slider is now subtracted from the max. Therefore the slider top position is now the top data position.

But now one has to set the slider position to the maxium after having filled it with data to prevent a glitch. This replaces the update Line which one had to do.
 
Updated to 7B:
The built in Tooltip-Box will now resize itself, based on the amount of text inside the Extended Tooltip (the bottom part).
7B uses a different fdf then previous versions, if you update you have to import the new one. From this version the fdf has in the first line a comment for which version it was created.
Made some parts of the Lua version faster.
Uploaded new Images to show that.
 
Last edited:
Level 1
Joined
Apr 8, 2020
Messages
110
Even though I saw the recommended version tag for this system, I just wanted to make sure that the current version works for the latest legacy patch.
 
Hey Tasyen you are amazing..
I trained for a long time.
Can you use this system as shop? To buy Items?
This System can help in creating a item shop.
TasButtonList is generic, it can use much but one has to tell it what to do with the given data when creating the list.
Items are cumbersome because one can not just get the gold costs with a getter (at least I don't know of such). Making the cost display difficult or custom written cost lists in trigger editor.

The Lua version has an example of buying items, but it is not a shop on the map. It also uses another piece of code to calc the costs buy buying the items with an passive player. Such a shop I did not show.

Edit: Uploaded a Fix for the demo code of the Lua version it. The way i made the code selfexecuting by creating&starting Timers in the Lua root code made a map desync as soon the garbage collector run in reforged (my guess at least).
 
Last edited:
Level 1
Joined
Sep 24, 2020
Messages
1
Can't figure out for the life of me why this is desyncing....

Using a modified version of this system, but at first garbage collection (About 1.5 minutes into my game) all players desync at the same time.

Disabling these two triggers fixes the issue.


EDIT: Changed MarkGameStarted to DetectGameStarted and desyncs less frequently but still does







Code:
do

    local realFunc = DetectGameStarted
    function DetectGameStarted()
    realFunc()
    xpcall(function()

    ItemData = {}

    local box = BlzCreateFrame("EscMenuBackdrop", BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI, 0), 0, 0)
    BlzFrameSetSize(box, 0.255, 0.29)
    BlzFrameSetAbsPoint(box, FRAMEPOINT_TOPRIGHT, .245, 0.53)

    local button = BlzCreateFrame("ScriptDialogButton", BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI, 0), 0, 0)
    local buttonText = BlzGetFrameByName("ScriptDialogButtonText",0)
    BlzFrameSetSize(button, 0.1, 0.03)
    BlzFrameSetAbsPoint(button, FRAMEPOINT_TOP, .12, 0.55)
    BlzFrameSetText(buttonText, "Hide Shop")


    local objectListA = object
    local frameB = BlzCreateFrameByType("FRAME", "", box, "",0)
    BlzFrameSetSize(frameB, 0.23, 0.001)
    --BlzFrameSetAbsPoint(frameB, FRAMEPOINT_TOPRIGHT, 0.78, 0.54)
    BlzFrameSetPoint(frameB, FRAMEPOINT_TOPLEFT, box, FRAMEPOINT_TOPLEFT, 0, -0.02)
    BlzFrameSetVisible(frameB, true)
    local object = CreateTasButtonList(9, frameB, function(data, buttonListObject, dataIndex)
        local player = GetTriggerPlayer()
        local lumber = ItemData[data].Lumber
        local gold = ItemData[data].Gold
        if GetPlayerState(player, PLAYER_STATE_RESOURCE_GOLD) >= gold then
            if GetPlayerState(player, PLAYER_STATE_RESOURCE_LUMBER) >= lumber then
                AdjustPlayerStateSimpleBJ(player, PLAYER_STATE_RESOURCE_GOLD, -gold)
                AdjustPlayerStateSimpleBJ(player, PLAYER_STATE_RESOURCE_LUMBER, -lumber)
        CreateItem(data, GetPlayerStartLocationX(player), GetPlayerStartLocationY(player))
            end
        end
    
    end,
    --runs once for each button shown
    function (frameObject, data)
        --print("Custom Update Item")
        BlzFrameSetTexture(frameObject.Icon, BlzGetAbilityIcon(data), 0, false)
        BlzFrameSetText(frameObject.Text, GetObjectName(data, 0))
 
        BlzFrameSetTexture(frameObject.ToolTipFrameIcon, BlzGetAbilityIcon(data), 0, false)
        BlzFrameSetText(frameObject.ToolTipFrameName, GetObjectName(data, 0))  
    --        frameObject.ToolTipFrameSeperator
        BlzFrameSetText(frameObject.ToolTipFrameText, BlzGetAbilityExtendedTooltip(data, 0))
        if ItemData[data] then
            local lumber = ItemData[data].Lumber
            local gold = ItemData[data].Gold
            if GetPlayerState(GetLocalPlayer(), PLAYER_STATE_RESOURCE_GOLD) >= gold then
                BlzFrameSetText(frameObject.TextGold, gold)
            else
                BlzFrameSetText(frameObject.TextGold, "|cffff2010"..gold)
            end
        
            if GetPlayerState(GetLocalPlayer(), PLAYER_STATE_RESOURCE_LUMBER) >= lumber then
                BlzFrameSetText(frameObject.TextLumber, lumber)
            else
                BlzFrameSetText(frameObject.TextLumber, "|cffff2010"..lumber)
            end
        else
            BlzFrameSetText(frameObject.TextLumber, "xxx")
            BlzFrameSetText(frameObject.TextGold, "xxx")
        end
    end)
    function AddItemAndGetCost(buttonListObject, itemCode)
        if GetObjectName(itemCode) ~= "" then
            TasButtonListAddData(object, itemCode)
            local tempLum = 0
            local tempGold = 0
        if itemCode == FourCC("I034") then
            tempGold = 10
        elseif itemCode == FourCC("I02U") then
            tempGold = 15
        elseif itemCode == FourCC("I02W") then
            tempGold = 15
        elseif itemCode == FourCC("I02V") then
            tempGold = 15
        elseif itemCode == FourCC("I018") then
            tempGold = 15
        elseif itemCode == FourCC("I01C") then
            tempGold = 35
        elseif itemCode == FourCC("I01D") then
            tempGold = 10
        elseif itemCode == FourCC("I00S") then
            tempGold = 25
        elseif itemCode == FourCC("I00R") then
            tempGold = 10
        elseif itemCode == FourCC("I01O") then
            tempGold = 25
        elseif itemCode == FourCC("I01S") then
            tempGold = 15
        elseif itemCode == FourCC("I02N") then
            tempGold = 15
        elseif itemCode == FourCC("I02O") then
            tempGold = 25
        elseif itemCode == FourCC("I036") then
            tempGold = 15
        elseif itemCode == FourCC("I035") then
            tempGold = 30
        elseif itemCode == FourCC("I02J") then
            tempGold = 30
        elseif itemCode == FourCC("I021") then
            tempGold = 60
        elseif itemCode == FourCC("I03O") then
            tempGold = 30
        elseif itemCode == FourCC("I03S") then
            tempGold = 25
        elseif itemCode == FourCC("I03N") then
            tempGold = 50
        elseif itemCode == FourCC("I03H") then
            tempGold = 35
        elseif itemCode == FourCC("I041") then
            tempLum = 2
        elseif itemCode == FourCC("I03Z") then
            tempLum = 2
        elseif itemCode == FourCC("I02Q") then
            tempLum = 2
        elseif itemCode == FourCC("I01M") then
            tempLum = 2
        elseif itemCode == FourCC("I02E") then
            tempLum = 2
        elseif itemCode == FourCC("I00Y") then
            tempLum = 2
        elseif itemCode == FourCC("I046") then
            tempLum = 2
        elseif itemCode == FourCC("I01U") then
            tempLum = 2
        elseif itemCode == FourCC("I01B") then
            tempLum = 1
        elseif itemCode == FourCC("I042") then
            tempGold = 5
        elseif itemCode == FourCC("I043") then
            tempGold = 5
        elseif itemCode == FourCC("I044") then
            tempGold = 5
        elseif itemCode == FourCC("I045") then
            tempGold = 5
        end
 
    if not ItemData[itemCode] then
            ItemData[itemCode] = {
                    Gold = tempGold,
                    Lumber = tempLum
                }
    end
        end
    end

    AddItemAndGetCost(object, FourCC("I034"))
    AddItemAndGetCost(object, FourCC("I02U"))
    AddItemAndGetCost(object, FourCC("I02W"))
    AddItemAndGetCost(object, FourCC("I02V"))
    AddItemAndGetCost(object, FourCC("I018"))
    AddItemAndGetCost(object, FourCC("I01C"))
    AddItemAndGetCost(object, FourCC("I01D"))
    AddItemAndGetCost(object, FourCC("I00S"))
    AddItemAndGetCost(object, FourCC("I00R"))
    AddItemAndGetCost(object, FourCC("I01O"))
    AddItemAndGetCost(object, FourCC("I01S"))
    AddItemAndGetCost(object, FourCC("I02N"))
    AddItemAndGetCost(object, FourCC("I02O"))
    AddItemAndGetCost(object, FourCC("I036"))
    AddItemAndGetCost(object, FourCC("I035"))
    AddItemAndGetCost(object, FourCC("I02J"))
    AddItemAndGetCost(object, FourCC("I021"))
    AddItemAndGetCost(object, FourCC("I03O"))
    AddItemAndGetCost(object, FourCC("I03S"))
    AddItemAndGetCost(object, FourCC("I03N"))
    AddItemAndGetCost(object, FourCC("I03H"))
    AddItemAndGetCost(object, FourCC("I041"))
    AddItemAndGetCost(object, FourCC("I03Z"))
    AddItemAndGetCost(object, FourCC("I02Q"))
    AddItemAndGetCost(object, FourCC("I01M"))    
    AddItemAndGetCost(object, FourCC("I02E"))
    AddItemAndGetCost(object, FourCC("I00Y"))
    AddItemAndGetCost(object, FourCC("I046"))
    AddItemAndGetCost(object, FourCC("I01U"))
    AddItemAndGetCost(object, FourCC("I01B"))
    AddItemAndGetCost(object, FourCC("I042"))
    AddItemAndGetCost(object, FourCC("I043"))
    AddItemAndGetCost(object, FourCC("I044"))
    AddItemAndGetCost(object, FourCC("I045"))

 
--    UpdateTasButtonList(object)
    BlzFrameSetValue(object.Slider, 9999)


    local trigger = CreateTrigger()
    TriggerAddAction(trigger, function()
        if GetLocalPlayer() == GetTriggerPlayer() then
            if BlzFrameIsVisible(frameB) then
                BlzFrameSetVisible(box, false)
                BlzFrameSetText(button, "Show Shop")
            else
                BlzFrameSetVisible(box, true)
                BlzFrameSetText(button, "Hide Shop")
            end
        end
    end)
    BlzTriggerRegisterFrameEvent(trigger, button, FRAMEEVENT_CONTROL_CLICK)
end, print)
end
end


Code:
--[[
TasButtonList7b by Tasyen

function CreateTasButtonList(buttonCount, parent, buttonAction[, updateAction, searchAction, filterAction])
create a new List
parent is the container of this Frame it will attach itself to its TOP.
buttonAction is the function that executes when an option is clicked. args: (clickedData, buttonListObject, dataIndex)
when your data are object Editor object-RawCodes (but not buffs) then updateAction & searchAction use a default one handling them.
updateAction runs for each Button and is used to set the diplayed content. args:(frameObject, data)
    frameObject.Button
    frameObject.ToolTipFrame
    frameObject.ToolTipFrameIcon
    frameObject.ToolTipFrameName
    frameObject.ToolTipFrameSeperator
    frameObject.ToolTipFrameText

    frameObject.Icon
    frameObject.Text
    frameObject.IconGold
    frameObject.TextGold
    frameObject.IconLumber
    frameObject.TextLumber
    TasButtonList[frameObject] => buttonListObject

    data is one entry of the TasButtonLists Data-Array.

searchAction is a function that returns true if the current data matches the searchText. Args: (data, searchedText, buttonListObject)
filterAction is meant to be used when one wants an addtional non text based filtering, with returning true allowing data or false rejecting it. Args: (data, buttonListObject, isTextSearching)
searchAction , udateAction & filterAction are async this functions should not do anything that alters the game state/flow.

function CreateTasButtonListV2(rowCount, parent, buttonAction[, updateAction, searchAction, filterAction])
    2 Buttons each Row, takes more Height then the other Versions
function CreateTasButtonListV3(rowCount, parent, buttonAction[, updateAction, searchAction, filterAction])
    3 Buttons each Row, only Icon, and Costs

function TasButtonListClearData(buttonListObject)
    remove all data
function TasButtonListRemoveData(buttonListObject, data)
    search for data and remove it
function TasButtonListAddData(buttonListObject, data)
    add data for one Button
function TasButtonListCopyData(writeObject, readObject)
    writeObject uses the same data as readObject and calls UpdateButtonList.
    The copier writeObject still has an own filtering and searching.
function UpdateTasButtonList(buttonListObject)
    update the displayed Content should be done after Data was added or removed was used.
TasButtonListSearch(buttonListObject[, text])
    The buttonList will search it's data for the given text, if nil is given as text it will search for what the user currently has in its box.
    This will also update the buttonList
--]]

BlzLoadTOCFile("war3mapimported\\TasButtonList.toc")
TasButtonList = {}

TasButtonList.SyncTrigger = CreateTrigger()
TasButtonList.SyncTriggerAction = TriggerAddAction(TasButtonList.SyncTrigger, function()
    xpcall(function()
    local buttonListObject = TasButtonList[BlzGetTriggerFrame()]
    local dataIndex = math.tointeger(BlzGetTriggerFrameValue())

    if buttonListObject.ButtonAction then
        -- call the wanted action, 1 the current Data
        buttonListObject.ButtonAction(buttonListObject.Data[dataIndex], buttonListObject, dataIndex)
    end
    UpdateTasButtonList(buttonListObject)
    end,print)
end)

-- handles the clicking
TasButtonList.ButtonTrigger = CreateTrigger()
TasButtonList.ButtonTriggerAction = TriggerAddAction(TasButtonList.ButtonTrigger, function()
    local buttonIndex = TasButtonList[BlzGetTriggerFrame()].Index
    local buttonListObject = TasButtonList[TasButtonList[BlzGetTriggerFrame()]]
    local dataIndex = buttonListObject.DataFiltered[buttonListObject.ViewPoint + buttonIndex]
    BlzFrameSetEnable(BlzGetTriggerFrame(), false)
    BlzFrameSetEnable(BlzGetTriggerFrame(), true)
    if GetLocalPlayer() == GetTriggerPlayer() then
        BlzFrameSetValue(buttonListObject.SyncFrame, dataIndex)
    end
end)

-- scrolling while pointing on Buttons
TasButtonList.ButtonScrollTrigger = CreateTrigger()
TasButtonList.ButtonScrollTriggerAction = TriggerAddAction(TasButtonList.ButtonScrollTrigger, function()
    local buttonListObject = TasButtonList[TasButtonList[BlzGetTriggerFrame()]]
    local frame = buttonListObject.Slider
    if GetLocalPlayer() == GetTriggerPlayer() then
        if BlzGetTriggerFrameValue() > 0 then
            BlzFrameSetValue(frame, BlzFrameGetValue(frame) + buttonListObject.SliderStep)
        else
            BlzFrameSetValue(frame, BlzFrameGetValue(frame) - buttonListObject.SliderStep)
        end
    end
end)

-- scrolling while pointing on slider aswell as calling
TasButtonList.SliderTrigger = CreateTrigger()
TasButtonList.SliderTriggerAction = TriggerAddAction(TasButtonList.SliderTrigger, function()
    local buttonListObject = TasButtonList[BlzGetTriggerFrame()]
    local frame = BlzGetTriggerFrame()
    if GetLocalPlayer() == GetTriggerPlayer() then
        if BlzGetTriggerFrameEvent() == FRAMEEVENT_MOUSE_WHEEL then
            if BlzGetTriggerFrameValue() > 0 then
                BlzFrameSetValue(frame, BlzFrameGetValue(frame) + buttonListObject.SliderStep)
            else
                BlzFrameSetValue(frame, BlzFrameGetValue(frame) - buttonListObject.SliderStep)
            end
        else
            -- when there is enough data use viewPoint. the Viewpoint is reduced from the data to make top being top.
            if buttonListObject.DataFiltered.Count > buttonListObject.Frames.Count then
                buttonListObject.ViewPoint = buttonListObject.DataFiltered.Count - math.tointeger(BlzGetTriggerFrameValue())
            else
                buttonListObject.ViewPoint = 0
            end
            UpdateTasButtonList(buttonListObject)
        end
    end
end)

--runs once for each button shown
function UpdateTasButtonListDefaultObject(frameObject, data)
    BlzFrameSetTexture(frameObject.Icon, BlzGetAbilityIcon(data), 0, false)
    BlzFrameSetText(frameObject.Text, GetObjectName(data))

    BlzFrameSetTexture(frameObject.ToolTipFrameIcon, BlzGetAbilityIcon(data), 0, false)
    BlzFrameSetText(frameObject.ToolTipFrameName, GetObjectName(data))  
--        frameObject.ToolTipFrameSeperator
    BlzFrameSetText(frameObject.ToolTipFrameText, BlzGetAbilityExtendedTooltip(data, 0))

    if not IsUnitIdType(data, UNIT_TYPE_HERO) then
        local lumber = GetUnitWoodCost(data)
        local gold = GetUnitGoldCost(data)
        if GetPlayerState(GetLocalPlayer(), PLAYER_STATE_RESOURCE_GOLD) >= gold then
            BlzFrameSetText(frameObject.TextGold, GetUnitGoldCost(data))
        else
            BlzFrameSetText(frameObject.TextGold, "|cffff2010"..GetUnitGoldCost(data))
        end
    
        if GetPlayerState(GetLocalPlayer(), PLAYER_STATE_RESOURCE_LUMBER) >= lumber then
            BlzFrameSetText(frameObject.TextLumber, GetUnitWoodCost(data))
        else
            BlzFrameSetText(frameObject.TextLumber, "|cffff2010"..GetUnitWoodCost(data))
        end
    else
        BlzFrameSetText(frameObject.TextLumber, 0)
        BlzFrameSetText(frameObject.TextGold, 0)
    end
end

-- update the shown content
function UpdateTasButtonList(buttonListObject)
    xpcall(function()
    local data = buttonListObject.Data
    BlzFrameSetVisible(buttonListObject.Slider, buttonListObject.DataFiltered.Count > buttonListObject.Frames.Count)
    for int = 1, buttonListObject.Frames.Count do
        local frameObject = buttonListObject.Frames[int]
        if buttonListObject.DataFiltered.Count >= int  then
            buttonListObject.UpdateAction(frameObject, data[buttonListObject.DataFiltered[int + buttonListObject.ViewPoint]])
            BlzFrameSetVisible(frameObject.Button, true)
        else
            BlzFrameSetVisible(frameObject.Button, false)
        end
    end
end, print)
end

function InitTasButtonListObject(parent, buttonAction, updateAction)
    local object = {
        Data = {Count = 0}, --an array each slot is the user data
        DataFiltered = {Count = 0}, -- indexes of Data fitting the current search
        ViewPoint = 0,
        Frames = {},
        Parent = parent
    }
    object.ButtonAction = buttonAction --call this inside the SyncAction after a button is clicked
    object.UpdateAction = updateAction --function defining how to display stuff (async)
    if not updateAction then object.UpdateAction = UpdateTasButtonListDefaultObject end
    if not searchAction then object.SearchAction = SearchTasButtonListDefaultObject end
    table.insert(TasButtonList, object) --index to TasButtonList
    TasButtonList[object] = #TasButtonList -- TasButtonList to Index

    object.SyncFrame = BlzCreateFrameByType("SLIDER", "", parent, "", 0)
    BlzFrameSetMinMaxValue(object.SyncFrame, 0, 9999999)
    BlzFrameSetStepSize(object.SyncFrame, 1.0)
    BlzTriggerRegisterFrameEvent(TasButtonList.SyncTrigger, object.SyncFrame, FRAMEEVENT_SLIDER_VALUE_CHANGED)
    BlzFrameSetVisible(object.SyncFrame, false)
    TasButtonList[object.SyncFrame] = object

    object.InputFrame = BlzCreateFrame("TeamLabelTextTemplate", parent, 0, 0)

    BlzFrameSetPoint(object.InputFrame, FRAMEPOINT_TOPRIGHT, parent, FRAMEPOINT_TOPRIGHT, 0, 0)
    TasButtonList[object.InputFrame] = object

    return object
end

function InitTasButtonListSlider(object, stepSize, rowCount)
    object.Slider = BlzCreateFrameByType("SLIDER", "FrameListSlider", object.Parent, "QuestMainListScrollBar", 0)
    TasButtonList[object.Slider] = object -- the slider nows the TasButtonListobject
    object.SliderStep = stepSize
    BlzFrameSetStepSize(object.Slider, stepSize)
    BlzFrameClearAllPoints(object.Slider)
    BlzFrameSetVisible(object.Slider, true)
    BlzFrameSetMinMaxValue(object.Slider, 0, 0)
    BlzFrameSetPoint(object.Slider, FRAMEPOINT_TOPLEFT, object.Frames[1].Button, FRAMEPOINT_TOPRIGHT, 0, 0)
    BlzFrameSetSize(object.Slider, 0.012, BlzFrameGetHeight(object.Frames[1].Button) * rowCount)
    BlzTriggerRegisterFrameEvent(TasButtonList.SliderTrigger, object.Slider , FRAMEEVENT_SLIDER_VALUE_CHANGED)
    BlzTriggerRegisterFrameEvent(TasButtonList.SliderTrigger, object.Slider , FRAMEEVENT_MOUSE_WHEEL)
end


function TasButtonListAddData(buttonListObject, data)
    local oData = buttonListObject.Data
    local oDataFil = buttonListObject.DataFiltered
    oData.Count = oData.Count + 1
    oDataFil.Count = oDataFil.Count + 1
    oData[oData.Count] = data
    oDataFil[oDataFil.Count] = oData.Count
    BlzFrameSetMinMaxValue(buttonListObject.Slider, buttonListObject.Frames.Count, oData.Count)
end

function TasButtonListRemoveData(buttonListObject, data)
    for index, value in ipairs(buttonListObject.Data)
    do
        if value == data then
            table.remove(buttonListObject.Data, index)
            buttonListObject.Data.Count = buttonListObject.Data.Count - 1
            break
        end
    end
    BlzFrameSetMinMaxValue(buttonListObject.Slider, buttonListObject.Frames.Count, buttonListObject.Data.Count)
end

function TasButtonListClearData(buttonListObject)
    repeat until not table.remove(buttonListObject.Data)
    repeat until not table.remove(buttonListObject.DataFiltered)
    BlzFrameSetMinMaxValue(buttonListObject.Slider, 0, 0)
end

function TasButtonListCopyData(writeObject, readObject)
    writeObject.Data = readObject.Data
    for index = 1, readObject.DataFiltered.Count do writeObject.DataFiltered[index] = readObject.DataFiltered[index] end
    writeObject.DataFiltered.Count = readObject.DataFiltered.Count

    BlzFrameSetMinMaxValue(writeObject.Slider, writeObject.Frames.Count, writeObject.Data.Count)
    BlzFrameSetValue(writeObject.Slider,999999)
end

-- demo Creators
function CreateTasButtonTooltip(frameObject, parent)
    frameObject.ToolTipFrame = BlzCreateFrame("TasButtonListTooltipBox", frameObject.Button, 0, 0)
    frameObject.ToolTipFrameIcon = BlzGetFrameByName("TasButtonListTooltipIcon", 0)
    frameObject.ToolTipFrameName = BlzGetFrameByName("TasButtonListTooltipName", 0)
    frameObject.ToolTipFrameSeperator = BlzGetFrameByName("TasButtonListTooltipSeperator", 0)
    frameObject.ToolTipFrameText = BlzGetFrameByName("TasButtonListTooltipText", 0)    
    BlzFrameSetPoint(frameObject.ToolTipFrameText, FRAMEPOINT_TOPLEFT, parent, FRAMEPOINT_TOPRIGHT, 0.025, -0.052)
    BlzFrameSetPoint(frameObject.ToolTipFrame, FRAMEPOINT_TOPLEFT, frameObject.ToolTipFrameIcon, FRAMEPOINT_TOPLEFT, -0.005, 0.005)
    BlzFrameSetPoint(frameObject.ToolTipFrame, FRAMEPOINT_BOTTOMRIGHT, frameObject.ToolTipFrameText, FRAMEPOINT_BOTTOMRIGHT, 0.005, -0.005)
    BlzFrameSetTooltip(frameObject.Button, frameObject.ToolTipFrame)
end

function CreateTasButtonList(buttonCount, parent, buttonAction, updateAction)
    local object = InitTasButtonListObject(parent, buttonAction, updateAction)
    object.Frames.Count = buttonCount
    for int = 1, buttonCount do
        local frameObject = {}
        frameObject.Index = int
        frameObject.Button = BlzCreateFrame("TasButton", parent, 0, 0)
        CreateTasButtonTooltip(frameObject, parent)

        frameObject.Icon = BlzGetFrameByName("TasButtonIcon", 0)
        frameObject.Text = BlzGetFrameByName("TasButtonText", 0)
        frameObject.IconGold = BlzGetFrameByName("TasButtonIconGold", 0)
        frameObject.TextGold = BlzGetFrameByName("TasButtonTextGold", 0)
        frameObject.IconLumber = BlzGetFrameByName("TasButtonIconLumber", 0)
        frameObject.TextLumber = BlzGetFrameByName("TasButtonTextLumber", 0)
        TasButtonList[frameObject.Button] = frameObject
        TasButtonList[frameObject] = object
        object.Frames[int] = frameObject
        BlzTriggerRegisterFrameEvent(TasButtonList.ButtonTrigger, frameObject.Button, FRAMEEVENT_CONTROL_CLICK)
        BlzTriggerRegisterFrameEvent(TasButtonList.ButtonScrollTrigger, frameObject.Button, FRAMEEVENT_MOUSE_WHEEL)
        if int > 1 then
           BlzFrameSetPoint(frameObject.Button, FRAMEPOINT_TOP, object.Frames[int - 1].Button, FRAMEPOINT_BOTTOM, 0, -0)
        else
            BlzFrameSetPoint(frameObject.Button, FRAMEPOINT_TOPRIGHT, object.InputFrame, FRAMEPOINT_BOTTOMRIGHT, 0, 0)
        end
    end
    InitTasButtonListSlider(object, 1, buttonCount)

    return object
end
 
Last edited:
Updated to V8.
Now supports performing an action when a Button in the List is Rightclicked. Button in the List also play a different Sound when they are rightclicked.
Made the included system demo Creators into wrappers for a more complex but more powerful creator.
Seperated the TocLoading into an own function. The Lua version will, if one has my FrameLoader, reload the TOC when the game is loaded. When you use vjass remember that you have to reload the TOC before you try to recreate Frames after Save&Load.
Added a error message in the Lua version which will tell you when no Button could be created.
 
Level 2
Joined
Oct 3, 2020
Messages
10
This system is really amazing.
Tasyen, can you make this system combination-only?

Like the picture..
 

Attachments

  • asd.png
    asd.png
    1.8 MB · Views: 80
This system itself is no fusion/Item Shop. it is a generic Buttonlist system. Which can be the base for a item fusion UI.

One can. But first you have to choose you want to have a system that does the fusion for you or you do it more simpler without a system doing the fusions.

You have to create a fusion system knowing all the possible fusions. Then on button click, you put all the items the player can use in an array. Now you check if the array contains the materials needed for the fusion, if provided remove the Fusion-material and create the Fusion Item. Otherwise print(missing items).

Or If you want it simpler you could create a powerup fake version of your fusions which you add as options to the buttonList. They don't costing anything. You add some simple create item onClick action. Then you have triggers with pickup events and the conditions equal to one of the the fusion powerup options. Now follows a fusion trigger like one knows from simple GUI fusions. If your hero has item of type x and item of type y remove x and y get fusion.


One could allow to replace material with Gold, this is more complex because then you also need to consider that a material also can be a fusion which is not provided but a material of it could be provided.

I am creating an item shop, it isn't done yet it does this replaces not provided mats with gold. A preview image
Pic 19.jpg
 
Level 2
Joined
Oct 3, 2020
Messages
10
Thank you so much for your kind and detailed answer. Your work is always amazing. So I like the passing of time these days. Every time I connect to Hive, I get excited.
You are probably an important person among Warcraft mapmakers.
 
Updated to V9)
New fdf.
^^ Buttons inside the list change color while they are Pushed. (hard to see for undead players)
Seperated & Changed the rightclick detection from the system.
Added optional AsyncButtonAction and AsyncRightClickAction. They are called directly inside the Click-Events for the clicking Player only.
Each TasButtonList can now have different Data for each Player. Added Ex versions of TasButtonListClearData, TasButtonListRemoveData, TasButtonListAddData, TasButtonListCopyData which only affect the mentioned player. Using the old functions affects all players.
^^ (Lua) you can use the old functions and just add a player arg.
^^ Added a new Demo to demonstrate the new Feature. Based on the player Race one gets different units in the List.
 
Updated to V10)
Added Col and Row gaps in the Creator function. in jass the old creator function was transformed into a wrapper for the new, using 2 globals for the gap settings.
Replaced the contained fdf. Because the used Border Textures (by the buttons) were broken in Reforged maybe also in 1.31.
 
Lua version was Updated to V11)
Made the Rightclick Lib optional.
Added simpler button List versions which only display a Text or Text in a box,
Lua:
Wrapper Creator for Lists having only Text in a Box
Default update & search: exepect data to be either a number (object Editor rawCode), a string or a table(title, text, icon)
function CreateTasButtonBoxedTextList(rowCount, colCount, parent, buttonAction[, updateAction, searchAction, filterAction])
function CreateTasButtonBoxedTextListBig(rowCount, colCount, parent, buttonAction[, updateAction, searchAction, filterAction])

Wrapper Creator for Lists having only Text 
Default update & search: exepect data to be either a number (object Editor rawCode), a string or a table(title, text, icon)
function CreateTasButtonTextList(rowCount, colCount, parent, buttonAction[, updateAction, searchAction, filterAction])
function CreateTasButtonTextListBig(rowCount, colCount, parent, buttonAction[, updateAction, searchAction, filterAction])
208284-3fa93db314dedac5ede4b356e68a0fd0_tn2.jpg

208287-13f1418597692a5eec2cfbe1c73dfd54_tn2.jpg


Added an description & features of the System at the top of the code comment
Added TasButtonListAddDataBatch & TasButtonListAddDataBatchEx to add a moderate amount of data to a List. This has limits my all units demo had to much entries for it. But it works for 100.
 
Last edited:
Very nice system ( : I found a bug though. If you add data to a tasbuttonlist while a user has a value in the search box it shows glitchy buttons even with a call to UpdateTasButtonList()
 

Attachments

  • Warcraft III Reforged 2021.12.22 - 11.54.36.07.mp4
    17.6 MB
  • TasButtonListV10a jass.w3x
    70.8 KB · Views: 10
Very nice system ( : I found a bug though. If you add data to a tasbuttonlist while a user has a value in the search box it shows glitchy buttons even with a call to UpdateTasButtonList()
Thanks for your report, I was able to reproduce the bug I'll look further into it and fix it, but not right now, probably after Christmas.
(this bug does not happen in the Lua version)

Edit: You could cover the bug from your side by using call TasButtonListSearch(udg_tasindex, null) instead of TasButtonListUpdate. Search takes more performance then update and update is contained in search.
 
Last edited:
Top