Inconsistent input handling/sinking for buttons and active controls

Bug Summary
Buttons and .Active GuiObjects do not sink mouse and touch control input consistently:

  • gameProcessedEvent is false for an inactive Button with touch controls yet true for an inactive Button with mouse controls
  • InputBegan and InputEnded do not fire/refrain from firing for non-top-most Buttons or .Active GuiObjects consistently (see the Observable Behaviour from Script section below)
  • InputChanged always fires, which is inconsistent compared to InputBegan/InputEnded

Recreation Steps

Put the following code in a LocalScript in the StarterGui in an otherwise blank place and play solo, then click and drag where the buttons overlap using the mouse or touch controls.

local active, guiType = false, "TextButton" -- configure these to explore different cases
local screenGui = Instance.new("ScreenGui")
screenGui.Parent = script.Parent
local function new(y)
    local b = Instance.new(guiType)
    b.Active = active
    b.Size = UDim2.new(0, 200, 0, 100)
    b.Position = UDim2.new(0, 0, 0, 67 * (y-1))
    b.Parent = screenGui
    b.InputBegan:Connect(function() print("b" .. y .. ".InputBegan") end)
    b.InputChanged:Connect(function() print("b" .. y .. ".InputChanged") end)
    b.InputEnded:Connect(function() print("b" .. y .. ".InputEnded") end)
    return b
end
local b1, b2 = new(1), new(2)
local UserInputService = game:GetService("UserInputService")
UserInputService.InputBegan:Connect(function(input, gameProcessedEvent)
    print("UIS.InputBegan | Processed:", gameProcessedEvent)
end)
game:GetService("UserInputService").InputChanged:Connect(function(input, gameProcessedEvent)
    print("UIS.InputChanged | Processed:", gameProcessedEvent)
end)
game:GetService("UserInputService").InputEnded:Connect(function(input, gameProcessedEvent)
    print("UIS.InputEnded | Processed:", gameProcessedEvent)
end)

Observable Behaviour from Script

Results of clicking on the overlap between the buttons and dragging:

(UserInputService's InputBegan/Changed/Ended events always fire)
TextButtons:
    active = false:
        touch controls:
            gameProcessedEvent is false
            all events fire for both buttons
        mouse controls:
            gameProcessedEvent is true for InputBegan and InputEnded but false for InputChanged
            only the button on top fires for InputBegan and InputEnded, but InputChanged fires for both
    active = true:
        touch controls:
            gameProcessedEvent is true
            only the button on top fires for InputBegan and InputEnded, but InputChanged fires for both
        mouse controls:
            gameProcessedEvent is true
            only the button on top fires for InputBegan and InputEnded, but InputChanged fires for both
Frames:
    active = false:
        touch controls:
            gameProcessedEvent is false
            all events fire for both frames
        mouse controls:
            gameProcessedEvent is false
            all events fire for both frames
    active = true:
        touch controls:
            gameProcessedEvent is true
            only the button on top fires for InputBegan and InputEnded, but InputChanged fires for both
        mouse controls:
            gameProcessedEvent is true
            all events fire for both frames

Tested TextButtons in Play Solo, Test Local Server, and Online modes

Expected Behaviour

  • Consistency between mouse and touch controls
  • Consistency between InputBegan/InputEnded and InputChanged
  • For overlapping GuiObjects, the top .Active GuiObject should either:
    • never prevent the other GuiObjects from firing InputBegan/Changed/Ended events (based on the Active page, where it says “The events InputBegan, InputChanged, and InputEnded work as normal no matter the value of this property.”)
    • always prevent the other GuiObjects from firing InputBegan/Changed/Ended events if its Active property is true (based on the GuiObject page where Active’s summary is “Determines whether a UI element sinks input”)

Benefits if only the top .Active GuiObject fires InputBegan/Changed/Ended events

The documentation isn’t clear whether the intended behaviour is for the top .Active GuiObject being the only one to fire InputBegan/Changed/Ended events. Reasons I think this behaviour is desirable:

  • Active frames could act as movable/resizable “windows”. Windows (like shop menus), popups, and confirmation messages could all easily be used/shown to the player without having to hide all guis that might overlap with whatever the player is actively interacting with. Further, the player could be allowed to resize any gui without concern that, while interacting with the top gui, the player will accidentally also click (or activate an on-hover event) on something beneath it
  • When a player hovers over two active buttons with AutoButtonColor enabled, Roblox would no longer change the colour of both buttons (making it look much more professional if the buttons are both at least partially visible).

Bug Age
I’m unsure if this was ever working as intended

Bug Report Limitations
I have only tested it on Windows (including with the in-studio touch device emulator) and have not tested:

  • controllers
  • GuiObject.Activated event
  • If the input was correctly prevented from going into the 3D world (ex into a ClickDetector)
2 Likes

Thanks for the report! We are aware of the issues between mouse and touch, but this discrepancy is relied upon in some games. We have plans to slowly migrate the behavior in the future, in the form of a toggle to fix the behavior, that at some point will be mandatory for all games.

1 Like