Quantcast Table-based frame markup - WoWInterface
Thread Tools Display Modes
08-31-17, 02:46 PM   #1
MunkDev
A Scalebane Royal Guard
 
MunkDev's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2015
Posts: 417
Table-based frame markup

I guess this idea isn't completely new (thinking about AceConfig for example), but I created a crossover between XML and Lua that is akin to JSON. By using tables and a special parser, it essentially does the job of XML but without any XML. I'm using this to create frames and templates for my controller UI with slightly different syntax. Figured I'd post it here to see if there's any interest in releasing this as a library. Feedback is welcome.

What are the upsides?
  • Full control of the API syntax, no need to adhere to XML rules.
  • Fully recursive; you can create an entire UI with just one big nested table.
  • Unlike XML, regions don't need to be sorted by layer.
  • Supports anchoring to relative regions.
  • Runs constructors at the end of building the frame.
  • Templates and frames can be local.
  • Automatically adds and hooks scripts on widgets by using an extended mixin function.
  • Metatable functions can be called directly within the table that creates the widget.
  • Any arbitrary data (not a metatable function, API call or script) found within a table is moved over to the widget itself.
  • Indentation is optional and is entirely up to the author.
  • Generally cleaner code and easier to debug.
  • Tables are collapsible in most code editors that support Lua.

What are the downsides?
  • Extra overhead and garbage generation.
  • Tends to lead to more lines in the code, depending on indentation.
  • Depends on a lot of packing, unpacking and repacking of tables to unify the syntax.
  • By default, the numerical index of 1 denotes a table of children.
  • By default, children will always be named and therefore globally accessible.
  • Children cannot be anonymous.
  • A child's name and its parent frame key is always the same.

To show you how this works, I'll start by comparing a regular XML template (ActionButtonTemplate) and a rewritten version that uses the table parser.

