Thread Tools Display Modes
07/23/14, 03:59 AM   #1
zgrssd
AddOn Author - Click to view addons
Join Date: May 2014
Posts: 280
Creating a whole bunch of Elements dynamic from code

I have been thinking for a while now about making something I call the "Event Explorer". A single window that allows you to register every event in the game for simple debugging/testing purposes.
LibConstantMapper provides me the ability to get all the Event ID and thier names in a fully automatic fashion. And later even get me subgroups. My current count for 1.2.6 is 469 Event ID's

The only piece still missing is the GUI side of the equation.
At a simple level the window will look like this:
Window
->Row1
-->Label with event ID
-->Label with event Name
-->Checkbox to enabeled/disable listening to the event
-->maybe extra checkboxes for the way the data is output (into chat, onscreen notification, how detailed the output should be (all parameters or only that the event was fired?))
->Row2
-->Label with event ID
->Row3

I propably have to add some paging/register cards for efficiency.
A later version will likely have some categories/filters/grouping for specific event groups (like all COMBAT Events).

But for now the big issues are the handlers on the first checkbox.
Basically I need one handler that can automagically identify to wich Row (and thus event ID) it belongs too. One handler to regsiter it 469 times at 469 UI Elements.
I wil propably need a big code behind table anyway to hold all the data (wich setting is enabeled/disabeled). So if I need to store something like the UI Element reference after creating it would not be an issue.
  Reply With Quote
07/23/14, 05:04 AM   #2
merlight
AddOn Author - Click to view addons
Join Date: Jul 2014
Posts: 671
I think paging will be the way to go. That way you create say 50 rows from a template, which you can write in xml with checkbox and labels already in it. You can put any data you want in the row control, similar to how inventory rows have control.data={bagId=1, slotIndex=2, etc}. There were ingame scripts from Aug 2013 on XeNTaX forum, where loot window was paged, and it did pretty much exactly what I described - OnInitialize created a fixed number of rows from a template, and on EVENT_LOOT_UPDATED and when switching pages, updated data in those rows.

Later you can even make a search filter, e.g. events containing "TRADE" in their name

edit: can we even publish snippets from ingame scripts? It's so awkward to always tell people "there is something somewhere ..."

Last edited by merlight : 07/23/14 at 05:07 AM.
  Reply With Quote
07/23/14, 06:07 AM   #3
zgrssd
AddOn Author - Click to view addons
Join Date: May 2014
Posts: 280
Originally Posted by merlight View Post
I think paging will be the way to go. That way you create say 50 rows from a template, which you can write in xml with checkbox and labels already in it. You can put any data you want in the row control, similar to how inventory rows have control.data={bagId=1, slotIndex=2, etc}. There were ingame scripts from Aug 2013 on XeNTaX forum, where loot window was paged, and it did pretty much exactly what I described - OnInitialize created a fixed number of rows from a template, and on EVENT_LOOT_UPDATED and when switching pages, updated data in those rows.

Later you can even make a search filter, e.g. events containing "TRADE" in their name

edit: can we even publish snippets from ingame scripts? It's so awkward to always tell people "there is something somewhere ..."
Controls have a data property? That makes this a whole lot easier.
I know that as "Tag" property from WinForms and WPF. It is the second best solution for such a problem.

What is the way to get the "parent" XML Element of a control?
I would perfer to just drop the Event's ID as data in the Row. The event handler would get the Checkboxes Reference. Then I would go up on step to get the Row it is contained in and use the ID from that.

Edit:
I think I found it. Something liek thsi event handler code?
Lua Code:
  1. local function eventHandler(sender)
  2.     local senderParent = sender.GetParent()
  3.     local EventID = senderParent.data
  4.  
  5.     --Appropirate code that can now work with the EventID of the dynamicalyl created control
  6. end

Last edited by zgrssd : 07/23/14 at 06:10 AM.
  Reply With Quote
07/23/14, 06:28 AM   #4
merlight
AddOn Author - Click to view addons
Join Date: Jul 2014
Posts: 671
Originally Posted by zgrssd View Post
Controls have a data property?
I'm spamming a bit, but this deserves a post separate from the garbage I posted above
Controls are userdata with metatables, and their __index and __newindex are plain tables, so you can put anything you want in them.
  Reply With Quote
