• 🏆 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!

[Lua] [Lua] ToggleIconButton

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.

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
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)
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.​


ToggleIconButtonGroup is an addon for ToggleIconButton. It combines multiple ones into a shared entity. The values for Buttons used in a ToggleIconButtonGroup should be unique power2 values.
Lua:
--[[
    ToggleIconButtonGroup 1.5 by Tasyen
uses ToggleIconButton by Tasyen

ToggleIconButtonGroup is a addon for ToggleIconButton. It combines multiple ones into a shared entity, the values for Buttons used in ToggleIconButtonGroup should be unique power2.

function CreateToggleIconButtonGroup(multiSelection, action, ...)
    create a Group with action happening when any button is toggled considering the sharedRules set by the individual buttons.
    multiSelection(false) unselect others before setting x, can also be changed later with groupObject.MultiSelection[player] = true/false.
    A ToggleIconButton inside a ToggleIconButtonGroup can not have a custom Action, because it is used by the ToggleIconButtonGroup, but the Group also supports an Action.
    afer action you can give any amount of ToggleIconButtonTables
    The actionfunction provides this args function(groupObject, buttonObject, player, groupValue)

function ToggleIconButtonGroupGetValue(groupObject, player)
function ToggleIconButtonGroupAddButton(groupObject, buttonObject)
function ToggleIconButtonGroupSetModeButton(groupObject[, buttonObject])
    define a ToggleIconButton as multiSelection mode toggle, this uses the Buttons ToggleIconButton Action.
function ToggleIconButtonGroupModeButton(groupObject, parent)
    Creates a default ModeButton for groupObject
    Returns the buttonObject of the new ModeButton
    The new Button still has to be placed
function ToggleIconButtonGroupClearButton(groupObject[, parent, iconPath])
    When the group does not have a Clear Button, then this creates a BUTTON that clears selection when clicked.
    Returns the clear Button-Frame of the group.
    The Button has to be placed after it is created.
function ToggleIconButtonGroupClear(groupObject, player)

--]]

ToggleIconButtonGroup = {}
ToggleIconButtonGroup.Action = function(object, player, enabled)
    local groupObject = object.Group
    if not groupObject.MultiSelection[player] then
        
        local oldValue = ToggleIconButtonGroupGetValue(groupObject, player)
        ToggleIconButtonGroupClear(groupObject, player)
        if enabled or oldValue ~= 0 then
            ToggleIconButtonSetValue(object, player, true)
        end
    end
    groupObject.Action(groupObject, object, player, ToggleIconButtonGroupGetValue(groupObject, player))
end


ToggleIconButtonGroup.ActionMode = function(object, player, enabled)
    object.Group.MultiSelection[player] = enabled
end

function ToggleIconButtonGroupClear(groupObject, player)
    for index, value in ipairs(groupObject) do
        ToggleIconButtonSetValue(value, player, false)
    end
end

function ToggleIconButtonGroupGetValue(groupObject, player)
    local returnValue = 0
    for index, value in ipairs(groupObject) do
        returnValue = returnValue + ToggleIconButtonGetValue(value, player)
    end
    return returnValue
end

function ToggleIconButtonGroupAddButton(groupObject, buttonObject)
    table.insert(groupObject, buttonObject)
    buttonObject.Action = ToggleIconButtonGroup.Action
    buttonObject.Group = groupObject
    BlzTriggerRegisterFrameEvent(ToggleIconButtonGroup.RightClick, buttonObject.Button, FRAMEEVENT_MOUSE_UP)
end

function ToggleIconButtonGroupSetModeButton(groupObject, buttonObject)
    groupObject.ModeButton = buttonObject
    buttonObject.Action = ToggleIconButtonGroup.ActionMode
    buttonObject.Group = groupObject
    return buttonObject    
end

function ToggleIconButtonGroupModeButton(groupObject, parent)
    if groupObject.ModeButton then return groupObject.ModeButton end
    if not parent then parent = BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI, 0) end
    local buttonObject = ToggleIconButtonGroupSetModeButton(groupObject, CreateToggleIconButton(parent, 1, "MultiSelection", "ui\\widgets\\battlenet\\bnet-mainmenu-customteam-up", nil, "UI\\Widgets\\Glues\\GlueScreen-ROC-EditionButton-up", "SingleSelection"))
    -- start with multiselection mode enabled?
    for index = 0, bj_MAX_PLAYERS - 1 do ToggleIconButtonSetValue(groupObject.ModeButton, Player(index), groupObject.MultiSelection[Player(index)]) end
    return buttonObject
