Creating standard left-sliding frames
The container (bag) frames have their own custom code which uses SetPoint to adjust them relative to each other, but frames like the CharacterInfo and Merchant use common functions from UIParent.lua. To make use of this common code, you need to define UI Panel layout attributes for your frame after you create it. You can do it directly in your XML, or as Lua in the Scripts/OnLoad section of your XML, or right after you call CreateFrame in Lua.
Pure XML method:
<Frame name="YourFrame" toplevel="true" movable="true" enableMouse="true" hidden="true" parent="UIParent"> <Attributes> <Attribute name="UIPanelLayout-defined" type="boolean" value="true"/> <Attribute name="UIPanelLayout-enabled" type="boolean" value="true"/> <Attribute name="UIPanelLayout-area" type="string" value="left"/> <Attribute name="UIPanelLayout-pushable" type="number" value="5"/> <Attribute name="UIPanelLayout-whileDead" type="boolean" value="true"/> </Attributes>
Pure Lua method:
MyFrameName:SetAttribute("UIPanelLayout-defined", true) MyFrameName:SetAttribute("UIPanelLayout-enabled", true) MyFrameName:SetAttribute("UIPanelLayout-area", "left") MyFrameName:SetAttribute("UIPanelLayout-pushable", 5) --MyFrameName:SetAttribute("UIPanelLayout-width", width) -- Custom width is not recommended. MyFrameName:SetAttribute("UIPanelLayout-whileDead", true)
WoW's "UI Panels" system considers the screen to be divided into three areas: Left, Center and Right. That's enough space for three "single-width" frames side by side (or one "double-width" plus one "single-width" frame), but no more than that. Any attempt to open a 4th frame would cause another frame to close (almost always starting with the leftmost frame first (the rules are explained in-depth further below); so whichever frame is in the "left" panel slot at the moment will usually be closed first). Furthermore, the game automatically sorts the panels using a complex system of "area" values and "pushable"-priorities, which is all explained in detail below.
"UIPanelLayout-area" tells the game which area you want your frame to reside in. The possibilities are:
- "left" (most frames use this),
- "full" (fullscreen things like the world map),
- "doublewide" (auction and guild bank uses this),
- or "center" (things like the main menu uses this).
- Your custom addons should most likely only be using "left", since the others are for very specialized purposes.
- If you use the "full" area (ie. what the world map uses), the game will forcibly close all other UIPanels and all bags and then display your panel in the middle of the screen, since there can only be one "full" panel on-screen at a time; and the API will then refuse to open any other non-"full" panels while a fullscreen panel remains open.
- If you use the "doublewide" area (ie. what the auction house uses), your panel will take up the "left" and "center" spots (which only leaves one more space on the "right" for another frame to fit), and will automatically keep a single existing frame (either the "left" (but only if the leftmost frame's "pushable" value is over 0), or "center" (if one exists instead of a "left" frame or if the leftmost frame had "pushable == 0"), or lastly "right" (if all else failed and a frame only exists in the rightmost slot for some weird reason)) and will then simply move that preserved frame to the "right" instead.
- If you use "left" (which is what most Blizzard frames use) it will take up the "left" spot.
- If you use "center" (ie. what the game menu uses) it will forcibly close all other "left/center/right" panels (but not any "fullscreen" panel) and all bags and then take up the "center" spot.
- Note that there is no explicit "right" panel area, so don't attempt to use that as a value manually!
"UIPanelLayout-pushable" specifies the sorting and pushability of your panel, within your chosen "area". The "pushability" aspect mostly applies to the "left" (and in a roundabout way "doublewide") choices, and it works as follows:
- The value must be "0" or higher ("1", "2", ... etc). There is no limit to how high the value can be, since Blizzard simply uses it in numerical comparisons to check which frame has the highest number. Negative values (such as "-1"), however, are not legal and would be handled improperly by Blizzard's code and would lead to unexpected results, since they expect all numbers to be 0 or higher.
- A "pushable = 0" value means "NOT pushable", and the behavior depends on how many frames are on-screen. If there are 1-2 frames on-screen, and you attempt to open a frame with "pushable = 0", then any existing leftmost (their code only checks the current "left" slot) panel with "pushable = 0" will automatically close and then your new panel will appear in whatever slot (most likely "left") you told it to use, which means that there can only be a single "pushable = 0" frame on-screen in the leftmost area at once, since it automatically gets replaced whenever any other "pushable = 0" frame is opened in any "area". You can think of it as "pushable = 0" frames being mutually exclusive with each other. However, if there are already 3 frames on-screen when you attempt to open a 4th frame (meaning that something must definitely close to fit the new frame), then the behavior is a bit different and the leftmost frame is always killed regardless of its own "pushable" value, and then the remaining two frames plus the new frame are re-arranged/sorted based on their "pushable" priorities (see below for more about that).
- So that's how "pushable = 0" works. But what about higher values? Well, a value of "1" or higher means that the frame is dynamically pushable, which tells the game that if there's already a frame in the desired area (such as "left"), then the new frame you're trying to open will instead be PUSHED one step to the right (ie. a "left" frame will open at "center" instead, or a "center" frame will open at "right" instead). However, it's not entirely that simple. The "pushable" value also specifies how to SORT (order) the frames when pushing is necessary. The lower the "pushable" value is, the more "leftwards" the frame will be placed. So if you have an "area: left, pushable: 3" frame visible on the screen in the leftmost slot, and you open an "area: left, pushable: 1" (or "pushable: 0", or "pushable: 2") frame, then the new frame will open to the LEFT of the existing frame, since your new frame's "pushable" value (1, or 0, or 2) is lower than the other/existing frame's (3). Basically, the number determines which frame (either the new frame or any/all of the existing ones) will get pushed to the right to make room for the new frame; so the one(s) with the highest "pushable" values are the ones that will get pushed (or opened) to the right. As for the situation when 3 frames are open and you try to open a 4th one, it's handled in exactly the same way as described above in the "pushable = 0" section: The leftmost frame is always killed (regardless of its "pushable" value), and then the remaining frames are sorted based on their "pushable" values from lowest-to-highest (left to right).
- If two frames have identical non-zero "pushable" values (such as both having "area: left, pushable: 2"), then the newest/latest frame will open to the right of the existing one.
- Also note that "area: doublewide" frames are very easily accidentally closed, because they're programmed to always close themselves whenever a "pushable: 0" frame (in any area) is opened. That explains why Blizzard's auction GUI (which uses "area: doublewide") so easily closes itself when you open almost any other frame (since most Blizzard frames are using "area: left, pushable: 0").
- Most official game windows have a "pushable" order between 0-2 (the highest they use is 7), and you should think very closely about which order you want your custom window to have. Zero ("0") is never recommended for your custom windows, since that's the order of most of the Blizzard UI panels and will cause lots of conflicts (causing the Blizzard panels and your own window to easily auto-close, since all "0" panels are mutually exclusive, as mentioned earlier, and will replace each other on-screen if a new one is opened). One ("1") is acceptable but not really recommended either, since that's the order of the player trade window which you never, ever want to conflict with; but if you really decide to use the same pushable priority ("1"), then you'll give the user non-intuitive stacking orders (they'll see either your window or the trade window as their leftmost window, depending on which was opened first, rather than getting consistent results every time). Any number from 2 and up will be pretty safe. But if you want to be really safe, you should probably use 8 or up (numbers which aren't used by Blizzard). You can see most (but not all) of Blizzard's frames and their UIPanel settings if you view the contents of the global "UIPanelWindows" Lua table in-game; or if you want the full list then you must extract the game's source code and search for "UIPanelWindows" in all Blizzard addons and FrameXML code.
"UIPanelLayout-width" controls the effective width of the UI panel (for stacking purposes), used as an override for frame:GetWidth(). Not recommended, unless you have a very specific reason to want a different width than the frame's "true width". You should simply omit this value/attribute completely, in most cases.
"UIPanelLayout-whileDead" specifies that the frame can be opened while the player is dead. Omit this field/attribute (or set it to nil/false if setting it via Lua, or false if setting it via XML) to prevent the player from accessing the frame while dead. Or set it to boolean true (as seen in the examples above) if you want the frame to be openable while dead.
Lastly, you'll need to always use ShowUIPanel(frame) and HideUIPanel(frame) instead of frame:Show() / Hide(), to tell blizzard's UI Panel code to handle your frame and its stacking, as desired. Have fun!