Search code examples
luaworld-of-warcraft

WoW - Setting (and resetting) frame anchors


I have a very simple add-on that displays four pieces of information textually instead of with a visual representation—each on their own frame. Each one can be turned on an off at the discretion of the user.

I would like these frames to all be anchored to each other horizontally. Easy enough right? Anchor left edge of Frame2 to the right edge of Frame 1, Frame3 to Frame2 in the same manner, etc. If Frame2 is disabled then Frame3 needs to be anchored to Frame1.

I tried to run Frame:GetChildren() on an anchor frame to count the children and anchor them to the anchor frame itself instead of to each other, but Frame:GetChildren() returns a table of tables, and the # operator doesn't count tables.

As a bonus, I would like the user to be able to change the order of the frames.

The question of how to do this has consumed me all day today. Perhaps it's the lack of sleep, or perhaps the lack of Lua experience. Either way, any help would be much appreciated.


Solution

  • Part One: Organizing the Frames

    Assign undesired frames a near-zero width and all the others will shuffle over. (Don't set width to exactly zero, or else anything anchored from it will also hide.)

    function displayFrames(showFrame1, showFrame2, showFrame3, showFrame4)
        Frame1:SetWidth((showFrame1 and 300) or 0.0001)
        Frame1:SetShown(showFrame1)
        Frame2:SetWidth((showFrame2 and 300) or 0.0001)
        Frame2:SetShown(showFrame2)
        -- etc.
    end
    

    For the bonus, re-ordering the frames, anchor all frames relative to a common parent (vice each other) and calculate the x offset manually:

    Frame1:SetPoint("LEFT", UIParent, "LEFT", 0, 0)
    Frame2:SetPoint("LEFT", UIParent, "LEFT", 300, 0)
    Frame3:SetPoint("LEFT", UIParent, "LEFT", 900, 0)  -- will be right of Frame4
    Frame4:SetPoint("LEFT", UIParent, "LEFT", 600, 0)
    

    Part Two: GetChildren() return values

    GetChildren() returns multiple values, each of which is a table representing a single child (ie, frame). If there are four children then you could do something like:

    local child1, child2, child3, child4 = Frame:GetChildren()
    

    If you don't know in advance how many children there are, consider wrapping all the values into a table so you can iterate through it

    local children = { Frame:GetChildren() }
    
    for __, child in ipairs(children) do
       --do something to each child
    end
    

    Since your goal was to actually anchor each frame to the previous one, except anchoring the first one to some place else, you will want to use a different type of loop:

    local children = { Frame:GetChildren() }
    
    -- anchor the first frame if it exists
    if (children[1]) then
        children[1]:SetPoint("CENTER", UIParent)
    end
    
    -- anchor any remaining frames
    for i=2, #children do
        children[i]:SetPoint("LEFT", children[i-1], "RIGHT")
    end