Tapping button on modal overlay propagates to scene below after hideOverlay()

I have a scene where you can click a button to show an overlay, using composer.showOverlay(), with isModal = true. The overlay has two buttons (OK and Cancel) that hide the overlay, using composer.hideOverlay(). However, when you tap those buttons, the tap event propagates to the scene below. This happens both in the Corona simulator and on the device.

This does not happen if you tap anything else in the overlay. In that sense it is correctly modal. But if you tap the buttons that hide the overlay, the tap events propagate down to the scene underneath.

I discovered that this exact issue was discussed about 4 years ago: https://forums.coronalabs.com/topic/27804-tap-of-button-on-modal-overlay-propagates-to-scene-below-after-hideoverlay/

In that thread, the workaround was putting a timer before the hideOverlay(). I assume that will work (I have not yet tried it), but is this expected behavior? Or should I enter a bug report on hideOverlay()?

Hi @r_donato,

Can you please post your code on how you create the overlay and these buttons associated with it? Please surround your code with “lua” tags for clarity in the forum thread:

[lua] -- code here [/lua]

Thanks,

Brent

[lua]

— main.lua

local composer = require(‘composer’)

composer.gotoScene(‘scene1’)

— scene1.lua

local composer = require(‘composer’)
local scene = composer.newScene()
local widget = require(‘widget’)

function scene:create(event)
    local bg = display.newRect(0, 0, 320, 480)
    bg.anchorX, bg.anchorY = 0, 0
    bg.fill = { 1, 0, 0 } – Red background

    local function onTap(event)
        print('scene1 click detected: ', event.name, event.phase, event.x, event.y)
    end
    bg:addEventListener(‘tap’, onTap)

    local function showOverlayClicked(event)
        – isModal so events should not propagate to scene1
        composer.showOverlay(‘scene2’, { isModal = true })
    end

    local showOverlayButton = widget.newButton({
        x = 160,
        y = 240,
        label = ‘Show overlay’,
        fontSize = 20,
        shape = ‘roundedRect’,
        width = 160,
        height = 30,
        fillColor = { default = {0, 1, 0}, over = {0, 0, 1} },
        onRelease = showOverlayClicked
    })
end

scene:addEventListener(‘create’, scene)
return scene

— scene2.lua

local composer = require(‘composer’)
local scene = composer.newScene()

function scene:create(event)
    print(‘in scene2’)
    local bg = display.newRect(160, 120, 160, 240)
    bg.fill = { 1, 1, 1 } – White background
end

scene:addEventListener(‘create’ , scene)
return scene

[/lua]

When I run the above code in the simulator, I can click the red background and see a print() of the event. Those print()s keep happening even if the overlay is showing. All the events are passing through to the scene underneath, whether or not I click the white background that belongs to the overlay.

Hi, I don’t see the buttons you’re talking about anywhere in your code? However, I think that the problem might be that you’re not properly adding your display objects to your scenes’ views. At least I can’t see you doing it anywhere in the code posted. I’ve experienced this myself, and making sure that all display object are inserted correctly into self.view solved it for me.

Like this:

self.view:insert(aButtonOrWhatever)

Hi Markus, you’re right on that! I did not realize that insert()ing into the scene view was needed for the overlay to work. Thanks for that. However, this does NOT fully solve the problem.

I updated the code with insert()s and a Hide Overlay button.

[lua]

— main.lua

local composer = require(‘composer’)

composer.gotoScene(‘scene1’)

— scene1.lua

local composer = require(‘composer’)
local scene = composer.newScene()
local widget = require(‘widget’)

function scene:create(event)
    local bg = display.newRect(0, 0, 320, 480)
    bg.anchorX, bg.anchorY = 0, 0
    bg.fill = { 1, 0, 0 } – Red background
    self.view:insert(bg)

    local function onTap(event)
        print('scene1 click detected: ', event.name, event.phase, event.x, event.y)
    end
    bg:addEventListener(‘tap’, onTap)

    local function showOverlayClicked(event)
        – isModal so events should not propagate to scene1
        print(‘Show Overlay clicked’)
        composer.showOverlay(‘scene2’, { isModal = true })
    end

    local showOverlayButton = widget.newButton({
        x = 160,
        y = 240,
        label = ‘Show overlay’,
        fontSize = 20,
        shape = ‘roundedRect’,
        width = 160,
        height = 30,
        fillColor = { default = {0, 1, 0}, over = {0, 0, 1} },
        onRelease = showOverlayClicked
    })
    self.view:insert(showOverlayButton)
end

scene:addEventListener(‘create’, scene)
return scene

— scene2.lua

local composer = require(‘composer’)
local scene = composer.newScene()
local widget = require(‘widget’)