end


function ToggleIconButtonGroupClearButton(groupObject, parent, iconPath)
    -- only one clearButton
    if not groupObject.ClearButton then
        -- default optional values
        if not parent then parent = BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI, 0) end
        if not iconPath then iconPath = "ReplaceableTextures\\CommandButtons\\BTNCancel" end

        local button = BlzCreateFrameByType("BUTTON", "ToggleIconButtonGroupClearButton", parent, "", 0)
        local buttonIcon = BlzCreateFrameByType("BACKDROP", "ToggleIconButtonGroupClearButtonIcon", button, "", 0)
        BlzFrameSetSize(button, ToggleIconButton.DefaultSizeX, ToggleIconButton.DefaultSizeY)
        BlzFrameSetAllPoints(buttonIcon, button)
        BlzFrameSetTexture(buttonIcon, iconPath, 0, false)
        ToggleIconButton.CreateTooltip(button, "Clear")        
        ToggleIconButtonGroup[button] = groupObject
        groupObject.ClearButton = button
        groupObject.ClearButtonIcon = buttonIcon
        if not ToggleIconButtonGroup.ClearTrigger then
            ToggleIconButtonGroup.ClearTrigger = CreateTrigger()
            ToggleIconButtonGroup.ClearTriggerAction = TriggerAddAction(ToggleIconButtonGroup.ClearTrigger, function()
                local button = BlzGetTriggerFrame()
                local groupObject = ToggleIconButtonGroup[button]
                local player = GetTriggerPlayer()
                StartSoundForPlayerBJ(player, ToggleIconButton.Sound)
                ToggleIconButtonGroupClear(groupObject, player)
                -- remove focus
                BlzFrameSetEnable(button, false)
                BlzFrameSetEnable(button, true)
                groupObject.Action(groupObject, nil, player, ToggleIconButtonGroupGetValue(groupObject, player))
            end)
        end
        BlzTriggerRegisterFrameEvent(ToggleIconButtonGroup.ClearTrigger, button, FRAMEEVENT_CONTROL_CLICK)
        return button
    else
        return groupObject.ClearButton
    end
end

function CreateToggleIconButtonGroup(multiSelection, action, ...)
    if not ToggleIconButtonGroup.RightClick then
        ToggleIconButtonGroup.RightClick = CreateTrigger()
        TriggerAddAction(ToggleIconButtonGroup.RightClick, function()
            local player = GetTriggerPlayer()
            if IsRightClick(player) then
                local frame = BlzGetTriggerFrame()
                local buttonObject = ToggleIconButton[frame]
                local groupObject = buttonObject.Group
                StartSoundForPlayerBJ(player, ToggleIconButton.Sound)
                ToggleIconButtonGroupClear(groupObject, player)
                ToggleIconButtonSetValue(buttonObject, player, true)
                groupObject.Action(groupObject, buttonObject, player, ToggleIconButtonGroupGetValue(groupObject, player))
            end
        end)
    end
    local object = {MultiSelection = __jarray(multiSelection), Action = action}
    for index, value in ipairs({...})  do ToggleIconButtonGroupAddButton(object, value) end
    return object
end

Changelog:
V1.5 The ClearButton used a hardcoded Texture and ignored the arg.
V1.4 ToggleIconButtonGroup makes ToggleIconButtons rightclickable will deselect current and select the rightclicked.
V1.3
Moved the Trigger Creation out of the Lua Root, into the creator functions.
The Clear Button will execute the GroupAction without an object.
V1.2
One could not deselect the selected category in singleselection mode
Added default Creators for Clear & ModeButton
MultiSelection Mode is now synced knowlage (array)
V1.1 adjusted ToggleIconButtonGroupGetValue to fit ToggleIconButton1.1
 

Attachments

  • ToggleIconButton.w3x
    22.5 KB · Views: 20
Last edited:
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
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
Could you please add a demo map for this resource (as well as any others which don't yet have one) which includes the thing all set up? I understand that your 1.1 script should be "plug 'n play", but the resource is hard to visualize just by looking at the numbers/description. I am really impressed by what you are able to achieve given these very verbose frame-manipulation natives.
 
Done, added A demo map to the first post with working examples one with and one without ToggleIconButtonGroup.
The demo map uses the version which requires Templates.toc

I am really impressed by what you are able to achieve given these very verbose frame-manipulation natives.
I trained quite a while, before i was able to create such.
 
Top