setFocus preventing multi-touch events? (bug? - sample code attached)

I am noting that using setFocus is causing multitouch events to stop being received.  Is this a bug?  

If not any ideas on how to work around what I am seeing.  Sample code is below.  To reproduce:

* Touch the rectangle with finger A (keep down)

* Touch rectangle somewhere else with finger two, and note there is no event with a separate unique id (well no event at all in fact)

My goal/requirements are:

* To have an area (the rectangle) accept multiple touch events, including moves

* Ensure that when any finger slides out of rectangle, or quickly right off screen, that the touch listener code ensures that an “ended” event is detected

* So if you like the touch listener is to be a model that passes on multitouch events ON/MOVED/OFF to another module robustly such that there should never be a single multitouch event for which an “OFF” is missed being passed (i.e. each ON for a specific multitouch unique needs to have a corresponding OFF with the same multitouch unique id)

Code

system.activate( "multitouch" ) local displayW, displayH = display.contentWidth, display.contentHeight local myRectangle = display.newRect( displayW/2, displayH/2, displayW/2, displayH/2 ) myRectangle.strokeWidth = 3 myRectangle:setFillColor( 0.9, 0.9, 0.9 ) myRectangle:setStrokeColor( 1, 1, 0 ) local function touchListener(event) local uniqueTouchId = event.id if event.phase == "began" then display.getCurrentStage():setFocus(event.target, uniqueTouchId); -- SET FOCUS event.target.isFocus = true; print("BEGAN:", uniqueTouchId, event.phase) elseif event.phase == "moved" and event.target.isFocus then elseif event.target.isFocus then -- for ended or cancel  display.getCurrentStage():setFocus(event.target, nil); -- REMOVE FOCUS event.target.isFocus = nil; print("ENDED:", uniqueTouchId, event.phase) end end myRectangle:addEventListener("touch", touchListener)

bump - still stuck here - anyone from CoronaLabs able to confirm if what I’m trying to achieve is possible somehow?

Hi Greg,

According to the docs for :setFocus():

“When calling this method while multitouch is enabled, the optional parameter touchID means that the specified touch has focus on that object, but other touches do not.”

So, perhaps try omitting that ID from the set focus call, and then see if you get other touch responses on that object. Please report back what happens in that case.

Thanks,

Brent

thanks Brent - I’d missed this & will try it - unfortunately I’m not in a position to test with a device, as I would have loved to test now and get back to you…however one question just now if I may…

In the overall application there will be other areas of the screen that still need to accept touches.  So for the sake of the question here, if you assume I have two (2) rectangle areas (with margin in between) side-by-side, and they both have to accept multiple touches but at the same time have a means of not “missing” an “ended” even.  So there can be multiple touches going on at any point time in each rectangle (say with four fingers being touching the screen, two in each rectangle)

Question - So given this if you issue a “setFocus” without passing the touchID would that impact other areas outside of the rectangle (e.g. the 2nd rectangle for example) being able to pick up touches at all?     

Hi Greg,

Also from the docs:

“Using this API, it is possible to create an object that will “own” the first touch it gets, for the lifetime of that touch, and for multiple objects to obtain their own focused touches at the same time.”

So in your case, you probably shouldn’t set focus, because then I doubt you’ll be able to get multiple touches on multiple objects.

A better approach may be to log touches on each object, as properties of the object, so you know when it has 1 touch, 2 touches, etc. But this sounds like a fairly complex UI you’re building, so it’s likely to get a bit complicated… just forewarning you. :slight_smile:

Brent

oh ok.   In fact all I really want I guess is for “addEventListener” to have another parameter “always issue an ENDED event” really. 

This isn’t quite what I had in mind however here’s an example, not sure if it triggers any ideas…

Say you want a Piano Keyboard (multitouch), hang on lets make that an Organ with two levels of keyboard.   The added requirement here is that you want to be able to slide your finger side-to-side across the keyboard and in this mode it changes the frequency uniformly, not in steps.  So it’s like the keyboard enters another mode as if it’s just one long slide which changes freq (no step by step increases in frequency at the note points).   So to do this each keyboard level of the organ is just one display object, and you check to see what note is being played based on the position.  However if you get MOVE events that are greater then X pixels it starts change frequencies uniformly.