function scene:create(event)
    print(‘in scene2’)
    local bg = display.newRect(160, 120, 160, 240)
    bg.fill = { 1, 1, 1 } – White background
    self.view:insert(bg)
    
    local function hideOverlayClicked(event)
        print(‘Hide Overlay clicked’)
        composer.hideOverlay()
    end

    local hideOverlayButton = widget.newButton({
        x = 160,
        y = 225,
        label = ‘Hide overlay’,
        fontSize = 20,
        shape = ‘roundedRect’,
        width = 160,
        height = 30,
        fillColor = { default = {0, 0, 0}, over = {0, 0, 1} },
        onRelease = hideOverlayClicked
    })
    self.view:insert(hideOverlayButton)
end

scene:addEventListener(‘create’ , scene)
return scene

[/lua]

In this code, when the overlay is shown, nothing passes through to the scene underneath unless you click Hide Overlay. If you click Hide Overlay, you see printed:

10:09:19.644  Hide Overlay clicked
10:09:19.644  scene1 click detected:     tap    nil    227    236

Any idea on why? If I wrap composer.hideOverlay in a timer (of even 1 millisecond) the problem goes away.

I’m not sure, but you should always return true from your event listener if you want to stop the event from propagating.

local function hideOverlayClicked(event) print('Hide Overlay clicked') composer.hideOverlay() return true end

Good point, Markus. Adding “return true” did not solve the problem, so I changed the print()s to get more details:

[lua]

        print('Show Overlay clicked: ', event.name, event.phase, event.x, event.y)
        print('Hide Overlay clicked: ', event.name, event.phase, event.x, event.y)
[/lua]

This gave me the following outputs:

11:04:44.527  Hide Overlay clicked:     touch    ended    105    230
11:04:44.527  scene1 click detected:     tap    nil    105    230
 

So there are two different events. The overlay button receives a “touch”, then a “tap” is sent to the scene beneath. That is the same behavior described in this tutorial: https://coronalabs.com/blog/2013/10/01/tutorial-taptouch-anatomy/

The button listeners (onPress, onRelease, onEvent) only seem to respond to “touch” events. That’s why returning true doesn’t solve the problem; it’s a different (“tap”) event being sent to the scene below.

I tried manually adding a “tap” listener to the Hide Overlay button:

[lua]

    local function hideOverlayTapped(event)
        print('Hide Overlay clicked: ', event.name, event.phase, event.x, event.y)
        return true
    end
    hideOverlayButton:addEventListener(‘tap’, hideOverlayTapped)

[/lua]

But that did not solve the problem. If it receives a tap event, it still passes it through to the scene underneath, even though the listener includes a “return true”.

Is adding a timer delay the only way to avoid passing events to the scene underneath?

I tried running your code, and just like you described it I couldn’t get it to work without adding a small delay before hiding the overlay.

Seems to be a problem with the widget button. When I tried replacing the button with a rectangle and a normal tap listener everything worked as expected.

Good thought, I hadn’t considered changing to a rectangle.

Can a Staff member like Brent confirm whether this is expected behavior?

Hi @r_donato,

Can you please post your code on how you create the overlay and these buttons associated with it? Please surround your code with “lua” tags for clarity in the forum thread:

[lua] -- code here [/lua]

Thanks,

Brent

[lua]

— main.lua

local composer = require(‘composer’)

composer.gotoScene(‘scene1’)

— scene1.lua

local composer = require(‘composer’)
local scene = composer.newScene()
local widget = require(‘widget’)

function scene:create(event)
    local bg = display.newRect(0, 0, 320, 480)
    bg.anchorX, bg.anchorY = 0, 0
    bg.fill = { 1, 0, 0 } – Red background

    local function onTap(event)
        print('scene1 click detected: ', event.name, event.phase, event.x, event.y)
    end
    bg:addEventListener(‘tap’, onTap)

    local function showOverlayClicked(event)
        – isModal so events should not propagate to scene1
        composer.showOverlay(‘scene2’, { isModal = true })
    end

    local showOverlayButton = widget.newButton({
        x = 160,
        y = 240,
        label = ‘Show overlay’,
        fontSize = 20,
        shape = ‘roundedRect’,
        width = 160,
        height = 30,
        fillColor = { default = {0, 1, 0}, over = {0, 0, 1} },
        onRelease = showOverlayClicked
    })
end

scene:addEventListener(‘create’, scene)
return scene

— scene2.lua

local composer = require(‘composer’)
local scene = composer.newScene()

function scene:create(event)
    print(‘in scene2’)
    local bg = display.newRect(160, 120, 160, 240)
    bg.fill = { 1, 1, 1 } – White background
end

scene:addEventListener(‘create’ , scene)
return scene

[/lua]

When I run the above code in the simulator, I can click the red background and see a print() of the event. Those print()s keep happening even if the overlay is showing. All the events are passing through to the scene underneath, whether or not I click the white background that belongs to the overlay.