07/23/14, 02:00 PM   #5
zgrssd
AddOn Author - Click to view addons
Join Date: May 2014
Posts: 280
I took the basic window from AIResearch Grid:
xml Code:
  1. <GuiXml>
  2.     <Controls>
  3.         <TopLevelControl name="EventExplorerWindow" mouseEnabled="true" movable="true" clampedToScreen="true" hidden="true">
  4.             <Dimensions x="760" y="650" />
  5.             <Anchor point="CENTER" />
  6.             <Controls>
  7.  
  8.                 <Backdrop name="$(parent)BG" inherits="ZO_DefaultBackdrop" />
  9.  
  10.             </Controls>
  11.         </TopLevelControl>
  12.     </Controls>
  13. </GuiXml>

Added merlights template:
xml Code:
  1. <Control name="EventExplorerRow" virtual="true">
  2.     <Dimensions y="45"/>
  3.     <Controls>
  4.         <Label name="$(parent)Id" font="ZoFontGame" horizontalAlignment="RIGHT">
  5.             <Anchor point="RIGHT" relativePoint="LEFT" offsetX="50"/>
  6.         </Label>
  7.         <Label name="$(parent)Name" font="ZoFontGame">
  8.             <Anchor point="LEFT" relativeTo="$(parent)Id" relativePoint="RIGHT" offsetX="20"/>
  9.         </Label>
  10.     </Controls>
  11. </Control>

And made up some basic code to get the table and fill the window with entries:
Lua Code:
  1. local uniqueID = "d893fa70102add4c118054adcc1a1f82"
  2. local LCM = LibStub:GetLibrary("LibConstantMapper")
  3.  
  4. local dataList = {}
  5.  
  6. --get the list of Events
  7. local Events = LCM:getMappedData("Events")
  8.  
  9. local function fillWindow()
  10.     for key, value in ipairs(Events) do
  11.         local row = CreateControlFromVirtual("$(parent)Row", EventExplorerWindow, "EventExplorerRow", key)
  12.     end
  13. end
  14.  
  15. local function windowToggle()
  16.     EventExplorerWindow:SetHidden(not EventExplorerWindow:IsHidden())
  17. end
  18.  
  19. local function OnAddOnLoaded(eventID, AddonName)
  20.     if (AddonName~= "EventExplorer") then return end
  21.    
  22.     fillWindow()
  23.    
  24.     SLASH_COMMANDS["/eventexplo"] = windowToggle
  25. end
  26.  
  27. EVENT_MANAGER:RegisterForEvent("MyAddOn", EVENT_ADD_ON_LOADED, OnAddOnLoaded)
I only added the OnLoadedEvent because I got the errors from below and asumed the code was a bit to "early". But that was not it.


And all I get are those error messages and an empty window:
Code:
CreateControlFromVirtual failed.  ControlName[EventExplorerDynamicRow1], ParentName[EventExplorerWindow], VirtualName[EventExplorerRow].
CreateControlFromVirtual failed.  ControlName[EventExplorerDynamicRow2], ParentName[EventExplorerWindow], VirtualName[EventExplorerRow].
CreateControlFromVirtual failed.  ControlName[EventExplorerDynamicRow3], ParentName[EventExplorerWindow], VirtualName[EventExplorerRow].
And there is no explanation what exactly went wrong. Or even what remotely went wrong.
  Reply With Quote
07/23/14, 02:19 PM   #6
Garkin
 
Garkin's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2014
Posts: 832
How your xml looks like? It should be something like this:

xml Code:
  1. <GuiXml>
  2.     <Controls>
  3.         <Control name="EventExplorerRow" virtual="true">
  4.             <Dimensions y="45"/>
  5.             <Controls>
  6.                 <Label name="$(parent)Id" font="ZoFontGame" horizontalAlignment="RIGHT">
  7.                     <Anchor point="RIGHT" relativePoint="LEFT" offsetX="50"/>
  8.                 </Label>
  9.                 <Label name="$(parent)Name" font="ZoFontGame">
  10.                     <Anchor point="LEFT" relativeTo="$(parent)Id" relativePoint="RIGHT" offsetX="20"/>
  11.                 </Label>
  12.             </Controls>
  13.         </Control>
  14.         <TopLevelControl name="EventExplorerWindow" mouseEnabled="true" movable="true" clampedToScreen="true" hidden="true">
  15.             <Dimensions x="760" y="650" />
  16.             <Anchor point="CENTER" />
  17.             <Controls>
  18.                 <Backdrop name="$(parent)BG" inherits="ZO_DefaultBackdrop" />
  19.             </Controls>
  20.         </TopLevelControl>
  21.     </Controls>
  22. </GuiXml>