Oh and the may point being if any fingers slide off the keyboard (or right off the screen), you need to get the ENDED event otherwise that key of the organ will keep playing (i.e. it won’t get an off)

Any ideas?

HI Brent - here’s an update after some testing:

a) No setFocus ==> Lose “ended” events when sliding finger off screen

 

b) Use setFocus but do not pass uniqueTouchId pass => when touching with two fingers then release one by one, get first “ended” but not second “ended” (i.e. Finger1_began, Finger2_began, Finger2_ended)

 

c) Use setFocus & pass uniqueTouchId => when touching with two fingers then release one by one, don’t get second “began” or “ended” (i.e. Finger1_began, Finger1_ended)

So no obvious way forward here.   Is there a way in Corona to develop the 2-layer organ I describe in my post above then?  (i.e. or would this not be possible with Corona)

[PS.  Also raised a feature request: Touch listener support for always providing an “ended” event for every “began” ]

 

Are you checking for canceled phase?

If you slide finger off-screen you might get a canceled phase.

–Edit

Just rechecked your code and I see you process ended and canceled the same way so that is probably not it.

Hi Greg,

As far as I know, there’s no way you’ll ever get an “ended” phase when sliding a finger completely off the screen, since the device simply can’t read any signals there (obviously). I’m not sure any SDK does this, and perhaps not even natively can it be done (although if somebody knows for sure that this can be done, I’d be curious to hear so).

For something like you describe, with multiple fingers sliding all over the screen, over different “organ keys” and such, you may need to construct a fairly complex system that uses the “moved” phase instead of began and ended (or it uses both). Start simple… try building two “keys” and testing different methods. Then build two more keys, and test multitouch. If you get that going, keep expanding and see what happens. I maintain (stubbornly) that this is possible in Corona, but you’ll have to keep testing. :slight_smile:

Brent

thanks Brent - I can’t really see how it would be possible in Corona.  I thought of setting a timer for each individual touch but then the problem  is you don’t really know if the finger slid off the edge, or whether the user is still holding down the key for a long time.  

It seems to be the “slide” feature of the organ that triggers the road block I think.   If I drop this feature requirement I think it might be achievable then, as each key could be it’s own display object then…I’ll have to test…

I assumed that each “key” would be its own display object. Were you thinking of doing one giant object, and then somehow “mapping” touch events on it?

For the screen edge, why not just have the overall key region tucked in slightly? Then put a slim border around it which detects touches? If that receives a moved phase, then you know the user’s touch is off the keyboard, but not yet off the screen.

The top and bottom organ keyboards are their display object so as to be able to support sliding the finger from left to right but getting a constant frequency change for the notes, not step changes in frequency. So yes detects which key a new touch is in based on position. Actually this is probably possible using separate display objects per key isn’t it then thinking about it?

Re putting some margin: tried this but it didn’t work if you swipe really fast. So the setfocus seemed to be the only solution for this.

Hi Brent/all

As an update I think I have things working with the “have to use separate display objects” approach (code below).  So to achieve my “slide” requirements I’ll just have to use this and build a bit more intelligence into the listeners as the slide moves across keys (i.e. which key, at what distance from the center of each key etc).  

Couldn’t get the “ended” events to act reliably (without dropping “ended” events) in the case of using a single display object, with multiple touches on that one display object.   So the feature request here is valid I believe:  

Code for using separate display object:

system.activate( "multitouch" ) local displayW, displayH = display.contentWidth, display.contentHeight local noteObjects = {} local function touchListener(event) local uniqueTouchId = event.id local note = event.target local id = note.id if event.phase == "began" then display.getCurrentStage():setFocus(event.target, uniqueTouchId); -- display.getCurrentStage():setFocus(event.target); -- Did not work note.isFocus = true; print(event.id, note.id, "began") elseif event.phase == "moved" and note.isFocus then elseif note.isFocus then -- only ended and cancel should have remained as phases display.getCurrentStage():setFocus(event.target, nil); note.isFocus = nil; print(event.id, note.id, "ended/cancelled") end end -- create notes for i=1,3 do local tempNote = display.newRect( (i-2)\*displayW/4+displayW/2,displayH/2, displayW/4,displayH/2) tempNote:setFillColor( 0.9, 0.9, 0.9 ) tempNote.strokeWidth = 1 tempNote:setStrokeColor( 0, 0, 0 ) tempNote.id = i tempNote:addEventListener("touch", touchListener) noteObjects[i] = tempNote end

bump - still stuck here - anyone from CoronaLabs able to confirm if what I’m trying to achieve is possible somehow?

Hi Greg,

According to the docs for :setFocus():

“When calling this method while multitouch is enabled, the optional parameter touchID means that the specified touch has focus on that object, but other touches do not.”

So, perhaps try omitting that ID from the set focus call, and then see if you get other touch responses on that object. Please report back what happens in that case.

Thanks,

Brent

thanks Brent - I’d missed this & will try it - unfortunately I’m not in a position to test with a device, as I would have loved to test now and get back to you…however one question just now if I may…

In the overall application there will be other areas of the screen that still need to accept touches.  So for the sake of the question here, if you assume I have two (2) rectangle areas (with margin in between) side-by-side, and they both have to accept multiple touches but at the same time have a means of not “missing” an “ended” even.  So there can be multiple touches going on at any point time in each rectangle (say with four fingers being touching the screen, two in each rectangle)

Question - So given this if you issue a “setFocus” without passing the touchID would that impact other areas outside of the rectangle (e.g. the 2nd rectangle for example) being able to pick up touches at all?     

Hi Greg,

Also from the docs:

“Using this API, it is possible to create an object that will “own” the first touch it gets, for the lifetime of that touch, and for multiple objects to obtain their own focused touches at the same time.”

So in your case, you probably shouldn’t set focus, because then I doubt you’ll be able to get multiple touches on multiple objects.

A better approach may be to log touches on each object, as properties of the object, so you know when it has 1 touch, 2 touches, etc. But this sounds like a fairly complex UI you’re building, so it’s likely to get a bit complicated… just forewarning you. :slight_smile:

Brent

oh ok.   In fact all I really want I guess is for “addEventListener” to have another parameter “always issue an ENDED event” really. 

This isn’t quite what I had in mind however here’s an example, not sure if it triggers any ideas…

Say you want a Piano Keyboard (multitouch), hang on lets make that an Organ with two levels of keyboard.   The added requirement here is that you want to be able to slide your finger side-to-side across the keyboard and in this mode it changes the frequency uniformly, not in steps.  So it’s like the keyboard enters another mode as if it’s just one long slide which changes freq (no step by step increases in frequency at the note points).   So to do this each keyboard level of the organ is just one display object, and you check to see what note is being played based on the position.  However if you get MOVE events that are greater then X pixels it starts change frequencies uniformly.

Oh and the may point being if any fingers slide off the keyboard (or right off the screen), you need to get the ENDED event otherwise that key of the organ will keep playing (i.e. it won’t get an off)

Any ideas?

HI Brent - here’s an update after some testing:

a) No setFocus ==> Lose “ended” events when sliding finger off screen

 

b) Use setFocus but do not pass uniqueTouchId pass => when touching with two fingers then release one by one, get first “ended” but not second “ended” (i.e. Finger1_began, Finger2_began, Finger2_ended)

 

c) Use setFocus & pass uniqueTouchId => when touching with two fingers then release one by one, don’t get second “began” or “ended” (i.e. Finger1_began, Finger1_ended)

So no obvious way forward here.   Is there a way in Corona to develop the 2-layer organ I describe in my post above then?  (i.e. or would this not be possible with Corona)

[PS.  Also raised a feature request: Touch listener support for always providing an “ended” event for every “began” ]

 

Are you checking for canceled phase?

If you slide finger off-screen you might get a canceled phase.

–Edit

Just rechecked your code and I see you process ended and canceled the same way so that is probably not it.