Thread Tools Display Modes
01-27-10, 05:37 AM   #1
nightcracker
A Molten Giant
 
nightcracker's Avatar
AddOn Author - Click to view addons
Join Date: Sep 2009
Posts: 716
LibCoreAPI

Hi all!
I'm making a library to add some more functions to the WoW API and widget API. Mainly to make your life easier, and to shorten up your code. I'm asking YOU what you want added in it, so post here! This is what I have currently(I will be editing this post):
lua Code:
  1. local metatables = {}
  2. local dummy = function() end
  3.  
  4. for key, val in pairs(_G) do
  5.     if type(val)=="table" and val.GetObjectType then
  6.         obj = val:GetObjectType()
  7.         if not metatables[obj] then
  8.             metatables[obj] = getmetatable(val).__index
  9.         end
  10.     end
  11. end
  12.  
  13. local function destroy(self)
  14.     self.Show = dummy
  15.     self:Hide()
  16.     if self.UnregisterAllEvents then
  17.         self:UnregisterAllEvents()
  18.     end
  19. end
  20.  
  21. local function fireevent(self, event, ...)
  22.     local handler = self:GetScript("OnEvent")
  23.     if handler then
  24.         handler(self, event, ...)
  25.         return true
  26.     end
  27.     return false
  28. end
  29.  
  30. local function fire(self, script, ...)
  31.     local handler = self:GetScript(script)
  32.     if handler then
  33.         handler(self, ...)
  34.         return true
  35.     end
  36.     return false
  37. end
  38.  
  39. local function lock(self)
  40.     self.ClearAllPoints = dummy
  41.     self.SetPoint = dummy
  42.     self.SetAllPoints = dummy
  43. end
  44.  
  45. local function unlock(self)
  46.     self.ClearAllPoints = nil
  47.     self.SetPoint = nil
  48.     self.SetAllPoints = nil
  49. end
  50.  
  51. local function setsize(self, w, h)
  52.     self:SetWidth(w)
  53.     self:SetHeight(h or w)
  54. end
  55.  
  56. local function getclassbycolor(r, g, b)
  57.     r, g, b = floor(r*100+.5)/100, floor(g*100+.5)/100, floor(b*100+.5)/100
  58.     for class, color in pairs(RAID_CLASS_COLORS) do
  59.         if color.r==r and color.g==g and color.b==b then
  60.             return class
  61.         end
  62.     end
  63.     return false
  64. end
  65.  
  66. local function getsize(self)
  67.     return self:GetWidth(), self:GetHeight()
  68. end
  69.  
  70. local function setclassicon(self, class)
  71.     self:SetTexture("Interface\\WorldStateFrame\\Icons-Classes")
  72.     if class then
  73.         local classcoords = CLASS_BUTTONS[class]
  74.         if classcoords then
  75.             self:SetTexCoord(unpack(classcoords))
  76.             return true
  77.         else
  78.             self:SetTexCoord(.5, .75, .5, .75)
  79.             return false
  80.         end
  81.     end
  82.     return false
  83. end
  84.  
  85. local function islocked(self)
  86.     if self.ClearAllPoints==dummy and self.SetPoint==dummy and self.SetAllPoints==dummy then
  87.         return true
  88.     end
  89.     return false
  90. end
  91.  
  92. local function isdestroyed(self)
  93.     if self.Show==dummy and not self:IsShown() then
  94.         return true
  95.     end
  96.     return false
  97. end
  98.  
  99. local function toggle(self)
  100.     if self:IsShown() then
  101.         self:Hide()
  102.         return false
  103.     end
  104.     self:Show()
  105.     return true
  106. end
  107.  
  108. local function setshown(self, show)
  109.     if show then
  110.         self:Show()
  111.     else
  112.         self:Hide()
  113.     end
  114. end
  115.  
  116. for key, val in pairs(metatables) do
  117.     if val.Hide then -- Region object
  118.         val.Destroy = destroy
  119.         val.IsDestroyed = isdestroyed
  120.         val.SetSize = setsize
  121.         val.GetSize = getsize
  122.         val.Lock = lock
  123.         val.Unlock = unlock
  124.         val.IsLocked = islocked
  125.         val.Toggle = toggle
  126.         val.SetShown = setshown
  127.     end
  128.     if val.RegisterEvent then -- Frame object
  129.         val.FireEvent = fireevent
  130.     end
  131.     if val.GetScript then -- Any object with scripts
  132.         val.Fire = fire
  133.     end
  134.     if val.SetTexture then -- Texture object
  135.         val.SetClassIcon = setclassicon
  136.     end
  137. end
  138.  
  139. GetClassByColor = getclassbycolor

