using a proxy for a display.newGroup object

Since we cannot attach a metatable to native Corona objects, I’m hoping another developer (or one of the CL engineers) can tell me if there is a way (using __index metamethod) to pass a normal Lua table to an Corona API param that is expecting a native-group (from display.newGroup).  In other words, can I attach an attribute, function or userdata to my table such that Corona will find the actual group object it needs in the api call??

Thanks for any ideas…

Dewey

ok, in the absence of any suggestions, I’ve overriden newGroup to return my proxy obj and instead of passing the proxy object raw to the Corona api, I’ve added an __call metamethod and I pass it like this:

someCoronaApiExpectingGroup( myGroupProxyObj() ) ;

so calling the proxy obj as a function returns it’s embedded “real” group obj

ok, in the absence of any suggestions, I’ve overriden newGroup to return my proxy obj and instead of passing the proxy object raw to the Corona api, I’ve added an __call metamethod and I pass it like this:

someCoronaApiExpectingGroup( myGroupProxyObj() ) ;

so calling the proxy obj as a function returns it’s embedded “real” group obj

Hi Dewey and All,  Firstly, Sorry i cant be much help in answering your original question, but I was wondering if you can post some code on how you modified the metamethods to allow referencing of additional variables attached to a corona displaygroup. 

I am trying to use the tutorial http://coronalabs.com/blog/2012/05/01/tutorial-property-callbacks/ but struggling because a lot of my code uses groups.  I have the feeling this proxy code only works for display.newImage objects.

As an example take a game where you add graphical letter tiles to a rack group object to make a word. I want a listener to listen to that group and any changes to the self.wordLength variable

[lua]

rack = {}

function rack.new()

local  self = display.newGroup()

–attributes

self.wordLength = 0

    self = proxy.get_proxy_for( self )

    – next, let’s set up a listener for property updates

    function self:propertyUpdate( event )

         if event.key == “wordLength” then

             print( "Current Word Length: " … event.value )

         end

    

    end

    self:addEventListener( “propertyUpdate” )

–etc

[/lua]

what i want is the proxy.lua reference to pickup on when the wordLength changes as a result of user actions etc. My current code is as follows, but nothing seems to get printed to screen when the self.wordLength variable is incremented.

The other code i am going off is http://coronalabs.com/blog/2012/06/26/how-to-use-custom-events-in-corona/

Do you or does anyone else have any ideas, or the changes required to my code or proxy.lua to handle groups as the display object container?

Any thoughts from you or others on the forums. Can you please post your solution?

cheers

Nick

I have used the proxy on groups without problems. First of all avoid using self as variable name as this is used in table methods to reference itself. Use another name. Actually other than that I don’t know what the issue would be. Are you sure you are changing the wordLength of this same object? As I mentioned if you are using self in any method (obj:method()) then self means something else and won’t update your group. Also if you use proxy make sure you don’t pass the proxy object to a corona API that expects a display object. Use yourObjName.raw.

You can use the fix for disaply object API call as mentioned by ETdoFresh in this comment. Then you can apss the proxy object and don’t have to use .raw .

I have used it with no problems.

@primoz

I’m really glad you referenced that comment…I had missed this trick:

– Use this to fix built-in corona functions
proxy._class = object._class
proxy._proxy = object._proxy

but it’s a really great idea.

Have you found that this approach is equally reliable for both groups AND displayObjects??

Has anyone from CL registered an opinion about the (future) safety of using this pass-thru technique?

@online2

as @primoz said, don’t use “self” as a variable name.

you should not need to modify anything else…all key settings on the proxy obj are really updates to the underlying “raw” object so all the standard corona basics should continue to work as before…

@dgaedcke

  1. I’m not really sure on which objects I have used it, but I am sure I used it a couple of time on groups and as groups are basically a special kind of display object I would think that it should work on all of them.

  2. Not that I know of. The way I see it, it is not likely that corona would change or remove these properties. They could add some that would be required for some API to work but we would just add that as well.

@primoz and @dgaedcke, thanks so much for your quick replies. They were very helpful, and I have now got the proxy working (it seems) . Heres my proxy code based on your recommendations - in case others have the same challenges [lua]

– proxy.lua

– Adds “propertyUpdate” events to any Corona display object.

– Modified with _class and _proxy to fix display group references

local Proxy = { }

