Guide to Dynamic Frames

Introduction
This is a tutorial on creating and using frames without the use of XML. In other words, all frames and their sub-elements are created completely in Lua script, contrary to what seems to be a popular belief.

This guide will assume some knowledge of both frames and Lua programming and therefore should be considered for intermediate to advanced add-on authors. However, if the reader has completed and understood the Guide to XML Frames, and has a working knowledge of Lua scripting for Runes of Magic, this tutorial should not present too much trouble. The example add-on we will create in this guide will be similar (but not identical to) the example add-on for the Guide to XML Frames so that comparisons can be made between the two.

Given this, the guide will not be spending much time on specific features of frames or Lua script as they are essentially the same whether the frames are created via XML or Lua. Instead, the guide will cover the methods needed to achieve the same results in Lua script as can be done via XML, and some of the differences, advantages and pitfalls that may arise by doing so.

What are Dynamic Frames
A dynamic frame is a frame that is created via Lua script, instead of through an XML file. There are several reasons we would want to do this, including better control over frame creation, ability to control global versus local access to the frames, but above all is simply the ability to create frames on-the-fly. Hence the name dynamic frame, as we can create them dynamically.

As an example of this last point, imagine creating an add-on to display buff bars. Each buff bar has an icon, the bar itself, the name of the buff displayed on the bar, as well as the time remaining on this buff. If we were to create our frames in XML, we would need to create all the frames, textures and FontStrings we will ever need in advance within the XML file, or set a maximum number of buffs that we could track. With dynamic frames, we can create new frames as we need them, and therefore don't waste memory with unneeded frames, nor require a cap to accommodate a lower number of pre-defined frames.

Starting the Example
The add-on we will create is similar to the one given in the Guide to XML Frames, however we will only have the close button and will not bother with saving and restoring the frame's position as this is mostly done in code anyway. This is also the reason we will not be setting the frame's position or size.

To start, create the add-on folder and TOC file. We'll call this add-on  to differentiate it from, yet still have the obvious connection to the previous version. As there is no XML file for this example add-on, we only need to list the Lua file in here, and optionally the comments indicating version etc.

Here's the TOC file contents:

Save this file then create and open. We'll start the code with a header and creating a variable to hold the our functions and variables.

The First Big Difference
Now we come to the first difference between creating frames in Lua and creating them in an XML file. Somehow we need to get a frame into the game engine that we can create to start things off. We don't need to create all the elements we need, but we need to get at least one frame in here otherwise what is going to call the functions we create in here?

The solution to this little dilemma comes from the fact that the game engine will, upon loading a Lua file, compile then run the file in question. Yes, you read that correctly. The Lua virtual machine, the part of the game engine that actually executes the Lua code, does not interpret the source code directly, instead it compiles the source into a binary form called byte code, then executes that result (this is similar to what Java and other interpreted languages do). Therefore, we can create at least one basic frame outside of any function and the game will end up executing our code, and hence create a frame.

Once that is done, we can have the rest of the initialization as part of the  code snippet. We could of course define all our elements in the same manner, but it would not properly show the creation of dynamic frames.

Creating a frame, or any other UI element is done via the  function. This function takes three strings, and an optional fourth string, as parameters. The first is the type of component to be created, and has the same value as the XML tags for the same element type. The second parameter is the name to give this component, and the third is the name of the parent object for this element. The optional forth parameter is the name of a virtual template to use during creation and functions just like the  attribute in the XML tags.

As we wish to create a frame, out type is, we'll call our frame   and we'll use   as the parent frame. So our code will be:

We also need to initialize a few things for our frame. Again we don't need to intialize everything here, but we can at least give it a size, set its position set the  and   code snippets. Here's the code to handle this:

The call to the  may be redundant here. It is still unclear at this time if components created in this way have a default anchor setup or not, but as it certainly doesn't hurt to have the call here, it is safe to always have it here.

The call to the  method works like the XML version, The first parameter is the relative location on this object (equates to the   attribute in XML), the second is the relative point on the object we are aligning to (this is equivalent to the   attribute in XML). The third parameter is the object we are aligning to (XML equivalent is the  attribute), and finally the x and y offsets respectively. The  method also allows us to specify the object we are aligning to directly instead of just the name of the object.