There is no UI error with this .xml file.
  Reply With Quote
07/23/14, 06:21 AM   #7
merlight
AddOn Author - Click to view addons
Join Date: Jul 2014
Posts: 671
I tried to write some untested code for illustration. Careful with dot where colon should be -- control:GetParent()

edit: or look at LAM controls, they too have custom data:
control.panel is the panel it's contained in
control.data is the optionData it was created from

Code:
<Control name="EventExplorerRow" virtual="true">
    <Dimensions y="45"/>
    <Controls>
        <Label name="$(parent)Id" font="ZoFontGame" horizontalAlignment="RIGHT">
            <Anchor point="RIGHT" relativePoint="LEFT" offsetX="50"/>
        </Label>
        <Label name="$(parent)Name" font="ZoFontGame">
            <!-- I don't know how, but it's definitely possible to make this clickable,
                 so that you can enable/disable events by cliking on the name (see "ZO_CheckButtonLabel") -->
            <Anchor point="LEFT" relativeTo="$(parent)Id" relativePoint="RIGHT" offsetX="20"/>
        </Label>
    </Controls>
</Control>
Lua Code:
  1. function EventExplorer:createRows()
  2.     local function onRowClicked(...)
  3.         self:onRowClicked(...)
  4.     end
  5.     self.rows = {}
  6.     for i = 1, NUM_PAGE_ROWS do
  7.         local row = CreateControlFromVirtual("$(parent)Row", self.pageScrollWindow, "EventExplorerRow", i)
  8.         row.data = {}
  9.         row:SetHandler("OnClicked", onRowClicked)
  10.         table.insert(self.rows, row)
  11.     end
  12. end
  13.  
  14. function EventExplorer:onRowClicked(control, button)
  15.     -- this handler should be usable for both labels and even the parent row control
  16.     -- (which I think will be useful so that cliking in the empty space between labels works)
  17.     local row
  18.     if control.data then
  19.         row = control
  20.     else
  21.         row = control:GetParent()
  22.     end
  23.     local status = self:toggleEventEnabled(row.data.eventId)
  24.     row:SetEnabled(status)
  25. end
Lua Code:
  1. -- this method will be used to fill the page with event ids and labels;
  2. -- you'd store all event ids returned from a filter in self.selectedEventIds;
  3. -- after the search you'd fill the 1st page with self:setPage(1);
  4. -- if there are more events than NUM_PAGE_ROWS, and you provide
  5. -- a control for switching to the next page, it can call self:setPage(self.currentPage);
  6. function EventExplorer:setPage(pageNumber)
  7.     local offset = math.min(0, pageNumber - 1) * NUM_PAGE_ROWS
  8.     for i, row in ipairs(self.rows) do
  9.         local eventId = self.selectedEventIds[i + offset]
  10.         local idLabel = row:GetNamedChild("Id")
  11.         local nameLabel = row:GetNamedChild("Name")
  12.         row.data.eventId = eventId
  13.         if eventId then
  14.             idLabel:SetText(tostring(eventId))
  15.             nameLabel:SetText(self:getEventName(eventId))
  16.             nameLabel:SetEnabled(self:isEventEnabled(eventId))
  17.         else
  18.             idLabel:SetText("")
  19.             nameLabel:SetText("")
  20.             nameLabel:SetEnabled(false)
  21.         end
  22.     end
  23. end
  24.  
  25. -- toggleEventEnabled, isEventEnabled will work with an internal table
  26. -- that will keep track of what events you have registered

Last edited by merlight : 07/23/14 at 06:30 AM.
  Reply With Quote

ESOUI » Developer Discussions » Lua/XML Help » Creating a whole bunch of Elements dynamic from code


Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off