This adds the following API functions:
Code:
GetClassByColor(red, green, blue)
Returns the class associated with the color.
And the following API Widget functions:
Code:
Region:Destroy()
Permanently hides the region and unregisters events if associated.

Region:IsDestroyed()
Returns true if the region is destroyed, otherwise false.

Region:Lock()
Blocks all attempts of moving this region until it's unlocked.

Region:Unlock()
Unlocks the frame from the above function.

Region:IsLocked()
Returns true if the region is locked, otherwise false.

Region:SetSize(width, [height])
Sets the width and height of this region, if only one parameter is given both width and height will be set to the same value. Example: Region:SetSize(10, 15) sets the width to 10 and height to 15 while Region:SetSize(15) sets the width and height to 15.

Region:GetSize()
Returns the width and height of the region. Example: local width, height = Region:GetSize()

Region:Toggle()
Hides the region if shown, shows the region if hidden. Returns true if the region is now shown, false if the region is now hidden.

Region:SetShown(show)
If show is a true value(any value except nil and false) then the region is shown, else the region is hidden.

Frame:FireEvent(event, ...)
Fires the event handler of the frame with the parameter "event" as the event, and ... the parameters. The ... variable means there could be as many parameters as you want. You must not give the "self" variable in the parameters(this is done automaticly). Returns true if the handler has been executed, false if not.

Frame:Fire(script, ...)
Fires the event handler of the frame for the script "script" with parameters "...". For example, Frame:Fire("OnUpdate", 2) will run the OnUpdate handler for the frame as if 2 seconds have elapsed. Don't give the "self" variable in the parameters, this is done automaticly. Returns true if the handler has been executed, false if not.

Texture:SetClassIcon(class)
Set's the texture of this texture object to the class icon of the parameter given. The class parameter MUST be in the english version capitalized, not the localized version! If an invalid class is given the texture will be set to transparent. Example: Texture:SetClassIcon("WARRIOR") will set the texture of the object to the class icon of a warrior. No textures except blizzard's have been used for this function. Returns true if the class icon was found and set, false if not.
__________________
Three things are certain,
Death, taxes and site not found,
You, victim of one.

Last edited by nightcracker : 01-27-10 at 08:45 AM.
  Reply With Quote
01-27-10, 05:52 AM   #2
nightcracker
A Molten Giant
 
nightcracker's Avatar
AddOn Author - Click to view addons
Join Date: Sep 2009
Posts: 716
Woops, this should be in Pre-Beta Interfaces/Scripts, can someone move it please?
__________________
Three things are certain,
Death, taxes and site not found,
You, victim of one.
  Reply With Quote
01-27-10, 06:37 AM   #3
xConStruct
A Chromatic Dragonspawn
 
xConStruct's Avatar
AddOn Author - Click to view addons
Join Date: May 2008
Posts: 199
Code:
Region:SetShown(shown)
    if shown then
        self:Show()
    else
       self:Hide()
    end
end
This often bothered me

And I would suggest to make getter-functions for your new Set() ones:
- destroyed = Region:IsDestroyed()
- locked = Region:IsLocked()
- width, height = Region:GetSize()

And try to provide return values as often as possible to show if it was successful or not.
- success = Frame:FireEvent()
- success = Frame:Fire()
- success = Texture:SetClassIcon()
__________________
« Website | GitHub »

Oh hai!

Last edited by xConStruct : 01-27-10 at 06:40 AM. Reason: dry-coding, you know :/
  Reply With Quote
01-27-10, 06:45 AM   #4
nightcracker
A Molten Giant
 
nightcracker's Avatar
AddOn Author - Click to view addons
Join Date: Sep 2009
Posts: 716
Originally Posted by Cargor View Post
Code:
Region:SetShown(shown)
    if shown then
        self:Show()
    else
       self:Hide()
    end
end
This often bothered me

And I would suggest to make getter-functions for your new Set() ones:
- destroyed = Region:IsDestroyed()
- locked = Region:IsLocked()
- width, height = Region:GetSize()