It should be noted that attempting to set the  script snippet from within the   code snippet will not work. It is uncertain at the time of writing if this is because it is blocked completely or if only changing code snippets from within a snippet of the same object is blocked. More testing in this area needs to be done, but it seems to only be blocked for the current object. That is, if inside the code for the  of , we can't set  's  , but some other object's   seems to work (at least on creation of other objects).

Setting The Handlers
So we'll now create the two handler functions themselves. First is the  function. The code will be almost identical to the previous version, except we will need to verify if the variables we need to access are available because it is possible for the OnUpdate handler to be called before everything is ready. This is because by default, all frames and elements created with  are visible by default, and therefore can trigger calls to the OnUpdate handler.

Our OnLoad handler function however will be extended in order to initialize the rest of our add-on, including the creation of all the sub-elements for our frame. We'll create some helper functions for creating these sub-elements, so as we will be adding a FontString for the text, a texture for decoration, and a close button, our OnLoad handler becomes:

Now we create the helper function, or at least the skeleton of these functions even if they don't do anything yet. Place these before the handler functions, but after the creation of the base frame.

Adding the FontString
Now we can start filling in our helper functions and create each element for our frame. We'll start with the FontString since that will also allow us to fill in the helper function to set the text as well.

Creating the FontString is not really different from creating the frame itself, however we'll need to initialize a few things. Instead of using a font template, we'll initialize the FontString manually to show how it can be done. Change our  function to this:

Here we first create the FontString itself. We are assigning it to the variable  inside our local. This is being done on purpose for later in this tutorial.

We then give our FontString a size. This will be the area the text can occupy, and can be thought of as the window size for the FontString to be displayed in. If we set text that would display wider than this area, the game will automatically cut the string short and add the ellipses (...) at the end.

Next we set the font file to use. Here the parameters are the actual font file, the point size to use, the weight to use for the text, and the outline mode. The valid values for the weight parameter are


 * THIN
 * NORMAL
 * BOLD

The values for the outline parameter, which affects the shadow around the text, are:


 * NONE
 * NORMAL
 * THICK

The  method sets the horizontal justification of the text within the sized area given. Valid values are


 * LEFT
 * CENTER
 * RIGHT

Note how the  method is being used here. Instead of giving the name of the frame to align to, the actual object itself is being passed. Both methods work.

The call to  here is merely putting in place holder text and is somewhat redundant.

The last line in this function is rather important. This is where we set which layer of our frame this FontString should appear at. The first parameter is the level the layer should be set to, and the second parameter is the FontString itself. It is likely that this function will take a table of layers for setting multiple layers at the same time, but this has not been determined as of this writing. What is known is that calling  in the fashion shown works. A word of caution should be given here. Do not attempt to give  a frame or frame derived element as its second parameter. Though the game will appear to function correctly, doing so will crash the game when exiting or returning to the character selection screen.

With the FontString created, we can now fill in the code for the helper function that sets the text with the memory display. It really isn't any different from the previous version so no more will be said about it. Here's the code:

Try this version out if you wish.

Texture Goodness
Much as was done in the XML guide to frames, we'll now place a texture behind the text for a glow effect. We once again use  to create the texture object itself, then call various methods to initialize the settings. Change our  helper function as follows:

First we create the texture object itself, then set a size and tell the game what actual image file to use via the  method. This is followed by a call to  with the coordinates to use as the visible area of the image. The values here work like the XML counterpart, but the order of the parameters is left side, right side, top, then bottom.

We then set a color (this is optional) that affects the tinting of the texture, then set the alpha transparency for the texture as well as the blending mode. We then set the position of the texture relative to our frame.

Lastly, we again perform a  on our frame to set which layer this texture will be drawn at. As we want the texture to be behind the text, we set a layer value that is less than the one we specified for the text.

Once these changes are done, try out the add-on.

Closing the Frame
Time to add a simple close button. We'll use the existing  template as we did in the XML frames guide. This will allow us to create a much simpler creation function as most of the settings are already done for us. Modify the  helper function like this:

Here we create the button as expected, but we also add the fourth parameter to the call giving the name of the template we wish to use. Now all that is needed is to set the button's position relative to our frame. Note that we do not perform a  call because buttons are frame derived objects themselves and therefore doing so would cause the game to crash on exit.

