Jump to content

[TOPIC: topicViewTemplate]
[GLOBAL: userSmallPhoto]
Photo

setFocus does not check if display object has an appropriate listener
Started by tap32 Oct 31 2019 03:10 AM

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

tap32

[GLOBAL: userInfoPane.html]
tap32
  • Contributor

  • 252 posts
  • Corona SDK

It's possible in a single touch app to block all touch inputs by set a display object to be the focus of all touches and then removing the event listener. This is a programmer error. However in my case it was caused by the fact that setFocus behaves very differently between single touch and multitouch apps and the fact that Corona does not seem to detect this state.

 

My code looked something like this:

-- scene one

scene:show(event)
  if event.phase == "will" then
    local button = display.newRect(...)
    local touchListener = function()
      if event.phase == "began" then
        display.getCurrentStage():setFocus(button, event.id)
      end
      if event.phase == "ended" then
        display.getCurrentStage():setFocus(nil)
      end
    end

    button:addEventListener("touch", touchListener)

    timer.performWithDelay(2000, function() 
      button:removeEventListener("touch", touchListener)
      composer.gotoScene("next scene")
    end)
end)

scene:addEventListener("show)

That's a simplification of what was actually happening.

 

The problem for me was that if the user is pressing the button when the timer kicks in all touches go to the display object button. However button no longer has a touch listener. In practice on a single touch app this means that all touches a silently absorbed by a display object which in my case was no longer on the screen.

 

It took me a while to figure out what was going on. I think Corona could have helped if:

1. the event system printed warnings when running in the simulator if the display object set to be the focus has no appropriate event handler

2. if the setFocus is passed an event id it should behave in the same way in single touch apps as it does in multitouch apps to avoid confusion.

 

I think 2. always catches me out when I move between single and multitouch apps, I don't know if that's just me or if it is a common problem.

 

 



[TOPIC: post.html]
#2

sporkfin

[GLOBAL: userInfoPane.html]
sporkfin
  • Contributor

  • 619 posts
  • Corona SDK

What exactly do you want to happen to the touch when you the button listener is removed?

 

If you are trying to remove the focus from the button with the listener is removed, you could try setting focus to nil in the timer.performWithDelay inline function

timer.performWithDelay(2000, function() 
      button:removeEventListener("touch", touchListener)
      display.getCurrentStage():setFocus( nil ) -- remove the focus here
      composer.gotoScene("next scene")
end) 



[TOPIC: post.html]
#3

sporkfin

[GLOBAL: userInfoPane.html]
sporkfin
  • Contributor

  • 619 posts
  • Corona SDK

Oh, I just noticed that your event.phase only had "ended".  You should add "cancelled" to account for events like removed event listeners - since the event did not typically end with the finger lifting

if event.phase == "ended" or event.phase == "cancelled" then
     display.getCurrentStage():setFocus(nil)
end



[TOPIC: post.html]
#4

tap32

[GLOBAL: userInfoPane.html]
tap32
  • Contributor

  • 252 posts
  • Corona SDK

What exactly do you want to happen to the touch when you the button listener is removed?

 

If you are trying to remove the focus from the button with the listener is removed, you could try setting focus to nil in the timer.performWithDelay inline function

timer.performWithDelay(2000, function() 
      button:removeEventListener("touch", touchListener)
      display.getCurrentStage():setFocus( nil ) -- remove the focus here
      composer.gotoScene("next scene")
end) 

 

Thanks, that's roughly what I went for in the end.

 

Internally Corona must either be checking that the focus object has a touch listener (or it would crash) or it must be caching the touchListener associated with the object/event. If it's the former case I think removing the check or printing a warning is all that is required. In the latter case something needs to change as I guess there's a function being persisted that should be garbage collected



[TOPIC: post.html]
#5

tap32

[GLOBAL: userInfoPane.html]
tap32
  • Contributor

  • 252 posts
  • Corona SDK

Oh, I just noticed that your event.phase only had "ended".  You should add "cancelled" to account for events like removed event listeners - since the event did not typically end with the finger lifting

if event.phase == "ended" or event.phase == "cancelled" then
     display.getCurrentStage():setFocus(nil)
end

Nice idea, however I just tested the following:

local button = display.newRect(
	display.contentCenterX,
	display.contentCenterY,
	200,
	200)

local touchListener = function(event)
	print (event.phase)
end

button:addEventListener("touch", touchListener)
display.getCurrentStage():setFocus(button)
timer.performWithDelay(1000, function()
	button:removeEventListener("touch", touchListener)
end)

You'll only get the began event. The cancelled event is I think only invoked if the app loses focus due to an interruption like a phone call.




[topic_controls]
[/topic_controls]