function Proxy.get_proxy_for( object )

    – Metatables start from blank, or near blank tables

    local proxy = { }

    proxy.raw = object

    – Use this to fix built-in corona functions

    proxy._class = object._class

    proxy._proxy = object._proxy

    – Define metatable

    local metatable = {

        – table[key] is accessed

        __index = function ( t, key )

            – used to check specific errors in access

            --print ( “Access”, t, key ) – debug comment    

            – This code allows raw passthrough

            if key == “raw” then

                return rawget( proxy, “raw” ) – i think this is right - proxy was t varable

            end

            – -- pass method and property requests to the display object

            – if type(object[key]) == ‘function’ then

            –     return function(…) arg[1] = object; objectkey end

            – else

            –     return object[key]

            – end

            

            return object[key]

        end,

        – value written to table[key]

        __newindex = function ( t, key, value )

            – used to check specific errors in access

            --print ( “Write”, t, key, value )  – debug comment    

            local event =

            {

                name = “propertyUpdate”,

                target=t,

                key=key,

                value=value

            }

            object:dispatchEvent( event )

            

            – update the property on the display object

            object[key] = value

        end

    }

    – Set metatable and return proxy

    setmetatable ( proxy, metatable )

    return proxy

end

return Proxy 

[/lua]

You can see i have added the .raw option in. I am assuming i have done this correctly. The other thing that doesn’t seem to work is when i add the code in from the corona tute below. you can see it commented out in my proxy code. Admittedly I dont quite understand what this code does. It seems to pass through function arguments, but it breaks the proxy on group objects.

[lua]

– pass method and property requests to the display object

        if type(object[key]) == ‘function’ then

                return function(…) arg[1] = object; objectkey end

            else

                return object[key]

            end

[/lua]

Any further comments? Again, thanks a bunch for your quick and helpful replies. Nick

That part is needed because that is the part that reads all the properties from the original display object and returns them through your proxy. Without it you can not read the display objects properties when using the proxy. What kind of error are you getting and when?

Thanks for other quick reply @primoz.  Ok, now i understand. I went through and removed the references to self as you earlier recommended, and added the code back into the proxy, and it now appears to be working as intended.

Heres my code for scrutiny or to help others 

[lua]

– proxy.lua

– Adds “propertyUpdate” events to any Corona display object.

– Modified with _class and _proxy to fix display group references

local Proxy = { }

function Proxy.get_proxy_for( object )

    – Metatables start from blank, or near blank tables

    local proxy = { }

    proxy.raw = object

    – Use this to fix built-in corona functions

    proxy._class = object._class

    proxy._proxy = object._proxy

    – Define metatable

    local metatable = {

        – table[key] is accessed

        __index = function ( t, key )

            – used to check specific errors in access

            --print ( “Access”, t, key ) – debug comment    

            – This code allows raw passthrough for unproxied object

            if key == “raw” then

                return rawget( proxy, “raw” )

            end

            – pass method and property requests to the display object

            if type(object[key]) == ‘function’ then

                return function(…) arg[1] = object; objectkey end

            else

                return object[key]

            end

            

            – return object[key]

        end,

        – value written to table[key]

        __newindex = function ( t, key, value )

            – used to check specific errors in access

            --print ( “Write”, t, key, value )  – debug comment    

            local event =

            {

                name = “propertyUpdate”,

                target=t,

                key=key,

                value=value

            }

            object:dispatchEvent( event )

            

            – update the property on the display object

            object[key] = value

        end

    }

    – Set metatable and return proxy

    setmetatable ( proxy, metatable )

    return proxy

end

return Proxy

[/lua]

and usage example. I hope this helps. Let me know if I have made any errors, or if theres better ways.

[lua]

Object  = {}

function Object.new(params)

    local object = display.newGroup()

    – object properties to listen for

    object.property = 0

    – etc

    – Setup Proxy and Listener

    object = proxy.get_proxy_for( object )

    function object:propertyUpdate( event )

        – listen to updates  on .property

        if event.key == “property” then

            print( "Changed " … event.key … " to " … event.value )

        end

    end

    object:addEventListener( “propertyUpdate” )

    – object functions below here

    function object:exampleIncrement()

        – body

        object.property = object.property + 1 

    end

    – etc

    return object

end

return Object

[/lua]

It looks fine to me. I was just looking at this yesterday as I need it for something again and I was considering that maybe it would be smart to nil the properties that the proxy sets up to avoid any possible leftover references and thus avoid possible memory leaks. This can be done in either a finalize event or when removeSelf is called which the proxy allows you to detect. I will add that later today probably and post the changes.

This is what I use. It adds the ability to listen for function calls on the object. I tried adding a cleanup routine but it is pointless since neither removeSelf() nor finalize event is called for objects that are inserted in a group and the group is removed. Also note that usually events won’t fire if corona changes something on the original object like physics moving an object. I have not tried it but it might work if you create the proxy first and after create the physics body on the proxy.