Try this version out. To get the frame back after closing it, type /run MemViewerDyn_Frame:Show in the chat edit box.

Placing a Backdrop
Our close button now looks kind of ridiculous floating there in the middle of nowhere. So lets place a backdrop on this frame. We can easily set a backdrop on a frame by calling the  method of our frame, but this method requires a table with the appropriate information. So first, we'll create this table.

Add the following right before we create the base frame:

This table contains all the information we'll need for setting a backdrop. Note the similarities between this and how the backdrop information is specified in XML.

Now that we have this table, we can add a call to  in our OnLoad handler. Add the following after the calls to create the sub-elements:

Save and try this version out.

Going Green
At the start of this guide, an example was given about a fictitious buff bar add-on. Now suppose we were creating this add-on. As mentioned, dynamically creating frames solves the problem of forcing a cap on the maximum number of buffs that can be tracked, it also allows us to not pre-define a set number of frames. But if all we did was create a new frame for every buff that gets applied, we'll eventually run out of memory as there doesn't appear to be any convenient way to delete frames. This is where the technique known as frame recycling comes into play.

The idea behind frame recycling is to maintain a pool of old, currently unused frames, and always check if there is a frame available in this pool prior to creating a frame. If so, remove one from the pool and use that. If not, then we create a new frame. Once a frame is no longer needed, hide the frame and add it back to the pool. In this fashion, we only ever create the maximum number of frames needed, and thereby save memory.

Stopping Global Pollution
One of the main problems with creating frames via XML is that all frames and their sub-elements are created in the global namespace. Not only does this make accessing these frames slower, but it also creates an enormous global namespace. Last time I checked (back in version 3.0.4 or so), the _G table contained well over 60000 entries. This will slow access down even more since the Lua virtual machine much slog through this table for each global variable accessed (well OK, due to how Lua works, it doesn't need to search all 60000 entries but it still slows it down). To make matters worse, each time we create a frame or frame element using, the game also creates a global variable with the same name as the element name provided. This constant use of global variables is known as polluting the global namespace.

When we create frames dynamically, we have an opportunity to stop this effect. The way we do this is to ensure that we always assign the objects we create to a local variable or at least to some other object or table that will be a single point of access from global namespace. As our example add-on already has such a table, we can set our frame in there, as well as all sub-objects. Though if you've been paying attention, you will notice that all sub-objects are already being set inside the main frame's object. So all we need to do is set the base frame into our namespace variable, and remove all the global variables created by.

So first, add the following two lines immediately after creating the base frame.

It is safe to  the global in this fashion since there is still another reference to the object within our file (namely the local  ).

We can now do the same for all the sub-elements we crete as well. For each call to, add a line to nil the global that it creates. Remember to use the name given in the call itself and not the name of the variable. It is also a good idea to place the  here to ensure it is the global reference that is being removed. The three lines will be (to be placed after each corresponding call to ):

In the case of the close button, because we are using a template, we should also ensure that any sub-objects created in the template is also removed from global space where appropriate. This is not required for this template so no other changes need be done for our code. However, some sub-elements created by templates may still need to reside in global space for the predefined functions to work correctly, so handle templates with care.

Once the changes are done, try the new version. You will notice that the add-on runs fine, but if you close the window you will now need to type /run MemViewerDyn.Frame:Show in the chat edit box to get it back.

If you parse this last version of out example add-on, you will see that only one global variable is visible yet the entire add-on works as before.

Avoiding a Potential Problem
There is one problem with using dynamic frames as described here that may show up. Due to how we create our frame, it is quite possible to not receive the  event. This is likely caused by not having an  code snippet setup in time to receive the event. This may at first seem to be a problem since add-ons often rely on this event to see if their variables are ready.

A workaround for this problem is to register for both the  and   events. In the  handler, un-register from the   event once either the   or   event has fired.

is fired after all loading is complete and the game is about to transfer control over to the player after a loading screen. At this point the loading screen is still visible, but everything has now been loaded and therefore any variables that your add-on may require will also be loaded. The reason we un-register from the  event is that this event is triggered for every load screen, so it will get triggered when teleporting, when entering an instance, etc. So unless these are places you are also interested in, you should un-register from this event.

Reference Code
Here is the completed example add-on for reference.