XML version:
XML Code:
  1. <CheckButton name="ActionButtonTemplate" virtual="true">
  2.     <Size>
  3.         <AbsDimension x="36" y="36"/>
  4.     </Size>
  5.     <Layers>
  6.         <Layer level="BACKGROUND">
  7.             <Texture name="$parentIcon" parentKey="icon"/>
  8.         </Layer>
  9.         <Layer level="ARTWORK" textureSubLevel="1">
  10.             <Texture name="$parentFlash" parentKey="Flash" file="Interface\Buttons\UI-QuickslotRed" hidden="true"/>
  11.             <Texture name="$parentFlyoutBorder" inherits="ActionBarFlyoutButton-IconFrame" parentKey="FlyoutBorder" hidden="true">
  12.                 <Anchors>
  13.                     <Anchor point="CENTER"/>
  14.                 </Anchors>
  15.             </Texture>
  16.             <Texture name="$parentFlyoutBorderShadow" inherits="ActionBarFlyoutButton-IconShadow" parentKey="FlyoutBorderShadow" hidden="true">
  17.                 <Anchors>
  18.                     <Anchor point="CENTER"/>
  19.                 </Anchors>
  20.             </Texture>
  21.         </Layer>
  22.         <Layer level="ARTWORK" textureSubLevel="2">
  23.             <Texture name="$parentFlyoutArrow" inherits="ActionBarFlyoutButton-ArrowUp" parentKey="FlyoutArrow" hidden="true"/>
  24.             <FontString name="$parentHotKey" inherits="NumberFontNormalSmallGray" parentKey="HotKey" justifyH="RIGHT">
  25.                 <Size x="36" y="10"/>
  26.                 <Anchors>
  27.                     <Anchor point="TOPLEFT" x="1" y="-3"/>
  28.                 </Anchors>
  29.             </FontString>
  30.             <FontString name="$parentCount" inherits="NumberFontNormal" parentKey="Count" justifyH="RIGHT">
  31.                 <Anchors>
  32.                     <Anchor point="BOTTOMRIGHT" x="-2" y="2"/>
  33.                 </Anchors>
  34.             </FontString>
  35.         </Layer>
  36.         <Layer level="OVERLAY">
  37.             <FontString name="$parentName" parentKey="Name" inherits="GameFontHighlightSmallOutline">
  38.                 <Size x="36" y="10"/>
  39.                 <Anchors>
  40.                     <Anchor point="BOTTOM" x="0" y="2"/>
  41.                 </Anchors>
  42.             </FontString>
  43.             <Texture name="$parentBorder" file="Interface\Buttons\UI-ActionButton-Border" parentKey="Border" hidden="true" alphaMode="ADD">
  44.                 <Size x="62" y="62"/>
  45.                 <Anchors>
  46.                     <Anchor point="CENTER"/>
  47.                 </Anchors>
  48.             </Texture>
  49.         </Layer>
  50.         <Layer level="OVERLAY" textureSubLevel="1">
  51.             <Texture parentKey="NewActionTexture" atlas="bags-newitem" useAtlasSize="false" alphaMode="ADD" hidden="true">
  52.                 <Size x="44" y="44"/>
  53.                 <Anchors>
  54.                     <Anchor point="CENTER"/>
  55.                 </Anchors>
  56.             </Texture>
  57.             <Texture parentKey="SpellHighlightTexture" atlas="bags-newitem" useAtlasSize="false" alphaMode="ADD" hidden="true">
  58.                 <Size x="44" y="44"/>
  59.                 <Anchors>
  60.                     <Anchor point="CENTER"/>
  61.                 </Anchors>
  62.             </Texture>
  63.             <Texture parentKey="AutoCastable" file="Interface\Buttons\UI-AutoCastableOverlay" hidden="true">
  64.                 <Size x="58" y="58"/>
  65.                 <Anchors>
  66.                     <Anchor point="CENTER" x="0" y="0"/>
  67.                 </Anchors>
  68.             </Texture>
  69.         </Layer>
  70.     </Layers>
  71.     <Animations>
  72.         <AnimationGroup parentKey="SpellHighlightAnim" looping="REPEAT">
  73.             <Alpha childKey="SpellHighlightTexture" smoothing="OUT" duration=".35" order="1" fromAlpha="0" toAlpha="1"/>
  74.             <Alpha childKey="SpellHighlightTexture" smoothing="IN" duration=".35" order="2" fromAlpha="1" toAlpha="0"/>
  75.         </AnimationGroup>
  76.     </Animations>
  77.     <Frames>
  78.         <Frame name="$parentShine" parentKey="AutoCastShine" inherits="AutoCastShineTemplate">
  79.             <Anchors>
  80.                 <Anchor point="CENTER" x="0" y="0"/>
  81.             </Anchors>
  82.             <Size x="28" y="28"/>
  83.         </Frame>
  84.         <Cooldown name="$parentCooldown" inherits="CooldownFrameTemplate" parentKey="cooldown">
  85.             <Size x="36" y="36"/>
  86.             <Anchors>
  87.                 <Anchor point="CENTER" x="0" y="-1"/>
  88.             </Anchors>
  89.             <SwipeTexture>
  90.                 <Color r="1" g="1" b="1" a="0.8"/>
  91.             </SwipeTexture>
  92.         </Cooldown>
  93.     </Frames>
  94.     <NormalTexture name="$parentNormalTexture" parentKey="NormalTexture" file="Interface\Buttons\UI-Quickslot2">
  95.         <Anchors>
  96.             <Anchor point="TOPLEFT" x="-15" y="15"/>
  97.             <Anchor point="BOTTOMRIGHT" x="15" y="-15"/>
  98.         </Anchors>
  99.     </NormalTexture>
  100.     <PushedTexture file="Interface\Buttons\UI-Quickslot-Depress"/>
  101.     <HighlightTexture alphaMode="ADD" file="Interface\Buttons\ButtonHilight-Square"/>
  102.     <CheckedTexture alphaMode="ADD" file="Interface\Buttons\CheckButtonHilight"/>
  103. </CheckButton>

