1. Head to the 33rd Modeling Contest Poll and drink to your heart's desire.
    Dismiss Notice
  2. Choose your means of doom in the 17th Mini Mapping Contest Poll.
    Dismiss Notice
  3. A slave to two rhythms, the 22nd Terraining Contest is here.
    Dismiss Notice
  4. The heavens smile on the old faithful. The 16th Techtree Contest has begun.
    Dismiss Notice
  5. The die is cast - the 6th Melee Mapping Contest results have been announced. Onward to the Hive Cup!
    Dismiss Notice
  6. The glory of the 20th Icon Contest is yours for the taking!
    Dismiss Notice
  7. Check out the Staff job openings thread.
    Dismiss Notice
Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

[Lua] ToggleIconButton

Discussion in 'Submissions' started by Tasyen, Jun 23, 2020.

  1. Tasyen

    Tasyen

    Joined:
    Jul 18, 2010
    Messages:
    1,660
    Resources:
    37
    Tools:
    2
    Maps:
    3
    Spells:
    11
    Tutorials:
    20
    JASS:
    1
    Resources:
    37
    ToggleIconButton is a custom Frame, it is a Button that changes it's value between 0 and the given value (both represented with a visual Texture) when clicked (kinda like a checkbox). But unlike a checkbox the Value can be read and set.

    V1.1 Does not need any fdf/toc.
    V1.1a

    Code (Lua):
    --[[
        ToggleIconButton 1.1a by Tasyen

    function CreateToggleIconButton(parent, valueOn, text, textureOn[, mode, textureOff, textOff])
     create an IconButton that swaps between 2 states. (0 and valueOn, visualy shown by textureOff/textureOn)
     the IconButton starts with 0 & textureOff
     textureOff is automatically calced when it is nil
     mode should be 0, 1 or - 1 when not set it is 0. see ToggleIconButton.MODE_DEFAULT
     You can add an Action function with object.Action = function(object, player, enabled)
     If you use Textures with Transparency one has to object.Blend = true, otherwise it is displayed wrongly
     when the mode is -1 then the function will only run for the clicking Player. object is the active ToggleIconButtonTable and player the clicking player.
     returns the table, returnValue.Button is the ButtonFrame

    function ToggleIconButtonSetValue(object, player[, enable])
     can be used to set the current value to x (true, false) or to toggle the current value(nil)
     will not call the object's Action.

    function ToggleIconButtonGetValue(object, player)
    --]]


    ToggleIconButton = {
        DefaultSizeX = 0.024,
        DefaultSizeY = 0.024,
    }
    ToggleIconButton.MODE_DEFAULT = 0 -- the visual is local only.
    ToggleIconButton.MODE_SHARED = 1 -- is the same for all players.
    ToggleIconButton.MODE_LOCAL = -1 -- Visual and Action are for the clicking player only

    function ToggleIconButton.GetKey(object, player)
        if object.Mode == ToggleIconButton.MODE_SHARED then
            return 0
        else
            return player
        end
    end

    function ToggleIconButtonSetValue(object, player, enable)
        local key = ToggleIconButton.GetKey(object, player)
        -- wana toggle?
        if enable == nil then
            --currently off?
            object.Value[key] = not object.Value[key]
        -- specific state
        else
            object.Value[key] = enable
        end

        -- update visual
        if object.Mode == ToggleIconButton.MODE_SHARED or GetLocalPlayer() == player then
            if not object.Value[key] then
                BlzFrameSetTexture(object.Icon, object.TextureOff, 0, object.Blend)
                BlzFrameSetText(object.ToolTip, object.TextOff)
            else
                BlzFrameSetTexture(object.Icon, object.Texture, 0, object.Blend)
                BlzFrameSetText(object.ToolTip, object.Text)
            end
        end
    end

    function ToggleIconButtonGetValue(object, player)
        if object.Value[ToggleIconButton.GetKey(object, player)] then
            return object.ValueOn
        else
            return 0
        end
    end

    function getDisabledIcon(icon)
        --ReplaceableTextures\CommandButtons\BTNHeroPaladin.tga -> ReplaceableTextures\CommandButtonsDisabled\DISBTNHeroPaladin.tga
        return string.gsub(icon , "CommandButtons\\BTN", "CommandButtonsDisabled\\DISBTN")
    end

    function ToggleIconButton.CreateTooltip(frame, text)
        --local toolTip = BlzCreateFrame("EscMenuMainPanelDialogTextTemplate", frame, 0, 0)
        local toolTip = BlzCreateFrameByType("TEXT", "TasCategoryButtonTooltip", frame, "", 0)
        BlzFrameSetEnable(toolTip, false)
        BlzFrameSetPoint(toolTip, FRAMEPOINT_BOTTOM, frame, FRAMEPOINT_TOP, 0, 0)
        BlzFrameSetText(toolTip, text)
        BlzFrameSetScale(toolTip, 1.2)
        BlzFrameSetTooltip(frame, toolTip)
        return toolTip
    end


    function CreateToggleIconButton(parent, valueOn, text, textureOn, mode, textureOff, textOff)
        if not textureOff then textureOff = getDisabledIcon(textureOn) end
        if not textOff then textOff = text end
        if not mode then mode = ToggleIconButton.MODE_DEFAULT end

        if not ToggleIconButton.Trigger then
            ToggleIconButton.Sound = CreateSound("Sound\\Interface\\MouseClick1.wav", false, false, false, 10, 10, "")
            SetSoundParamsFromLabel(ToggleIconButton.Sound, "InterfaceClick")
            SetSoundDuration(ToggleIconButton.Sound, 239)

            ToggleIconButton.Trigger = CreateTrigger()
            ToggleIconButton.TriggerAction = TriggerAddAction(ToggleIconButton.Trigger, function()
                xpcall(function()
                local frame = BlzGetTriggerFrame()
                local object = ToggleIconButton[frame]
                local player = GetTriggerPlayer()
                StartSoundForPlayerBJ(player, ToggleIconButton.Sound)
     
                --ToggleIconButtonSetValue(object, GetTriggerPlayer(), object.Value ~= object.ValueOn)
                ToggleIconButtonSetValue(object, player)
                if object.Action and (object.Mode ~= ToggleIconButton.MODE_LOCAL or GetLocalPlayer() == player) then object.Action(object, player, ToggleIconButtonGetValue(object, player) == object.ValueOn) end
                -- remove focus
                BlzFrameSetEnable(frame, false)
                BlzFrameSetEnable(frame, true)
                end, print)
            end)
        end

        local frame = BlzCreateFrameByType("BUTTON", "TasCategoryButton", parent, "ScoreScreenTabButtonTemplate", 0)
        local backdrop = BlzCreateFrameByType("BACKDROP", "TasCategoryButtonIcon", frame, "", 0)
        BlzFrameSetAllPoints(backdrop, frame)
        BlzFrameSetSize(frame, ToggleIconButton.DefaultSizeX, ToggleIconButton.DefaultSizeY)
        BlzFrameSetTexture(backdrop, textureOff, 0, false)
        BlzTriggerRegisterFrameEvent(ToggleIconButton.Trigger, frame, FRAMEEVENT_CONTROL_CLICK)

        local object = {
            Button = frame,
            Icon = backdrop,
            Mode = mode,
            Value = __jarray(false),
            ValueOn = valueOn,
            Texture = textureOn,
            TextureOff = textureOff,
            Text = text,
            TextOff = textOff,
            ToolTip = ToggleIconButton.CreateTooltip(frame, textOff)
        }

        ToggleIconButton[frame] = object
     
        return object
    end
    Demo
    Code (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 parent = BlzCreateFrameByType("FRAME", "", BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI, 0), "",0)
        local buttonA = CreateToggleIconButton(parent, 1, "A", "ReplaceableTextures\\CommandButtons\\BTNKnight")
        local buttonB = CreateToggleIconButton(parent, 2, "B", "ReplaceableTextures\\CommandButtons\\BTNHeroPaladin", 1)
        local buttonC = CreateToggleIconButton(parent, 4, "C", "ReplaceableTextures\\CommandButtons\\BTNPriest")
        --local buttonC = CreateToggleIconButton(parent, 4, "C", "ReplaceableTextures\\WorldEditUI\\Editor-MultipleUnits")
        --buttonC.Blend = true
        BlzFrameSetAbsPoint(buttonA.Button, FRAMEPOINT_CENTER, 0.4, 0.36)
        BlzFrameSetAbsPoint(buttonB.Button, FRAMEPOINT_CENTER, 0.4, 0.33)
        BlzFrameSetAbsPoint(buttonC.Button, FRAMEPOINT_CENTER, 0.4, 0.3)
        TimerStart(CreateTimer(), 1, true, function()
            ClearTextMessages()
            print("buttonA.Value", ToggleIconButtonGetValue(buttonA, Player(0)), ToggleIconButtonGetValue(buttonA, Player(1)))
            print("buttonB.Value", ToggleIconButtonGetValue(buttonB, Player(0)), ToggleIconButtonGetValue(buttonB, Player(1)))
            print("buttonC.Value", ToggleIconButtonGetValue(buttonC, Player(0)), ToggleIconButtonGetValue(buttonC, Player(1)))
        end)
        print("done")
        DestroyTimer(GetExpiredTimer())
    end, print)
    end)

    Code (Lua):
    --[[
        ToggleIconButton 1.3 by Tasyen

    function CreateToggleIconButton(parent, valueOn, text, textureOn[, mode, textureOff, textOff])
     create an IconButton that swaps between 2 states. (0 and valueOn, visualy shown by textureOff/textureOn)
     the IconButton starts with 0 & textureOff
     textureOff is automatically calced when it is nil
     mode should be 0, 1 or - 1 when not set it is 0. see ToggleIconButton.MODE_DEFAULT
     You can add an Action function with object.Action = function(object, player, enabled)
     If you use Textures with Transparency one has to object.Blend = true, otherwise it is displayed wrongly
     when the mode is -1 then the function will only run for the clicking Player. object is the active ToggleIconButtonTable and player the clicking player.
     returns the table, returnValue.Button is the ButtonFrame

    function ToggleIconButtonSetValue(object, player[, enable])
     can be used to set the current value to x (true, false) or to toggle the current value(nil)
     will not call the object's Action.

    function ToggleIconButtonGetValue(object, player)
    --]]


    ToggleIconButton = {
        DefaultSizeX = 0.024,
        DefaultSizeY = 0.024,
    }
    ToggleIconButton.MODE_DEFAULT = 0 -- the visual is local only.
    ToggleIconButton.MODE_SHARED = 1 -- is the same for all players.
    ToggleIconButton.MODE_LOCAL = -1 -- Visual and Action are for the clicking player only


    function ToggleIconButton.GetKey(object, player)
        if object.Mode == ToggleIconButton.MODE_SHARED then
            return 0
        else
            return player
        end
    end

    function ToggleIconButtonSetValue(object, player, enable)
        local key = ToggleIconButton.GetKey(object, player)
        -- wana toggle?
        if enable == nil then
            --currently off?
            object.Value[key] = not object.Value[key]
        -- specific state
        else
            object.Value[key] = enable
        end

        -- update visual
        if object.Mode == ToggleIconButton.MODE_SHARED or GetLocalPlayer() == player then
            if not object.Value[key] then
                BlzFrameSetTexture(object.Icon, object.TextureOff, 0, object.Blend)
                BlzFrameSetText(object.ToolTip, object.TextOff)
            else
                BlzFrameSetTexture(object.Icon, object.Texture, 0, object.Blend)
                BlzFrameSetText(object.ToolTip, object.Text)
            end
        end
    end

    function ToggleIconButtonGetValue(object, player)
        if object.Value[ToggleIconButton.GetKey(object, player)] then
            return object.ValueOn
        else
            return 0
        end
    end

    function getDisabledIcon(icon)
        --ReplaceableTextures\CommandButtons\BTNHeroPaladin.tga -> ReplaceableTextures\CommandButtonsDisabled\DISBTNHeroPaladin.tga
        return string.gsub(icon , "CommandButtons\\BTN", "CommandButtonsDisabled\\DISBTN")
    end

    function ToggleIconButton.CreateTooltip(frame, text)
        --local toolTip = BlzCreateFrame("EscMenuMainPanelDialogTextTemplate", frame, 0, 0)
        local toolTipBox = BlzCreateFrame("EscMenuControlBackdropTemplate", frame, 0, 0)
        local toolTip = BlzCreateFrameByType("TEXT", "TasCategoryButtonTooltip", toolTipBox, "", 0)
        BlzFrameSetEnable(toolTip, false)
        BlzFrameSetPoint(toolTip, FRAMEPOINT_BOTTOM, frame, FRAMEPOINT_TOP, 0, 0.008)
        BlzFrameSetPoint(toolTipBox, FRAMEPOINT_TOPLEFT, toolTip, FRAMEPOINT_TOPLEFT, -0.008, 0.008)
        BlzFrameSetPoint(toolTipBox, FRAMEPOINT_BOTTOMRIGHT, toolTip, FRAMEPOINT_BOTTOMRIGHT, 0.008, -0.008)
        BlzFrameSetText(toolTip, text)
        BlzFrameSetScale(toolTip, 1.2)
        BlzFrameSetTooltip(frame, toolTipBox)
        return toolTip
    end


    function CreateToggleIconButton(parent, valueOn, text, textureOn, mode, textureOff, textOff)
        if not textureOff then textureOff = getDisabledIcon(textureOn) end
        if not textOff then textOff = text end
        if not mode then mode = ToggleIconButton.MODE_DEFAULT end

        if not ToggleIconButton.Trigger then
            ToggleIconButton.Sound = CreateSound("Sound\\Interface\\MouseClick1.wav", false, false, false, 10, 10, "")
            SetSoundParamsFromLabel(ToggleIconButton.Sound, "InterfaceClick")
            SetSoundDuration(ToggleIconButton.Sound, 239)
            BlzLoadTOCFile("war3mapImported\\Templates.toc")
            ToggleIconButton.Trigger = CreateTrigger()
            ToggleIconButton.TriggerAction = TriggerAddAction(ToggleIconButton.Trigger, function()
                xpcall(function()
                local frame = BlzGetTriggerFrame()
                local object = ToggleIconButton[frame]
                local player = GetTriggerPlayer()
                StartSoundForPlayerBJ(player, ToggleIconButton.Sound)
                --ToggleIconButtonSetValue(object, GetTriggerPlayer(), object.Value ~= object.ValueOn)
                ToggleIconButtonSetValue(object, player)
                if object.Action and (object.Mode ~= ToggleIconButton.MODE_LOCAL or GetLocalPlayer() == player) then object.Action(object, player, ToggleIconButtonGetValue(object, player) == object.ValueOn) end
                -- remove focus
                BlzFrameSetEnable(frame, false)
                BlzFrameSetEnable(frame, true)
                end, print)
            end)
        end
     
        local frame = BlzCreateFrameByType("BUTTON", "TasCategoryButton", parent, "ScoreScreenTabButtonTemplate", 0)
        local backdrop = BlzCreateFrameByType("BACKDROP", "TasCategoryButtonIcon", frame, "", 0)
        BlzFrameSetAllPoints(backdrop, frame)
        BlzFrameSetSize(frame, ToggleIconButton.DefaultSizeX, ToggleIconButton.DefaultSizeY)
        BlzFrameSetTexture(backdrop, textureOff, 0, false)
     
        BlzTriggerRegisterFrameEvent(ToggleIconButton.Trigger, frame, FRAMEEVENT_CONTROL_CLICK)

        local object = {
            Button = frame,
            Icon = backdrop,
            Mode = mode,
            Value = __jarray(false),
            ValueOn = valueOn,
            Texture = textureOn,
            TextureOff = textureOff,
            Text = text,
            TextOff = textOff,
            ToolTip = ToggleIconButton.CreateTooltip(frame, textOff)
        }

        ToggleIconButton[frame] = object
     
        return object
    end
    ChangeLog:
    1.3)
    Moved the Trigger Creation out of the Lua Root, into the creator functions.
    Plays now the soft MouseClickSound when clicked.​
    1.2)
    Requires "war3mapImported\\Templates.toc". The tooltip is now in a Box.​
    1.1a)
    Moved the Trigger Creation out of the Lua Root, into the creator functions.
    Plays now the soft MouseClickSound when clicked.
    1.1a) was created as 1.3) was uploaded it does not require a toc-File but does not have the boxed Tooltip.​
    1.1)
    The value is now a boolean array, all players know the state for all players.
    Added function ToggleIconButtonGetValue(object, player) returning the current value as integer
    Replaced SharedByAll with Mode: 0 (Synced but Visual local), 1 (used together) or -1 (Visual and Action are for the clicking player only)
    Pushed the Mode arg from CreateToggleIconButton into the optionals, when not set 0 is used.​
     
    Last edited: Oct 9, 2020
  2. Tasyen

    Tasyen

    Joined:
    Jul 18, 2010
    Messages:
    1,660
    Resources:
    37
    Tools:
    2
    Maps:
    3
    Spells:
    11
    Tutorials:
    20
    JASS:
    1
    Resources:
    37
    Updated to V1.1 droped the async value approach. Now the selected value is known to all players but one has to use the new Getter function ToggleIconButtonGetValue to get the current value.
    Edit: [Lua] ToggleIconButtonGroup was edited to fit the change aswell, but I didn't add a post.
     
  3. Tasyen

    Tasyen

    Joined:
    Jul 18, 2010
    Messages:
    1,660
    Resources:
    37
    Tools:
    2
    Maps:
    3
    Spells:
    11
    Tutorials:
    20
    JASS:
    1
    Resources:
    37
    Updated to V1.3 Pushed the Trigger creators into the creation, it will run on the first time it is called, to be safer. It is quite dangerous to create warcraft 3 objects inside the Lua root.
    Added a click sound.
    V1.2 never published. Added a Box around the Tooltip which requires the map already having loaded: "UI\FrameDef\UI\EscMenuTemplates.fdf". Which would do that templates.toc