-- -- proxy.lua -- Adds "propertyUpdate" events to any Corona display object. -- local m = {} function m.get\_proxy\_for( obj ) local t = {} t.raw = obj t.\_class = obj.\_class t.\_proxy = obj.\_proxy local mt = { \_\_index = function(tb,k) if k == "raw" then return rawget( t, "raw" ) end -- pass method and property requests to the display object if type(obj[k]) == 'function' then return function(...) arg[1] = obj; local event = { name = "functionCall", target=tb, key=k } obj:dispatchEvent( event ) obj[k](unpack(arg)) end else return obj[k] end end, \_\_newindex = function(tb,k,v) -- dispatch event before property update local event = { name = "propertyUpdate", target=tb, key=k, value=v } obj:dispatchEvent( event ) -- update the property on the display object obj[k] = v end } setmetatable( t, mt ) return t end return m

Thanks @primoz.  I came across this library http://jonbee.be/post/3760389212/proper-group-cleaning-in-corona-script-download  to help with group removal but not sure if it is applicable i this instance.  Could you use this in conjunction with your cleanup proxy routine?

@primoz

in my experience (a while back), obj:dispatchEvent( event )

always runs in the NEXT frame (not the one the proxy is currently running in)

If that’s true, then execution order should be irrelevant here…

Has that changed or is there some other reason why the event should be dispatched BEFORE the property is set?

ah, I noticed that when you attach the listener (addEventListener outside of the proxy), THAT will fire the proxy too, but no listener MAY BE currently established (because of execution order)…

if the event goes no-where…then I’m wrong about “next frame”

if the event is firing EVEN FOR attaching addEventListener, then the next-frame situation is true and order should not matter…thoughts??

Thanks

Dewey

@online2 Yes that’s exactly what I was talking about. However after thinking about this a little bit more I can’t realy see whay it would be needed. It will be released when the reference to your proxy is released and the display object is handled in a normal way. I was just trying to be thorough as I don’t like surprises down the road.

@dgaedcke I haven’t realy tought about it. I see no reason why event should be sent before but in my experience I have never noticed events to fire in next frame as I use Glider and can step through running code and the events always fired imediately before continuing code. In this situation it might be good to set the property from the event table so you can actually change it in your propertyUpdate listener and it will get set to that value. I can test the calling order again later in the day.

Thanks for all your help @primoz and @dgaedcke. much appreciated. I’ll also modify my objects to avoid self in the main declaration. cheers.

You can use self in any object method like object:myMethod() self.something = anotherThing end   This is fine and self will always reference the object instance that this method was defined on. For that reason it is not wise to name your variables self, as when you are coding any object method you do not know what self will reference, your variable or the object the method is defined on.

Got it. I originally thought i was being clean by referencing in my incorrect way. But I can see now what your are saying. Even if there are still some aspects of the corona self reference that I am a little fuzzy on.  That said, this and playing around with metatables in the proxy is helping improving my understanding. thanks

Update on this. I have noticed that if there is an update going on on the proxy like a transition or onEnterFrame (which should be removed anyway before disposing of it) and you remove the object it may cause an error. This fixes it:

 if obj.dispatchEvent then obj:dispatchEvent( event ) end

That’s in two places that dispatchEvent gets called in my version.

Hi Dewey and All,  Firstly, Sorry i cant be much help in answering your original question, but I was wondering if you can post some code on how you modified the metamethods to allow referencing of additional variables attached to a corona displaygroup. 

I am trying to use the tutorial http://coronalabs.com/blog/2012/05/01/tutorial-property-callbacks/ but struggling because a lot of my code uses groups.  I have the feeling this proxy code only works for display.newImage objects.

As an example take a game where you add graphical letter tiles to a rack group object to make a word. I want a listener to listen to that group and any changes to the self.wordLength variable

[lua]

rack = {}

function rack.new()

local  self = display.newGroup()

–attributes

self.wordLength = 0

    self = proxy.get_proxy_for( self )

    – next, let’s set up a listener for property updates

    function self:propertyUpdate( event )

         if event.key == “wordLength” then

             print( "Current Word Length: " … event.value )

         end

    

    end

    self:addEventListener( “propertyUpdate” )

–etc

[/lua]

what i want is the proxy.lua reference to pickup on when the wordLength changes as a result of user actions etc. My current code is as follows, but nothing seems to get printed to screen when the self.wordLength variable is incremented.

The other code i am going off is http://coronalabs.com/blog/2012/06/26/how-to-use-custom-events-in-corona/

Do you or does anyone else have any ideas, or the changes required to my code or proxy.lua to handle groups as the display object container?

Any thoughts from you or others on the forums. Can you please post your solution?

cheers

Nick