Touch event propagation conundrum

I’m porting an existing game into Corona and I’m stuck right now with this little problem:

I have a virtual tray containing tiles (a la Scrabble) and the user drags tiles from the tray onto the playing board. But the tray extends off screen and I want to allow the user to swipe sideways to scroll the tray across screen to reveal the additional tiles.

So a "touch" event could either be the user dragging a tile onto the board, or it could be an attempt to scroll through the tray and I can’t know which until the "moved" phase event is generated. So in my tile dragging event handler I test to see whether the user has moved their finger more sideways than up-and-down the first time a "moved" event is generated and then try to pass focus to the tray like so:

if moveIsSideways then  
 display.getCurrentStage():setFocus( tray )  
 tray.isFocus = true  
 tile.isFocus = false  
 return false  
end  

But for whatever reason (and I suspect it’s because my handler returned true to the "began" event) the event never propagates to the tray and the tile remains active in drag mode (though because it doesn’t have the focus, it can get left behind by the finger/cursor and doesn’t necessarily receive the "ended" event).

Any ideas? [import]uid: 131885 topic_id: 22828 reply_id: 322828[/import]

Yep, i have a lot of ideas :slight_smile: Can i see your touch handler function? Then I can suggest the best way forward. [import]uid: 84637 topic_id: 22828 reply_id: 91262[/import]

Here are my two touch handlers (based on one of the demos) and a cut-down version of the code for creating the tray and the tiles:

function scrubTray(event)  
 local tray = event.target;  
 local phase = event.phase;  
 if "began" == phase then  
 -- Make target the top-most object  
 local parent = tray.parent;  
 parent:insert( tray );  
 display.getCurrentStage():setFocus( tray );  
 tray.isFocus = true  
 -- Store initial position  
 tray.x0 = event.x - tray.x;  
 elseif tray.isFocus then  
 if "moved" == phase then  
 tray.x = event.x - tray.x0;  
 elseif "ended" == phase or "cancelled" == phase then  
 display.getCurrentStage():setFocus( nil );  
 tray.isFocus = false;  
 end  
 end  
  
 return true  
end  
  
function dragTile(event)  
 local tile = event.target;  
 local phase = event.phase;  
 if "began" == phase then  
 display.getCurrentStage():setFocus( tile );  
 tile.isFocus = true;  
 tile.maybeScrubbing = true;  
 -- Store initial position  
 tile.x0 = event.x - tile.x;  
 tile.y0 = event.y - tile.y;  
 elseif tile.isFocus then  
 if "moved" == phase then  
 if tile.maybeScrubbing then  
 if math.abs(event.x - tile.x - tile.x0) \> math.abs(event.y - tile.y - tile.y0) then  
 -- horixontal component of move is greater than vertical  
 -- user is swiping the tray rather than dragging tile onto the board  
 display.getCurrentStage():setFocus( tile.tray );  
 tile.tray.isFocus = true;  
 tile.tray.x0 = tile.x0 + tile.x - tile.tray.x;  
 tile.isFocus = false;  
 return false;  
 else  
 tile.maybeScrubbing = false;  
 tile.xScale = 1;  
 tile.yScale = 1;  
 tile.alpha = 0.5;  
 end  
 end  
 tile.x = event.x - tile.x0;  
 tile.y = event.y - tile.y0;  
 elseif "ended" == phase or "cancelled" == phase then  
 -- snap tile to grid location  
 tx = (tile.x - WIDTH/2)/TILESIZE;  
 tile.x = WIDTH/2 + TILESIZE \* math.floor(tx + 0.5);  
 ty = (tile.y - HEIGHT/2)/TILESIZE;  
 tile.y = HEIGHT/2 + TILESIZE \* math.floor(ty + 0.5);  
 tile.xScale = 0.5;  
 tile.yScale = 0.5;  
 tile.alpha = 1;  
 display.getCurrentStage():setFocus( nil );  
 tile.isFocus = false;  
 board:insert(tile);  
 return true;  
 end  
 else  
 return false;  
 end  
 return true  
end  
  
-- create the tray  
local tray = display.newGroup();  
  
-- populate it with letter tiles  
for i, letter in ipairs(letterSet) do  
 local gridSquare = display.newImageRect("gridSquare.png",TILESIZE \* 2,TILESIZE \* 2);  
 gridSquare.x = i \* TILESIZE \* 2;  
 gridSquare.y = HEIGHT/2;  
 tray:insert(gridSquare);  
 local tile = spawnTile(letter);  
 tile.x = i \* TILESIZE \* 2;  
 tray:insert(tile);  
 tile:addEventListener("touch", dragTile);  
end  
  
-- add event listener:  
tray:addEventListener("touch", scrubTray);  
  

Thank you in advance for any advice you can offer. [import]uid: 131885 topic_id: 22828 reply_id: 91399[/import]

Ok. So this might work :

I am pretty sure that by returning false there (in your example it would be line 68>70) is going to end up locking your tile to focus.

Let me know if it helps :

[code]
function dragTile(event)
local tile = event.target;
local phase = event.phase;
if “began” == phase then
display.getCurrentStage():setFocus( tile );
tile.isFocus = true;
tile.maybeScrubbing = true;
– Store initial position
tile.x0 = event.x - tile.x;
tile.y0 = event.y - tile.y;
elseif tile.isFocus then
if “moved” == phase then
if tile.maybeScrubbing then
if math.abs(event.x - tile.x - tile.x0) > math.abs(event.y - tile.y - tile.y0) then
– horixontal component of move is greater than vertical
– user is swiping the tray rather than dragging tile onto the board
display.getCurrentStage():setFocus( tile.tray );
tile.tray.isFocus = true;
tile.tray.x0 = tile.x0 + tile.x - tile.tray.x;
tile.isFocus = false;
return false;
else
tile.maybeScrubbing = false;
tile.xScale = 1;
tile.yScale = 1;
tile.alpha = 0.5;
end
end
tile.x = event.x - tile.x0;
tile.y = event.y - tile.y0;
elseif “ended” == phase or “cancelled” == phase then
– snap tile to grid location
tx = (tile.x - WIDTH/2)/TILESIZE;
tile.x = WIDTH/2 + TILESIZE * math.floor(tx + 0.5);
ty = (tile.y - HEIGHT/2)/TILESIZE;
tile.y = HEIGHT/2 + TILESIZE * math.floor(ty + 0.5);
tile.xScale = 0.5;
tile.yScale = 0.5;
tile.alpha = 1;
display.getCurrentStage():setFocus( nil );
tile.isFocus = false;
board:insert(tile);
return true;
end
end
return true
end [import]uid: 84637 topic_id: 22828 reply_id: 91434[/import]

Thanks for your suggestion. It doesn’t seem to have helped, though.

The reason I was returning false in that case was that the handler should only be interested in the "moved" event if the tile in question already has focus and should otherwise allow the tray handler to deal with the event.

I just discovered my idiot mistake, though. I wasn’t passing the tray object as a parameter to the tile constructor, so when the tile tried to give focus to the tray, it was simply adding a "tray" key to the tile object and an "isFocus" key to that and assigning it a value true.

I haven’t done a lot of lua before, so the loose typing and dynamic declarations are catching me out.

Thanks again for your quick responses. [import]uid: 131885 topic_id: 22828 reply_id: 91660[/import]