And try to provide return values as often as possible to show if it was successful or not.
- success = Frame:FireEvent()
- success = Frame:Fire()
- success = Texture:SetClassIcon()
Nice to see some interest, adding those! Also I'm adding a :Toggle() script.

EDIT: Added!
__________________
Three things are certain,
Death, taxes and site not found,
You, victim of one.

Last edited by nightcracker : 01-27-10 at 07:16 AM.
  Reply With Quote
01-27-10, 07:24 AM   #5
nightcracker
A Molten Giant
 
nightcracker's Avatar
AddOn Author - Click to view addons
Join Date: Sep 2009
Posts: 716
Should I loop _G like this for gathering all metatables, or should I run this once, write the names down, and always use these(subject to changes/deletion of frames by blizzard)?

lua Code:
  1. local metatables = {}
  2. for key, val in pairs(_G) do
  3.     if type(val)=="table" and val.GetObjectType then
  4.         obj = val:GetObjectType()
  5.         if not metatables[obj] then
  6.             metatables[obj] = getmetatable(val).__index
  7.         end
  8.     end
  9. end
__________________
Three things are certain,
Death, taxes and site not found,
You, victim of one.
  Reply With Quote
01-27-10, 08:07 AM   #6
Slakah
A Molten Giant
 
Slakah's Avatar
AddOn Author - Click to view addons
Join Date: Aug 2007
Posts: 863
Originally Posted by nightcracker View Post
Should I loop _G like this for gathering all metatables, or should I run this once, write the names down, and always use these(subject to changes/deletion of frames by blizzard)?

lua Code:
  1. local metatables = {}
  2. for key, val in pairs(_G) do
  3.     if type(val)=="table" and val.GetObjectType then
  4.         obj = val:GetObjectType()
  5.         if not metatables[obj] then
  6.             metatables[obj] = getmetatable(val).__index
  7.         end
  8.     end
  9. end
You don't need to at all, all frames of the same type share the same metatable so muck about with one and you'll muck about with all of them (although there might be some frames which don't i.e. the ChatFrames)

But really imo a library shouldn't add methods to addons frames which don't use it.

Last edited by Slakah : 01-27-10 at 08:17 AM.
  Reply With Quote
01-27-10, 08:27 AM   #7
nightcracker
A Molten Giant
 
nightcracker's Avatar
AddOn Author - Click to view addons
Join Date: Sep 2009
Posts: 716
Originally Posted by Slakah View Post
You don't need to at all, all frames of the same type share the same metatable so muck about with one and you'll muck about with all of them (although there might be some frames which don't i.e. the ChatFrames)

But really imo a library shouldn't add methods to addons frames which don't use it.
Wrong, every frame type("Frame", "Button", "Texture", etc) uses a different metatable.

And about the second thing, it doesn't matter, because if they have their own function on that function name, it will override it for that frame(that's the idea of metatable.__index). If I make this library opt-in, it will be more resource heavy AND more work for the lib user. I think this is the best way.
__________________
Three things are certain,
Death, taxes and site not found,
You, victim of one.
  Reply With Quote
01-27-10, 10:47 AM   #8
Shadowed
...
Premium Member
Featured
Join Date: Feb 2006
Posts: 387
Originally Posted by nightcracker View Post
Wrong, every frame type("Frame", "Button", "Texture", etc) uses a different metatable.

And about the second thing, it doesn't matter, because if they have their own function on that function name, it will override it for that frame(that's the idea of metatable.__index). If I make this library opt-in, it will be more resource heavy AND more work for the lib user. I think this is the best way.
That's a terrible idea. Don't overwrite every single UIObject's function without the author being able to control which. Saying it's more resource heavy is silly. Looping over _G and checking type + pulling metatables is far far far far far far far far far far far far far far far far far far (far) more inefficient than just letting the frame be registered.

Last edited by Shadowed : 01-27-10 at 10:54 AM. Reason: Typo
  Reply With Quote
01-27-10, 12:03 PM   #9
Saiket
A Chromatic Dragonspawn
 
Saiket's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2008
Posts: 154
SetSize and GetSize are already part of the default widget API, although SetSize always requires both dimensions.
____
Edit: Perhaps rather than adding these methods to the shared metatables, you could expose something like LibCore:ExtendFrame to modify individual frames after they're made like Shadowed suggests. Then, a sort of LibCore:CreateFrame API could accept all the usual CreateFrame args but then extend the created frame and all its (templated) children.

