WoWInterface

WoWInterface (https://www.wowinterface.com/forums/index.php)
-   Lua/XML Help (https://www.wowinterface.com/forums/forumdisplay.php?f=16)
-   -   How to properly use CreateFramePool() ? (https://www.wowinterface.com/forums/showthread.php?t=55626)

Mayron 08-05-17 09:51 AM

How to properly use CreateFramePool() ?
 
I've been seeing a lot of Blizzard code using CreateFramePool() and I'm wondering if it's worth using this myself as opposed to creating my own stack-like data structure. However, maybe it does something more than this but I can't find any documentation on it.

This is the content found inside a returned frame pool object:

ReleaseAll
achieveObjects
numActiveObjects
inactiveObjects
GetNumActive
resettingFunc
OnLoad
creationFunc
EnumerateActive
Release
Acquire
GetNextActive
EnumerateIntactive

My question is this: Is there any documentation on how to use this?

I'm assuming that you can disable/enable frames from the pool using:

Lua Code:
  1. local pool = CreateFramePool()
  2. local frame = pool:Acquire() -- uses an inactive frame or creates one with "creationFunc" function
  3. pool:Release(frame) -- not really sure if this is the correct way to disable the frame
  4.  
  5. -- something is always true. Maybe it represents if the frame is active, but since I am iterating over only active frames this seems pointless
  6. for frame, something in pool:EnumerateActive() do
  7.     -- run code to update your frame (could be a buff icon for example)
  8. end

Not sure what OnLoad does. Does it run when a frame becomes active?

MunkDev 08-05-17 10:54 AM

This is entirely implemented in Lua so you can just read the source code.

Lua Code:
  1. ObjectPoolMixin = {};
  2.  
  3. function ObjectPoolMixin:OnLoad(creationFunc, resetterFunc)
  4.     self.creationFunc = creationFunc;
  5.     self.resetterFunc = resetterFunc;
  6.  
  7.     self.activeObjects = {};
  8.     self.inactiveObjects = {};
  9.  
  10.     self.numActiveObjects = 0;
  11. end
  12.  
  13. function ObjectPoolMixin:Acquire()
  14.     local numInactiveObjects = #self.inactiveObjects;
  15.     if numInactiveObjects > 0 then
  16.         local obj = self.inactiveObjects[numInactiveObjects];
  17.         self.activeObjects[obj] = true;
  18.         self.numActiveObjects = self.numActiveObjects + 1;
  19.         self.inactiveObjects[numInactiveObjects] = nil;
  20.         return obj, false;
  21.     end
  22.  
  23.     local newObj = self.creationFunc(self);
  24.     if self.resetterFunc then
  25.         self.resetterFunc(self, newObj);
  26.     end
  27.     self.activeObjects[newObj] = true;
  28.     self.numActiveObjects = self.numActiveObjects + 1;
  29.     return newObj, true;
  30. end
  31.  
  32. function ObjectPoolMixin:Release(obj)
  33.     assert(self.activeObjects[obj]);
  34.  
  35.     self.inactiveObjects[#self.inactiveObjects + 1] = obj;
  36.     self.activeObjects[obj] = nil;
  37.     self.numActiveObjects = self.numActiveObjects - 1;
  38.     if self.resetterFunc then
  39.         self.resetterFunc(self, obj);
  40.     end
  41. end
  42.  
  43. function ObjectPoolMixin:ReleaseAll()
  44.     for obj in pairs(self.activeObjects) do
  45.         self:Release(obj);
  46.     end
  47. end
  48.  
  49. function ObjectPoolMixin:EnumerateActive()
  50.     return pairs(self.activeObjects);
  51. end
  52.  
  53. function ObjectPoolMixin:GetNextActive(current)
  54.     return (next(self.activeObjects, current));
  55. end
  56.  
  57. function ObjectPoolMixin:GetNumActive()
  58.     return self.numActiveObjects;
  59. end
  60.  
  61. function ObjectPoolMixin:EnumerateInactive()
  62.     return ipairs(self.inactiveObjects);
  63. end
  64.  
  65. function CreateObjectPool(creationFunc, resetterFunc)
  66.     local objectPool = CreateFromMixins(ObjectPoolMixin);
  67.     objectPool:OnLoad(creationFunc, resetterFunc);
  68.     return objectPool;
  69. end
  70.  
  71. FramePoolMixin = Mixin({}, ObjectPoolMixin);
  72.  
  73. local function FramePoolFactory(framePool)
  74.     return CreateFrame(framePool.frameType, nil, framePool.parent, framePool.frameTemplate);
  75. end
  76.  
  77. function FramePoolMixin:OnLoad(frameType, parent, frameTemplate, resetterFunc)
  78.     ObjectPoolMixin.OnLoad(self, FramePoolFactory, resetterFunc);
  79.     self.frameType = frameType;
  80.     self.parent = parent;
  81.     self.frameTemplate = frameTemplate;
  82. end
  83.  
  84. function FramePool_Hide(framePool, frame)
  85.     frame:Hide();
  86. end
  87.  
  88. function FramePool_HideAndClearAnchors(framePool, frame)
  89.     frame:Hide();
  90.     frame:ClearAllPoints();
  91. end
  92.  
  93. function CreateFramePool(frameType, parent, frameTemplate, resetterFunc)
  94.     local framePool = CreateFromMixins(FramePoolMixin);
  95.     framePool:OnLoad(frameType, parent, frameTemplate, resetterFunc or FramePool_HideAndClearAnchors);
  96.     return framePool;
  97. end

Mayron 08-05-17 11:16 AM

This is perfect. I wasn't sure what the main object was called. Thank you!
I'll start running some tests and figure out how it all fits together :)

Kanegasi 08-05-17 12:05 PM

Once I understood what this frame pool did, I created a very simple version I use in Decliner for generating OnUpdate responses to some filters. This way, I don't need several dedicated OnUpdate frames, just a dynamic pool that uses the first unused frame or creates a new one that adds to the pool when finished. Blizzard's way is definitely more sophisticated and made for different scenarios, but if you just need a disposable OnUpdate frame with one piece of data included in the frame, you could try what I did.

Lua Code:
  1. local pool={}
  2. function f.pool(t,i)
  3.     if i then
  4.         pool[i]:SetScript('OnUpdate',nil)
  5.         pool[i].t=nil
  6.         return
  7.     end -- function was called with an index, terminate frame at that index
  8.     for i=1,#pool do
  9.         if pool[i] and not pool[i].t then
  10.             pool[i].t={i=i,t=t}
  11.             return i
  12.         end -- loop through frames to find an unused one and return the index, putting "t" into the frame
  13.     end
  14.     insert(pool,CreateFrame('frame'))
  15.     i=#pool
  16.     pool[i].t={i=i,t=t}
  17.     return i -- no unused frames found, generate a new one and return its index, also putting "t" into the frame
  18. end


An example of usage is the following response to blocked guild invites:

Lua Code:
  1. pool[f.pool((GetCVar('Sound_EnableSFX')))]:SetScript('OnUpdate',function(self)
  2.     SetCVar('Sound_EnableSFX',self.t.t)
  3.     f.pool('',self.t.i)
  4. end)
  5. SetCVar('Sound_EnableSFX','0')

This calls a frame directly from the pool table, while getting the index from f.pool's return and putting the current SFX setting into the "t" key of the frame. Then I turn off SFX. Once the OnUpdate fires a frame later, reset SFX and release the frame using its index stored in the "i" key.

This completely blocks the "level up" sound when someone invites you to a guild.

I'm sure there's a cleaner way to do what I did, but it works for me.

Mayron 08-05-17 01:13 PM

@Kanegasi I've made a few things like this myself for a timer bars tracking addon except it executes the update function for each frame in the pool iteratively and probably not as intricate as your interesting design. Thanks for the example, I like it. Gives me a new approach to consider.

p3lim 08-05-17 07:45 PM

It's mostly good for when you want to fill a list of objects, was messing around with this one and texturepools.


All times are GMT -6. The time now is 02:38 PM.

vBulletin © 2020, Jelsoft Enterprises Ltd
© 2004 - 2020 MMOUI