Thread Tools Display Modes
Prev Previous Post   Next Post Next
03/06/14, 11:02 PM   #1
inDef
Join Date: Mar 2014
Posts: 16
Window Chaining

Looking through various add-ons, I've noticed that everyone seems to use a generic method chaining function.

I'd like to analyze this function and see if I understand what is happening. Any input/feedback that you guys can provide is much appreciated.

The code I'm referring to is part of the "Crystal Fragments Passive" add-on by Lodur. Thanks to him! Here is the code

Code:
function CFP.BallAndChain( object )
	
	local T = {}
	setmetatable( T , { __index = function( self , func )
		
		if func == "__BALL" then	return object end
		
		return function( self , ... )
			assert( object[func] , func .. " missing in object" )
			object[func]( object , ... )
			return self
		end
	end })
	
	return T
end
And an example call:

Code:
  CFP.TLW = CFP.BallAndChain( 
      WINDOW_MANAGER:CreateTopLevelWindow("CFP_BuffDisplay") )
    :SetHidden(true)
    :SetDimensions(w,h)
  .__BALL
So here is what I THINK is happening from call to completion:

1. We create a new TopLevelWindow (TLW for short) by calling WINDOW_MANAGER:CreateTopLevelWindow("CFP_BuffDisplay"). We pass this Window object to the method chaining function CFP.BallAndChain.

2. The BallAndChain function returns an empty table T...but defines this table's __index metamethod.

3. We attempt to call the SetHidden method on the returned empty table T. Written out explicitly, this would look like T.SetHidden(T, true).

4. Since SetHidden isn't defined in T...we look in the defined metatable for T and look for the __index metamethod.

5. The __index metamethod returns a function. The function returned will first check to see that the original attempted method (in this case SetHidden) is defined in the upvalue variable "object" which is the original TLW Window object. Assuming SetHidden is defined for this window object, SetHidden is executed on the object. Written out explicitly, this would look something like: Window.SetHidden(Window, true). Lastly, the "self" object is returned which is the TLW window object.

NOTE: SetHidden doesn't even need to be defined for the Window object...so long as the Window object's __index metamethod eventually leads to the definition of SetHidden.

6. The same process as step 5 is executed for function SetDimensions with parameters w and h.

7. Simplifying, the entire call sort of looks like this:

CFP.TLW = CFP.(hidden TLW object with set width and height).__BALL

Since __BALL is not defined for this object, we again look at the defined __index metamethod. When the attempted function is __BALL, the Window object is finally returned to CFP.TLW.

The final result here is we have created a TopLevelWindow object which has width = w, height = h, and is hidden from the user.

Conclusion: It seems like the reason we need this function is because the original creation of the Window via WINDOW_MANAGER:CreateTopLevelWindow("CFP_BuffDisplay") would NOT work as part of a method chain. For example, if we tried to call

WINDOW_MANAGER
:CreateTopLevelWindow("CFP_BuffDisplay")
:SetHidden(true)
:SetDimensions(w,h)

this would result in an error because WINDOW_MANAGER would be the object passed to each method in the chain, not the actual TLW object created.

This is my analysis of what I think is happening. I look forward to you guys tearing this apart and telling me all the places where I am wrong .

Thanks again and I look forward to learning more about this.

Last edited by inDef : 03/06/14 at 11:32 PM.
  Reply With Quote
 

ESOUI » Developer Discussions » General Authoring Discussion » Window Chaining


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