Last edited by Saiket : 01-27-10 at 12:15 PM.
  Reply With Quote
01-27-10, 12:58 PM   #10
Slakah
A Molten Giant
 
Slakah's Avatar
AddOn Author - Click to view addons
Join Date: Aug 2007
Posts: 863
Originally Posted by nightcracker View Post
Wrong, every frame type("Frame", "Button", "Texture", etc) uses a different metatable.
I know for a fact "Cooldown" and "StatusBar" share at least the same __index table between frames of the same typeor else OmniCC and StatusQuo wouldn't work and iirc the only ones I found didn't share them were ChatFrames so you could probably end up doing:

lua Code:
  1. local frametypes = {"Frame", "StatusBar", etc....}
  2.  
  3. for i, v in pairs(frametypes) do
  4.     DoStuff(getmetatable(CreateFrame(v)).__index)
  5. end


but annnnnyway mucking about with frames in this way is imo a very very very very bad idea i.e. if blizz were to add these methods to frames then your library would just overwrite them leaving mods which were to not use your library in a sticky situation.

Last edited by Slakah : 01-27-10 at 01:03 PM.
  Reply With Quote
01-27-10, 01:12 PM   #11
nightcracker
A Molten Giant
 
nightcracker's Avatar
AddOn Author - Click to view addons
Join Date: Sep 2009
Posts: 716
Originally Posted by Slakah View Post
I know for a fact "Cooldown" and "StatusBar" share at least the same __index table or else OmniCC and StatusQuo wouldn't work and iirc the only ones I found didn't share them were ChatFrames, but annnnnyway mucking about with frames in this way is imo a very very very very bad idea.
Again wrong, "Cooldown" and "StatusBar" have shared functions, but aren't the same metatable.

Run the following snippet:
Code:
local metatables = {}

for key, val in pairs(_G) do 
	if type(val)=="table" and val.GetObjectType then
		obj = val:GetObjectType()
		if not metatables[obj] then
			metatables[obj] = getmetatable(val).__index
		end
	end
end

for key, val in pairs(metatables) do
	print(key," = ",val)
end
Originally Posted by Shadowed View Post
That's a terrible idea. Don't overwrite every single UIObject's function without the author being able to control which. Saying it's more resource heavy is silly. Looping over _G and checking type + pulling metatables is far far far far far far far far far far far far far far far far far far (far) more inefficient than just letting the frame be registered.
I don't OVERWRITE any single function, I add them. I'm just going to generate a static table with metatables, so no looping is required. And finally, using metatables is far far far far far far far far far (far) more efficient then having to set the custom functions on every registered frame.

Since I add functions, there shouldn't be a problem, because if an other author uses a function from my lib without him knowing, he would've gotten a nil error anyway.

About the blizz API change, I admit that it will go wrong, but a simple update should fix that. And it's the responsibility of addon authors to use updated libs.

Please stop argueing about the metatables system, I think it's the best way to affect all frames without having to do a lot as author. That's the reason metatables' __index is awesome after all.

For the people not knowing, __index works like this:
1. Request for the value of the key "key" in table "table".
2. table[key] is not found, Lua will look in the metatable.
3. metatable[key] is found, returns metatable[key].

Now the author makes a function frame:Destroy() on a frame. It will work now like this:
1. Request for the value of the key "Destroy" in table "frame".
2. frame.Destroy is found, Lua will return the function found.
3. (this step will never be executed because frame.Destroy is already found) metatable.Destroy is found, returns metatable.Destroy.
__________________
Three things are certain,
Death, taxes and site not found,
You, victim of one.

Last edited by nightcracker : 01-27-10 at 01:20 PM.
  Reply With Quote
01-27-10, 02:33 PM   #12
sigg
Featured Artist
 
sigg's Avatar
Featured
Join Date: Aug 2008
Posts: 1,251
VFL is the framework of RDX.

-------------------------------------------------------------------------
The framework contains an engine of pool objects :

The acquire function try to get a frame object from the pool, if there is no more frame in the pool, create it and execute a function OnAcquire.

local newframe = VFLUI.AcquireFrame("Frame");

The function Destroy execute all cleanup layout, unregisterd any event and release the frame in the pool, to be reused.

newframe:Destroy();

