March 12th, 2019
12/03/19
Boulevard of Broken Tests
In which our test script is repaired and methods that were added earlier make themselves useful.
Up until the last post we were doing pretty well in the testing department - some of the changes would break our test script, but only in little ways like variable names or a table being stored somewhere different.
With the changes to elements, z
, and the addition of a Layer class though... well, it's a bit of a problem for General Demonstration's custom functions since both of them relied pretty heavily on the first two.
"Ooh, I Think I'll Order A Tab
"
At the top of the window, a button labelled "Go!" opens a subwindow (using the Window
class) that details the values of the elements on the current tab. Our changes have utterly broken Window, since it does even more work with GUI.elms
, GUI.elms_hide
, and the z
layers than the Tab class did, and I suspect fixing it would be a whole post in and of itself. Another time, perhaps - for now we'll just replace the use of Window in this script with Reaper's native message box.
Here's the problem (I've left out all of the code for creating and opening the Window since we're abandoning that for the time being):
local tab_num = GUI.Val("tabs")
for k, v in pairs(GUI.elms_list[tab_num + 2]) do
...
Firstly, GUI.Val
doesn't have access to a global element table anymore, so it can't look up "tabs"
to get a value. We did add Layer:findElementByName()
, so there are two approaches we could take to fix this.
- Add a function to the GUI that looks through every layer, using
findElementByName
, until it finds a matching element, then haveGUI.Val
use it. - Store a reference to our Tabs element in the script and call its
:val()
method directly (GUI.Val()
just callsElement:val()
after looking it up anyway).
I've opted for the former since a global findElementByName
seems like it will be handy down the road, but the latter works just as well. It would actually be more performant, in fact, in situations where that might be a concern.
GUI.findElementByName = function (name)
for _, layer in pairs(GUI.Layers) do
local elm = layer:findElementByName(name)
if elm then return elm end
end
end
Gee, the Layer class sure made that process simple.
Since the function returns an element, and the Element class was created with our fancy table wrapper T
, getting the value in our script is even simpler:
local tab_num = GUI.findElementByName("tabs"):val()
Our second issue is that GUI.elms_list
no longer exists - we replaced it with an element table attached to each layer. The script keeps the layers in its own table and their numbering is consistent with the previous script, so the logic stays pretty consistent too:
local layer = layers[tab_num + 2]
for key, elm in pairs(layer.elements) do
...
There, back in business.
Fade To Black
Another button on the first tab shows off the Label
element's :fade()
method - click the button, some text fades out and disappears, click it again, the text fades back in.
Label:fade()
uses a hidden z
to do this, but... well, we can't hide a z
anymore and a Layer will need to be aware that one of its elements is going somewhere.
For the latter, we'll add a helper to the Element
class:
function Element:moveToLayer(dest)
if self.layer then self.layer:remove(self) end
if dest then dest:add(self) end
end
This function actually solves both problems for us, since elements can exist outside the Layer hierarchy entirely if we want. If we try to move an element without giving a destination, it'll just hop out of its parent Layer and wait until we ask for it back - no updating, no drawing, no keeping a hidden layer somewhere, nothing.
As long as we grab a reference to the Label when we create it, we can tell it to move and fade and dance the hootchie-cootchie all we want.
local fade_elm = GUI.findElementByName("my_lbl")
I told you findElementByName
would come in handy.
And with that (well, and some other screwing around that wasn't interesting enough to write about) our test script is up and running again. Huzzah!
Coming up next: We refactor something. But what is it? Does it go well? Will this series continue or is the next post going to frustrate me to the point of deleting every Lua file from my computer and giving up on programming?