Lua version:
Lua Code:
  1. local ActionButtonTemplate = {
  2.     _size = {36, 36},
  3.     SetPushedTexture = [[Interface\Buttons\UI-Quickslot-Depress]];
  4.     SetHighlightTexture = {[[Interface\Buttons\ButtonHilight-Square]], 'ADD'};
  5.     SetCheckedTexture = {[[Interface\Buttons\CheckButtonHilight]], 'ADD'};
  6.     OnLoad = function(self)
  7.         self:SetNormalTexture(self.NormalTexture);
  8.     end,
  9.     {
  10.         icon = {
  11.             _type = 'Texture';
  12.             _setup = {'BACKGROUND'},
  13.         },
  14.         Flash = {
  15.             _type = 'Texture';
  16.             _setup = {'ARTWORK', nil, 1};
  17.             _texture = [[Interface\Buttons\UI-QuickslotRed]];
  18.             _hide = true;
  19.             _point = {'CENTER', 0, 0};
  20.         },
  21.         FlyoutBorder = {
  22.             _type = 'Texture';
  23.             _setup = {'ARTWORK', 'ActionBarFlyoutButton-IconFrame', 1};
  24.             _hide = true;
  25.             _point = {'CENTER', 0, 0};
  26.         },
  27.         FlyoutBorderShadow = {
  28.             _type = 'Texture';
  29.             _setup = {'ARTWORK', 'ActionBarFlyoutButton-IconShadow', 1};
  30.             _hide = true;
  31.             _point = {'CENTER', 0, 0};
  32.         },
  33.         FlyoutArrow = {
  34.             _type = 'Texture';
  35.             _setup = {'ARTWORK', 'ActionBarFlyoutButton-ArrowUp', 2};
  36.             _hide = true;
  37.         },
  38.         HotKey = {
  39.             _type = 'FontString';
  40.             _setup = {'ARTWORK', 'NumberFontNormalSmallGray', 2};
  41.             _justifyH = 'RIGHT';
  42.             _size = {36, 10};
  43.             _point = {'TOPLEFT', 1, -3};
  44.         },
  45.         Count = {
  46.             _type = 'FontString';
  47.             _setup = {'ARTWORK', 'NumberFontNormal', 2};
  48.             _point = {'BOTTOMRIGHT', -2, 2};
  49.         },
  50.         Name = {
  51.             _type = 'FontString';
  52.             _setup = {'OVERLAY', 'GameFontHighlightSmallOutline'};
  53.             _size = {36, 10};
  54.             _point = {'BOTTOM', 0, 2};
  55.         },
  56.         Border = {
  57.             _type = 'Texture';
  58.             _setup = {'OVERLAY'};
  59.             _blend = 'ADD';
  60.             _texture = [[Interface\Buttons\UI-ActionButton-Border]];
  61.             _hide = true;
  62.             _size = {62, 62};
  63.             _point = {'CENTER', 0, 0};
  64.         },
  65.         NewActionTexture = {
  66.             _type = 'Texture';
  67.             _setup = {'OVERLAY', nil, 1};
  68.             _atlas = 'bags-newitem';
  69.             _hide = true;
  70.             _size = {44, 44};
  71.             _point = {'CENTER', 0, 0};
  72.         },
  73.         SpellHighlightTexture = {
  74.             _type = 'Texture';
  75.             _setup = {'OVERLAY', nil, 1};
  76.             _atlas = 'bags-newitem';
  77.             _blend = 'ADD';
  78.             _hide = true;
  79.             _size = {44, 44};
  80.             _point = {'CENTER', 0, 0};
  81.         },
  82.         AutoCastable = {
  83.             _type = 'Texture';
  84.             _setup = {'OVERLAY', nil, 1};
  85.             _texture = [[Interface\Buttons\UI-AutoCastableOverlay]];
  86.             _size = {58, 58};
  87.             _hide = true;
  88.             _point = {'CENTER', 0, 0};
  89.         },
  90.         SpellHighlightAnim = {
  91.             _type = 'AnimationGroup';
  92.             SetLooping = 'REPEAT';
  93.             {
  94.                 inAnim = {
  95.                     _type = 'Animation';
  96.                     _setup = 'Alpha';
  97.                     SetChildKey = 'SpellHighlightTexture';
  98.                     SetSmoothing = 'OUT';
  99.                     SetDuration = .35;
  100.                     SetOrder = 1;
  101.                     SetFromAlpha = 0;
  102.                     SetToAlpha = 1;
  103.                 },
  104.                 outAnim = {
  105.                     _type = 'Animation';
  106.                     _setup = 'Alpha';
  107.                     SetChildKey = 'SpellHighlightTexture';
  108.                     SetSmoothing = 'IN';
  109.                     SetDuration = .35;
  110.                     SetOrder = 2;
  111.                     SetFromAlpha = 1;
  112.                     SetToAlpha = 0;
  113.                 },
  114.             },
  115.         },
  116.         AutoCastShine = {
  117.             _type = 'Frame';
  118.             _setup = {'AutoCastShineTemplate'};
  119.             _point = {'CENTER', 0, 0};
  120.             _size = {28, 28};
  121.         },
  122.         cooldown = {
  123.             _type = 'Cooldown';
  124.             _setup = {'CooldownFrameTemplate'};
  125.             _size = {36, 36};
  126.             _point = {'CENTER', 0, -1};
  127.             SetSwipeColor = {1, 1, 1, .8};
  128.         },
  129.         NormalTexture = {
  130.             _type = 'Texture';
  131.             _texture = [[Interface\Buttons\UI-Quickslot2]];
  132.             _points = {
  133.                 {'TOPLEFT', -15, 15};
  134.                 {'BOTTOMRIGHT', 15, -15};
  135.             },
  136.         },
  137.     },
  138. }