You can replace Frame by Button, checkbutton, SecureGroupHeader etc... You can see all the pool objects, the number created, the number of use and the number available from the VFL profiler.

We have extended some functions, for example the show and hide
newframe:Show(0.2, true);

that mean execute a smooth alpha show, base on time and update every 0.2. the second argument say to also use scale smooth.

RDX is based on this engine, that mean we are able to create and destroy a whole UI without doing a ReloadUI.

-------------------------------------------------------------------------
The event engine of RDX

WoWEvents:Bind(event, obj, function, id)

Example :
WoWEvents:Bind("PLAYER_ENTERING_WORLD", nil, UpdateNewAction, "mainactionButtonStance" .. self.id);

You can also use the internal event RDX :

RDXEvents:Bind
BossmodEvents:Bind etc

And so more functions available

-------------------------------------------------------------------------
You can start by looking into existing framework if it can fit your need.
  Reply With Quote
01-27-10, 02:35 PM   #13
Shadowed
...
Premium Member
Featured
Join Date: Feb 2006
Posts: 387
Originally Posted by nightcracker View Post
Again wrong, "Cooldown" and "StatusBar" have shared functions, but aren't the same metatable.

Run the following snippet:
Code:
local metatables = {}

for key, val in pairs(_G) do 
	if type(val)=="table" and val.GetObjectType then
		obj = val:GetObjectType()
		if not metatables[obj] then
			metatables[obj] = getmetatable(val).__index
		end
	end
end

for key, val in pairs(metatables) do
	print(key," = ",val)
end


I don't OVERWRITE any single function, I add them. I'm just going to generate a static table with metatables, so no looping is required. And finally, using metatables is far far far far far far far far far (far) more efficient then having to set the custom functions on every registered frame.

Since I add functions, there shouldn't be a problem, because if an other author uses a function from my lib without him knowing, he would've gotten a nil error anyway.

About the blizz API change, I admit that it will go wrong, but a simple update should fix that. And it's the responsibility of addon authors to use updated libs.

Please stop argueing about the metatables system, I think it's the best way to affect all frames without having to do a lot as author. That's the reason metatables' __index is awesome after all.

For the people not knowing, __index works like this:
1. Request for the value of the key "key" in table "table".
2. table[key] is not found, Lua will look in the metatable.
3. metatable[key] is found, returns metatable[key].

Now the author makes a function frameestroy() on a frame. It will work now like this:
1. Request for the value of the key "Destroy" in table "frame".
2. frame.Destroy is found, Lua will return the function found.
3. (this step will never be executed because frame.Destroy is already found) metatable.Destroy is found, returns metatable.Destroy.
I assure you, we both know how metatables work. I'm trying to be nice, but since you don't understand I'll be blunt. You're being delusional.

