Jump to content

[TOPIC: topicViewTemplate]
[GLOBAL: userSmallPhoto]
Photo

OOP/ Runtime and System Events
Started by paulscottrobson Apr 15 2014 10:25 PM

6 replies to this topic
[TOPIC CONTROLS]
[/TOPIC CONTROLS]
[modOptionsDropdown]
[/modOptionsDropdown]
[reputationFilter]
[TOPIC: post.html]
#1

paulscottrobson

[GLOBAL: userInfoPane.html]
paulscottrobson
  • Pro
  • PipPipPipPipPipPip
  • 363 posts
  • Jedi

Hi.

 

Is there a clean way of dispatching events to object instances ?

 

e.g. I want to do something like

 

Runtime:addEventListener("accelerometer",someObject)

 

and have someObject have a method

 

function someClass:accelerometer(event)

 

that responds to it. Table listeners work (if you make the : a .) but there appears to be no simple way of setting 'self', so to speak. The best solution I have that works is a local function e.g.

 

Runtime:addEventListener("accelerometer",function(e) someInstance:accelerometer(e) end)

 

For touch events the object instance could be stored as a reference in the display object and grabbed from event.target but this seems to be a major hack :)

 


[TOPIC: post.html]
#2

dmccuskey

[GLOBAL: userInfoPane.html]
dmccuskey
  • Pro
  • PipPipPipPipPipPip
  • 142 posts
  • Jedi

you're close, but just need to perhaps formalize/generalize the process a little bit.
you create a closure as you have done, but you will need to save that on your object for use later, on 'removeEventListener'.
 

i have this function in my utilities module to create closures for object-callback as required:
 
-- createObjectCallback()
-- Creates a closure used to bind a method to an object. Useful for creating a custom callback.
--
-- @param object the object which has the method
-- @param method the method to call
--
function Utils.createObjectCallback( object, method )
  if object == nil or method == nil then
    error( "ERROR: missing object or method in createObjectCallback()" )
  end
  return function( ... )
    return method( object, ... )
  end
end

here's an example of its use:
function YourObject:setup()
  self._callback = Utils.createObjectCallback( self, self.runtimeEventHandler ) -- <<
  Runtime:addEventListener( 'accelerometer', self._callback )
end

function YourObject:teardown()
  Runtime:removeEventListener( 'accelerometer', self._callback )
  self._callback = nil
end

function YourObject:runtimeEventHandler( event )
  -- this method will now get runtime events
  -- in context of instances of YourObject
end

since i use this so much, i added it as a class method to my OOP library:
 
-- in OOP hierarchy
function Object:createCallback( method )
  if method == nil then
    error( "ERROR: missing method in createCallback()", 2 )
  end
  return function( ... )
    return method( self, ... )
  end
end
this style makes the callback creation a littler simpler, but everything else is the same:
function YourObject:setup()
  self._callback = self:createCallback( self.runtimeEventHandler ) -- << note use of : and . 
  Runtime:addEventListener( 'accelerometer', self._callback )
end

function YourObject:teardown()
  Runtime:removeEventListener( 'accelerometer', self._callback )
  self._callback = nil
end

function YourObject:runtimeEventHandler( event )
  -- this method will now get runtime events
  -- in context of instances of YourObject
end
HTH

cheers,
dmc

[TOPIC: post.html]
#3

davebollinger

[GLOBAL: userInfoPane.html]
davebollinger
  • Basic
  • PipPipPipPipPipPip
  • 272 posts
  • Jedi

[oops accidentally deleted wrong post, reposting from memory]
Is it specifically the accelerometer event that doesn't work? If so, that'd seem a bug, because isn't that exactly what table listeners were designed to do? fe:
local t = {}
t.name = "I am self, the instance"
function t:enterFrame(event) -- aka: t.enterFrame(self,event)
  print("self: " .. self.name .. "   event: " .. event.name)
end
Runtime:addEventListener("enterFrame", t)
or, same thing functionally, with OOP aspect elaborated:
local clas = {}
function clas:new()
  local inst = {}
  setmetatable(inst, {__index=clas})
  inst.name = "I am self, the instance"
  return inst
end
function clas:enterFrame(event)
  print("self: " .. self.name .. "   event: " .. event.name)
end
local inst = clas:new()
Runtime:addEventListener("enterFrame", inst)


[TOPIC: post.html]
#4

dmccuskey

[GLOBAL: userInfoPane.html]
dmccuskey
  • Pro
  • PipPipPipPipPipPip
  • 142 posts
  • Jedi

all event callbacks should work, either with a closure or table listener. it doesn't matter. 

 

though i rarely use table listeners. partly because i'd prefer to call my own methods/names for organization/consistency. mostly because OO code starts to break down when there's more than one object involved that gives the same event, e.g., 'touch' or custom events – this a much more likely situation in the apps i've made. in these situations, the solution using table listeners starts to get away from OO, which is the whole idea in the first place. :)

 

that said, with less-used events like 'accelerometer' and 'enterFrame', i would consider using a table listener. :)



[TOPIC: post.html]
#5

paulscottrobson

[GLOBAL: userInfoPane.html]
paulscottrobson
  • Pro
  • PipPipPipPipPipPip
  • 363 posts
  • Jedi

IME creating closures should always be done very carefully. Function listeners (sans closures) are not much use for OOP



[TOPIC: post.html]
#6

delwing

[GLOBAL: userInfoPane.html]
delwing
  • Starter
  • PipPipPipPipPipPip
  • 57 posts
  • Jedi

dmccuskey,

 

you can always make method that creates table listener. It helps me organize response from multiple components on same event type.

 

-- very rough draft, to show the concept


function class:createTouchListener( self , some, fancy, arguments )
	local listener = {
		touch = function( this , event )	-- this here is reference to table listener, self is reference to instance
			print( some , fancy , arguments, event.target , self )
		end,
	}
	return listener
end

--------

instance.rectangleListener = instance:createTouchListener( "some" , "fancy", "argument")
instance.ovalListener = instance:createTouchListener( "some" , "fancy", "argument")

instance.rectangle:addEventListener( instance.rectangleListener )
instance.oval:addEventListener( instance.ovalListener )


[TOPIC: post.html]
#7

davebollinger

[GLOBAL: userInfoPane.html]
davebollinger
  • Basic
  • PipPipPipPipPipPip
  • 272 posts
  • Jedi

I want to do something like
 
Runtime:addEventListener("accelerometer",someObject)
 
and have someObject have a method
 
function someClass:accelerometer(event)


If OP doesn't yet have an answer, then maybe a restatement of the problem is needed?

There are lots of clever solutions here, but for what you originally asked, none of them should be necessary - that functionality is already there, and works, in the form of table listeners, no closures necessary anywhere. (But I know the OP is proficient, so I at least have probably missed his intent, cuz I doubt the question was really intended to be that simple.)

If you DO need a closure, for whatever reason, then dmccuskey's approach is correct - you should store a reference for later removal. If it helps, his example (roughly) boils down to:
-- not this:
Runtime:addEventListener("someEvent", function(e) inst:someEventListener(e) end)
--because later: Runtime:removeEventListener("someEvent", wtf??

-- but this:
inst.someEventClosure = function(e) inst:someEventListener(e) end
Runtime:addEventListener("someEvent", inst.someEventClosure)
Runtime:removeEventListener("someEvent", inst.someEventClosure)
delwing's approach is also equivalent and correct btw. there are any number of ways you might do same, but the key to a 'correct' solution will be keeping a reference to that closure. closure's are usually only a source of trouble when anonymous - closures themselves aren't inherently evil.

fwiw, hth


[topic_controls]
[/topic_controls]