Personally, I find the second version a lot easier to read and modify, especially since the only hierarchical indentation is how a child relates to its parent. Note that there's no split between frames, regions or animation widgets, so they can be organised however you want. I'm currently using this for quite a few different frames and the performance dent is not as bad as I expected when I started tinkering with it. Having that said, this is most likely slower than XML and definitely slower than manually creating all your frames directly in Lua.

Here's the source code:
Lua Code:
  1. ----------------------------------------------------------------
  2. local   assert, pairs, ipairs, type, unpack, wipe, tconcat =
  3.         assert, pairs, ipairs, type, unpack, wipe, table.concat
  4. ----------------------------------------------------------------
  5. local   CreateFrame = CreateFrame
  6. ----------------------------------------------------------------
  7. local   anchor, load, call, mixin, callMethodsOnRegion, -- func calls
  8.         pop, popMulti, addSubTable, -- table operations
  9.         err, extractBuildInfo, getRelative -- misc
  10. ----------------------------------------------------------------
  11. local   ARGS, ERROR, ERROR_CODES, REGION, API, BUILDINFO
  12. local   ANCHORS, CONSTRUCTORS = {}, {}
  13. ----------------------------------------------------------------
  14.  
  15. --- Create a new frame.
  16. -- @param   object  : Type of object to be created. Frame or inherited therefrom.
  17. -- @param   name    : Name of the object to be created.
  18. -- @param   parent  : Parent of the object.
  19. -- @param   xml     : Inherited xml.
  20. -- @param   blueprint : Table consisting of additional regions to be created.
  21. -- @return  frame   : Returns the created object.
  22. function CreateTableFrame(object, name, parent, xml, blueprint, recursive)
  23.     ----------------------------------
  24.     local frame = CreateFrame(object, name, parent, xml)
  25.     ----------------------------------
  26.     if blueprint then
  27.         if recursive then
  28.             BuildFromTable(frame, blueprint, true)
  29.         else
  30.             local children = pop(blueprint, 1)
  31.             callMethodsOnRegion(frame, blueprint)
  32.             BuildFromTable(frame, children, true)
  33.         end
  34.     end
  35.     ----------------------------------
  36.     if not recursive then
  37.         anchor()
  38.         load()
  39.     end
  40.     return frame
  41. end
  42.  
  43. --- Build frame from blueprint.
  44. -- @param   frame   : Parent of the blueprint.
  45. -- @param   blueprint : Blueprint to be constructed.
  46. -- @param   recursive : Whether this is a recursive call.
  47. -- @return  frame   : Returns the altered frame.   
  48. function BuildFromTable(frame, blueprint, recursive)
  49.     assert(type(blueprint) == 'table', err('Blueprint', frame:GetName(), ERROR_CODES.BLUEPRINT))
  50.     for key, config in pairs(blueprint) do
  51.         assert(type(config) == 'table', err(key, frame:GetName(), ERROR_CODES.CONFIGTABLE))
  52.         ----------------------------------
  53.         local object, objectType, buildInfo, isLoop = extractBuildInfo(config)
  54.         ----------------------------------
  55.         for i = 1, ( isLoop or 1 ) do
  56.             local key = ( isLoop and key..i ) or key
  57.             local region = frame[key]
  58.             if not region then
  59.                 ----------------------------------
  60.                 if buildInfo then -- Assert type exists if setup table exists.
  61.                     assert(object, err(key, name, ERROR_CODES.REGION))
  62.                 end
  63.                 ----------------------------------
  64.                 if object then
  65.                     -- Region type has special constructor.
  66.                     if REGION[object] then
  67.                         region = REGION[object](frame, key, buildInfo)
  68.                     -- Region already exists.
  69.                     elseif objectType == 'table' then
  70.                         region = REGION.Existing(frame, key, object)
  71.                     -- Region should be a type of frame.
  72.                     elseif objectType == 'string' then
  73.                         region = CreateTableFrame(object, '$parent'..key, frame, buildInfo and tconcat(buildInfo, ', '), pop(config, 1), true)
  74.                     end
  75.                 else -- Assume this is a data table.
  76.                     region = config
  77.                 end
  78.                 ----------------------------------
  79.                 frame[key] = region
  80.             end
  81.  
  82.             if isLoop and region.SetID then
  83.                 region:SetID(i)
  84.             end
  85.  
  86.             callMethodsOnRegion(region, config)
  87.         end
  88.     end
  89.     -- parse if explicitly called without wrapping (building on top of existing frame)
  90.     if not recursive then
  91.         anchor()
  92.         load()
  93.     end
  94.     return frame
  95. end
  96.  
  97. ----------------------------------
  98. function call(region, method, data)
  99.     if data == 'nil' then
  100.         data = nil
  101.     end
  102.     local func = API[method] or region[method]
  103.     if type(func) == 'function' then
  104.         -- if sequential array, just unpack it.
  105.         if ( type(data) == 'table' and #data ~= 0 ) then
  106.             return func(region, unpack(data))
  107.         else
  108.             return func(region, data)
  109.         end
  110.     elseif type(data) == 'function' and region.HasScript and region:HasScript(method) then
  111.         if region:GetScript(method) then
  112.             region:HookScript(method, data)
  113.         else
  114.             region:SetScript(method, data)
  115.         end
  116.     else
  117.         region[method] = data
  118.     end
  119. end
  120.  
  121. function callMethodsOnRegion(region, methods)
  122.     -- mixin before running the rest of the region method stack,
  123.     -- since mixed in functions may be called from the blueprint
  124.     local mixin = pop(methods, '_mixin')
  125.     if mixin then
  126.         call(region, '_mixin', mixin)
  127.         -- if the mixin has an onload script, add it to the constructor stack.
  128.         -- remove the onload function from the object itself.
  129.         if region.OnLoad and not methods.OnLoad then
  130.             -- use :GetScript in case more than one load script was hooked.
  131.             methods.OnLoad = region:GetScript('OnLoad')
  132.             region:SetScript('OnLoad', nil)
  133.             region.OnLoad = nil
  134.         end
  135.     end
  136.  
  137.     for method, data in pairs(methods) do
  138.         call(region, method, data)
  139.     end
  140. end
  141.  
  142. ----------------------------------
  143. function getRelative(region, relative)
  144.     if type(relative) == 'table' then
  145.         return relative
  146.     elseif type(relative) == 'string' then
  147.         local searchResult
  148.         for key in relative:gmatch('%a+') do
  149.             if key == 'parent' then
  150.                 searchResult = searchResult and searchResult:GetParent() or region:GetParent()
  151.             elseif searchResult then
  152.                 searchResult = searchResult[key]
  153.             else
  154.                 searchResult = region[key]
  155.             end
  156.         end
  157.         return searchResult
  158.     else
  159.         err('Relative region', region:GetName(), ERROR.CODES.RELATIVEREGION)
  160.     end
  161. end
  162.  
  163. function anchor()
  164.     for _, setup in ipairs(ANCHORS) do
  165.         local numArgs = #setup
  166.         if numArgs == 2 then
  167.             local region, point = unpack(setup)
  168.             region:SetPoint(point)
  169.         elseif numArgs == 4 then
  170.             local region, point, xOffset, yOffset = unpack(setup)
  171.             region:SetPoint(point, xOffset, yOffset)
  172.         elseif numArgs == 6 then
  173.             local region, point, relativeRegion, relativePoint, xOffset, yOffset = unpack(setup)
  174.             region:SetPoint(point, getRelative(region, relativeRegion), relativePoint, xOffset, yOffset)
  175.         end
  176.     end
  177.     wipe(ANCHORS)
  178. end
  179.  
  180. function load()
  181.     for _, setup in ipairs(CONSTRUCTORS) do
  182.         local region, constructor = unpack(setup)
  183.         assert(type(constructor) == 'function', err('Constructor', region:GetName(), ERROR_CODES.CONSTRUCTOR))
  184.         constructor(region)
  185.     end
  186.     wipe(CONSTRUCTORS)
  187. end
  188.  
  189. local function gMixin(object, ...)
  190.     for i = 1, select("#", ...) do
  191.         local mixin = select(i, ...)
  192.         for k, v in pairs(mixin) do
  193.             object[k] = v
  194.         end
  195.     end
  196.  
  197.     return object
  198. end
  199.  
  200. function mixin(t, ...)
  201.     t = gMixin(t, ...)
  202.     if t.HasScript then
  203.         for k, v in pairs(t) do
  204.             if t:HasScript(k) then
  205.                 if t:GetScript(k) then
  206.                     t:HookScript(k, v)
  207.                 else
  208.                     t:SetScript(k, v)
  209.                 end
  210.             end
  211.         end
  212.     end
  213. end
  214.  
  215. function extractBuildInfo(bp)
  216.     local   fType, buildInfo, isLoop = popMulti(bp, unpack(BUILDINFO))
  217.     return  fType, type(fType), buildInfo, isLoop
  218. end
  219.  
  220. ----------------------------------
  221. function popMulti(tbl, ...)
  222.     local popped = {...}
  223.     for i, key in pairs(popped) do
  224.         popped[i] = tbl[key] or false
  225.         tbl[key] = nil
  226.     end
  227.     return unpack(popped)
  228. end
  229.  
  230. function pop(tbl, key)
  231.     local val = tbl[key]
  232.     tbl[key] = nil
  233.     return val
  234. end
  235.  
  236. function addSubTable(tbl, ...) tbl[#tbl + 1] = {...} end
  237.  
  238. ----------------------------------
  239. ARGS = {
  240. ----------------------------------
  241.     MULTI_RUN = 'function name on object as key, table of values to be unpacked into key function. Nesting allowed.',
  242.     REGION = 'Region key should be of type string and refer to a valid widget type.',
  243.     BLUEPRINT = '(frame, blueprint); blueprint = { child1 = {}, child2 = {}, ..., childN = {} }',
  244.     RELATIVEREGION = '(frame or string). Example of string: $parent.Sibling',
  245. }---------------------------------
  246.  
  247. ----------------------------------
  248. ERROR = '%s in %s %s.'
  249. ----------------------------------
  250. ERROR_CODES = {
  251. ----------------------------------
  252.     MULTI_FUNC  = 'does not exist. Loop table should contain: '..ARGS.MULTI_RUN,
  253.     MULTI_TABLE = 'has an invalid loop table. Loop table should contain: '..ARGS.MULTI_RUN,
  254.     REGION  = 'missing region type. '..ARGS.REGION,
  255.     RELATIVEREGION = 'is invalid. Type should be a parsable string or existing frame. Arguments: '..ARGS.RELATIVEREGION,
  256.     CONSTRUCTOR = 'is invalid. Constructor must be a function.',
  257.     BLUEPRINT = 'is invalid. Blueprint should be a nested table. Arguments: '..ARGS.BLUEPRINT,
  258.     CONFIGTABLE = 'is not a valid config table or existing region.',
  259. }---------------------------------
  260.  
  261. function err(key, name, code) return ERROR:format(key, name or 'unnamed region', code) end
  262.  
  263. -- Special constructors
  264. ----------------------------------
  265. REGION = {
  266. ----------------------------------
  267.     AnimationGroup = function(parent, key, setup) return parent:CreateAnimationGroup('$parent'..key, setup and unpack(setup)) end,
  268.     Animation = function(parent, key, setup) return parent:CreateAnimation(setup, '$parent'..key) end,
  269.     FontString = function(parent, key, setup) return parent:CreateFontString('$parent'..key, setup and unpack(setup)) end,
  270.     Texture = function(parent, key, setup) return parent:CreateTexture('$parent'..key, setup and unpack(setup)) end,
  271.     ---
  272.     Existing = function(parent, key, region)
  273.         _G[parent:GetName()..key] = region
  274.         region:SetParent(parent)
  275.         return region
  276.     end
  277. }---------------------------------
  278.  
  279.  
  280. ----------------------------------
  281. BUILDINFO = {
  282. ----------------------------------
  283.     '_type',    -- type of object to be created.
  284.     '_setup',   -- denotes the optional args to be passed to constructor.
  285.     '_repeat',  -- denotes whether multiple regions should be created.
  286. }
  287.  
  288. -- API listing
  289. ----------------------------------
  290. API = {
  291. ----------------------------------
  292.     [1]         = function(frame, blueprint) BuildFromTable(frame, blueprint, true) end,
  293.     _id         = function(region, ...) region:SetID(...) end,
  294.     --- Texture
  295.     _atlas      = function(texture, ...) texture:SetAtlas(...) end,
  296.     _blend      = function(texture, ...) texture:SetBlendMode(...) end,
  297.     _coords     = function(texture, ...) texture:SetTexCoord(...) end,
  298.     _gradient   = function(texture, ...) texture:SetGradientAlpha(...) end,
  299.     _texture    = function(texture, ...) texture:SetTexture(...) end,
  300.     --- FontString
  301.     _justifyH   = function(fontString, ...) fontString:SetJustifyH(...) end,
  302.     _justifyV   = function(fontString, ...) fontString:SetJustifyV(...) end,
  303.     _color      = function(fontString, ...) fontString:SetTextColor(...) end,
  304.     _font       = function(fontString, ...) fontString:SetFont(...) end,
  305.     _fontH      = function(fontString, ...) fontString:SetHeight(...) end,
  306.     _text       = function(fontString, ...) fontString:SetText(...) end,
  307.     --- LayeredRegion
  308.     _layer      = function(region, ...) region:SetDrawLayer(...) end,
  309.     _Vertex     = function(region, ...) region:SetVertexColor(...) end,
  310.     --- Frame
  311.     _attrib     = function(frame, attributes) for k, v in pairs(attributes) do frame:SetAttribute(k, v) end end,
  312.     _backdrop   = function(frame, backdrop) frame:SetBackdrop(backdrop) end,
  313.     _events     = function(frame, ...) for _, v in ipairs({...}) do frame:RegisterEvent(v) end end,
  314.     _hooks      = function(frame, scripts) for k, v in pairs(scripts) do frame:HookScript(k, v) end end,
  315.     _level      = function(frame, ...) frame:SetFrameLevel(...) end,
  316.     _strata     = function(frame, ...) frame:SetFrameStrata(...) end,
  317.     _scripts    = function(frame, scripts) for k, v in pairs(scripts) do frame:SetScript(k, v) end end,
  318.     --- Region
  319.     _alpha      = function(region, ...) region:SetAlpha(...) end,
  320.     _clear      = function(region) region:ClearAllPoints() end,
  321.     _fill       = function(region, target) region:SetAllPoints(target ~= true and getRelative(region, target)) end,
  322.     _height     = function(region, ...) region:SetHeight(...) end,
  323.     _hide       = function(region, ...) region:Hide() end,
  324.     _show       = function(region, ...) region:Show() end,
  325.     _size       = function(region, ...) region:SetSize(...) end,
  326.     _scale      = function(region, ...) region:SetScale(...) end,
  327.     _width      = function(region, ...) region:SetWidth(...) end,
  328.     --- Button
  329.     _click      = function(button, ...) button:SetAttribute('type', 'click') button:SetAttribute('clickbutton', ...) end,
  330.     _macro      = function(button, ...) button:SetAttribute('type', 'macro') button:SetAttribute('macrotext', ...) end,
  331.     _action     = function(button, ...) button:SetAttribute('type', 'action') button:SetAttribute('action', ...) end,
  332.     _spell      = function(button, ...) button:SetAttribute('type', 'spell') button:SetAttribute('spell', ...) end,
  333.     _unit       = function(button, ...) button:SetAttribute('type', 'target') button:SetAttribute('unit', ...) end,
  334.     _item       = function(button, ...) button:SetAttribute('type', 'item') button:SetAttribute('item', ...) end,
  335.     --- Constructor (faux event to replicate XML)
  336.     OnLoad      = function(region, ...) addSubTable(CONSTRUCTORS, region, ...) end,
  337.     --- Points
  338.     _point      = function(region, ...) addSubTable(ANCHORS, region, ...) end,
  339.     _points     = function(region, ...) for _, point in ipairs({...}) do addSubTable(ANCHORS, region, unpack(point)) end end,
  340.     -- Mixin
  341.     _mixin      = function(region, ...) mixin(region, ...) end,
  342.     --- Multiple runs
  343.     _multiple   = function(region, multiTable)
  344.         for k, v in pairs(multiTable) do
  345.             assert(region[k] or API[k], err(k, region:GetName(), ERROR_CODES.MULTI_FUNC))
  346.             assert(type(v) == 'table', err(k, region:GetName(), ERROR_CODES.MULTI_TABLE))
  347.             for _, args in pairs(v) do
  348.                 call(region, k, args)
  349.             end
  350.         end
  351.     end,
  352. }---------------------------------
__________________

Last edited by MunkDev : 08-31-17 at 03:30 PM.
  Reply With Quote

WoWInterface » Developer Discussions » General Authoring Discussion » Table-based frame markup

Thread Tools
Display Modes

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