The performance loss from having people call RegisterObject(frame) and manually setting them is trivial, when I say trivial I mean it's something no sane person worries about. On top of that, because UIObjects aren't created often (or if they are, it's in bursts), the "performance loss" is one shot, and 99% of it is out of combat.

If there is one thing you should learn about programming, it's when to optimize. This isn't one of those times.

RDX is based on this engine, that mean we are able to create and destroy a whole UI without doing a ReloadUI.
On a nit picky note, you aren't destroying the UI. You are recycling the widgets into a pool, it's a release not a destroy

Last edited by Shadowed : 01-27-10 at 02:40 PM.
  Reply With Quote
01-27-10, 02:40 PM   #14
nightcracker
A Molten Giant
 
nightcracker's Avatar
AddOn Author - Click to view addons
Join Date: Sep 2009
Posts: 716
Originally Posted by Shadowed View Post
I assure you, we both know how metatables work. I'm trying to be nice, but since you don't understand I'll be blunt. You're being delusional.

The performance loss from having people call RegisterObject(frame) and manually setting them is trivial, when I say trivial I mean it's something no sane person worries about. On top of that, because UIObjects aren't created often (or if they are, it's in bursts), the "performance loss" is one shot, and 99% of it is out of combat.

If there is one thing you should learn about programming, it's when to optimize. This isn't one of those times.



On a nit picky note, you aren't destroying an UI. You are recycling the widgets into a pool, it's a release not a destroy
I prefer bluntness then being nice but vague. I'm blunt myself, but don't see it as if I hate you or something, it's my way of discussion.

The performance loss of setting the metatables is also one shot, and I don't see any disadvantages of metatables, only advantages.

@sigg - Looking at RDX now!
__________________
Three things are certain,
Death, taxes and site not found,
You, victim of one.
  Reply With Quote
01-27-10, 02:42 PM   #15
Shadowed
...
Premium Member
Featured
Join Date: Feb 2006
Posts: 387
Originally Posted by nightcracker View Post
I prefer bluntness then being nice but vague. I'm blunt myself, but don't see it as if I hate you or something, it's my way of discussion.

The performance loss of setting the metatables is also one shot, and I don't see any disadvantages of metatables, only advantages.

@sigg - Looking at RDX now!
It's the equivalent to breaking down a door with C4 compared to picking the lock.
  Reply With Quote
01-27-10, 02:45 PM   #16
nightcracker
A Molten Giant
 
nightcracker's Avatar
AddOn Author - Click to view addons
Join Date: Sep 2009
Posts: 716
Originally Posted by Shadowed View Post
It's the equivalent to breaking down a door with C4 compared to picking the lock.
So be it.

Just give some good arguments why I SHOULDN'T use metatables.
__________________
Three things are certain,
Death, taxes and site not found,
You, victim of one.
  Reply With Quote
01-27-10, 02:54 PM   #17
sigg
Featured Artist
 
sigg's Avatar
Featured
Join Date: Aug 2008
Posts: 1,251
On a nit picky note, you aren't destroying the UI. You are recycling the widgets into a pool, it's a release not a destroy
Yes, in fact that's true, but we really no more need any ReloadUI

A old video of RDX :

http://www.youtube.com/watch?v=qQscDzXY2Cg
  Reply With Quote
01-27-10, 03:32 PM   #18
Saiket
A Chromatic Dragonspawn
 
Saiket's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2008
Posts: 154
Originally Posted by nightcracker View Post
So be it.

Just give some good arguments why I SHOULDN'T use metatables.
An author might see those methods in a frame's metatable and assume they were added to the default UI. After reading this thread, I had to double check the WoW Programming site to make sure SetSize and GetSize were real and not added by a mod.

Some mods might use keys like ".Toggle" as flags, and turning them on for all frames could break the mods' assumption that the key defaults to nil.
  Reply With Quote
01-27-10, 07:02 PM   #19
Slakah
A Molten Giant
 
Slakah's Avatar
AddOn Author - Click to view addons
Join Date: Aug 2007
Posts: 863
Originally Posted by nightcracker View Post
Again wrong, "Cooldown" and "StatusBar" have shared functions, but aren't the same metatable.

Run the following snippet:
Code:
local metatables = {}

for key, val in pairs(_G) do 
	if type(val)=="table" and val.GetObjectType then
		obj = val:GetObjectType()
		if not metatables[obj] then
			metatables[obj] = getmetatable(val).__index
		end
	end
end

for key, val in pairs(metatables) do
	print(key," = ",val)
end
Gah discussions like this make me wish I still had an active wow subscription.
If your right then I have no idea how statusquo/OmniCC work.

A stupidly quick test would be to do:
lua Code:
  1. local function go()
  2.     local f = CreateFrame("Frame")
  3.     getmetatable(f).__index.p = true
  4.     return f.p
  5. end
  6. if go() == go() then
  7.     print("Frames share same metatable")
  8. else
  9.     print("Slakah fails and is special")
  10. end
  Reply With Quote
01-27-10, 08:00 PM   #20
Cralor
Mmm... cookies!!!
 
Cralor's Avatar
AddOn Author - Click to view addons
Join Date: Jun 2007
Posts: 772
Originally Posted by Slakah View Post
Gah discussions like this make me wish I still had an active wow subscription.
If your right then I have no idea how statusquo/OmniCC work.

A stupidly quick test would be to do:
lua Code:
  1. local function go()
  2.     local f = CreateFrame("Frame")
  3.     getmetatable(f).__index.p = true
  4.     return f.p
  5. end
  6. if go() == go() then
  7.     print("Frames share same metatable")
  8. else
  9.     print("Slakah fails and is special")
  10. end
I don't know much about metatables, but trusting your knowledge with them, the above does print "Frames share same metatable".
__________________
Never be satisfied with satisfactory.
  Reply With Quote

WoWInterface » AddOns, Compilations, Macros » Alpha/Beta AddOns and Compilations » LibCoreAPI


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