Hi, I don’t see the buttons you’re talking about anywhere in your code? However, I think that the problem might be that you’re not properly adding your display objects to your scenes’ views. At least I can’t see you doing it anywhere in the code posted. I’ve experienced this myself, and making sure that all display object are inserted correctly into self.view solved it for me.

Like this:

self.view:insert(aButtonOrWhatever)

Hi Markus, you’re right on that! I did not realize that insert()ing into the scene view was needed for the overlay to work. Thanks for that. However, this does NOT fully solve the problem.

I updated the code with insert()s and a Hide Overlay button.

[lua]

— main.lua

local composer = require(‘composer’)

composer.gotoScene(‘scene1’)

— scene1.lua

local composer = require(‘composer’)
local scene = composer.newScene()
local widget = require(‘widget’)

function scene:create(event)
    local bg = display.newRect(0, 0, 320, 480)
    bg.anchorX, bg.anchorY = 0, 0
    bg.fill = { 1, 0, 0 } – Red background
    self.view:insert(bg)

    local function onTap(event)
        print('scene1 click detected: ', event.name, event.phase, event.x, event.y)
    end
    bg:addEventListener(‘tap’, onTap)

    local function showOverlayClicked(event)
        – isModal so events should not propagate to scene1
        print(‘Show Overlay clicked’)
        composer.showOverlay(‘scene2’, { isModal = true })
    end

    local showOverlayButton = widget.newButton({
        x = 160,
        y = 240,
        label = ‘Show overlay’,
        fontSize = 20,
        shape = ‘roundedRect’,
        width = 160,
        height = 30,
        fillColor = { default = {0, 1, 0}, over = {0, 0, 1} },
        onRelease = showOverlayClicked
    })
    self.view:insert(showOverlayButton)
end

scene:addEventListener(‘create’, scene)
return scene

— scene2.lua

local composer = require(‘composer’)
local scene = composer.newScene()
local widget = require(‘widget’)

function scene:create(event)
    print(‘in scene2’)
    local bg = display.newRect(160, 120, 160, 240)
    bg.fill = { 1, 1, 1 } – White background
    self.view:insert(bg)
    
    local function hideOverlayClicked(event)
        print(‘Hide Overlay clicked’)
        composer.hideOverlay()
    end

    local hideOverlayButton = widget.newButton({
        x = 160,
        y = 225,
        label = ‘Hide overlay’,
        fontSize = 20,
        shape = ‘roundedRect’,
        width = 160,
        height = 30,
        fillColor = { default = {0, 0, 0}, over = {0, 0, 1} },
        onRelease = hideOverlayClicked
    })
    self.view:insert(hideOverlayButton)
end

scene:addEventListener(‘create’ , scene)
return scene

[/lua]

In this code, when the overlay is shown, nothing passes through to the scene underneath unless you click Hide Overlay. If you click Hide Overlay, you see printed:

10:09:19.644  Hide Overlay clicked
10:09:19.644  scene1 click detected:     tap    nil    227    236

Any idea on why? If I wrap composer.hideOverlay in a timer (of even 1 millisecond) the problem goes away.

I’m not sure, but you should always return true from your event listener if you want to stop the event from propagating.

local function hideOverlayClicked(event) print('Hide Overlay clicked') composer.hideOverlay() return true end

Good point, Markus. Adding “return true” did not solve the problem, so I changed the print()s to get more details:

[lua]

        print('Show Overlay clicked: ', event.name, event.phase, event.x, event.y)
        print('Hide Overlay clicked: ', event.name, event.phase, event.x, event.y)
[/lua]

This gave me the following outputs:

11:04:44.527  Hide Overlay clicked:     touch    ended    105    230
11:04:44.527  scene1 click detected:     tap    nil    105    230
 

So there are two different events. The overlay button receives a “touch”, then a “tap” is sent to the scene beneath. That is the same behavior described in this tutorial: https://coronalabs.com/blog/2013/10/01/tutorial-taptouch-anatomy/

The button listeners (onPress, onRelease, onEvent) only seem to respond to “touch” events. That’s why returning true doesn’t solve the problem; it’s a different (“tap”) event being sent to the scene below.

I tried manually adding a “tap” listener to the Hide Overlay button:

[lua]

    local function hideOverlayTapped(event)
        print('Hide Overlay clicked: ', event.name, event.phase, event.x, event.y)
        return true
    end
    hideOverlayButton:addEventListener(‘tap’, hideOverlayTapped)

[/lua]

But that did not solve the problem. If it receives a tap event, it still passes it through to the scene underneath, even though the listener includes a “return true”.

Is adding a timer delay the only way to avoid passing events to the scene underneath?

I tried running your code, and just like you described it I couldn’t get it to work without adding a small delay before hiding the overlay.

Seems to be a problem with the widget button. When I tried replacing the button with a rectangle and a normal tap listener everything worked as expected.

Good thought, I hadn’t considered changing to a rectangle.

Can a Staff member like Brent confirm whether this is expected behavior?