After disabling/enabling UI action map button requiring two clicks

I enable/disable action maps depending on what input mode the player is in and it’s been a really awesome way to approach input. Part of this is I’ll disable the UI action map if I want to keep the player from interacting with UI.

However, once I reenable the action map it seems that the first click is ignored before working as normal again. Is there some warm up function or something that needs to be called?

Easy reproduction:

  1. Create scene with button and new input system
  2. Hook button up to log message (or something)
  3. Create way to disable and then enable the action map
  4. Note that the first click is ignored for OnClick (not ignored for Select though!)

Here’s a simple file I used to reproduce it.

using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.UI;

public class TestInputModule : MonoBehaviour
{
    private void Update()
    {
        if (Keyboard.current.spaceKey.wasPressedThisFrame)
        {
            InputSystemUIInputModule module = (InputSystemUIInputModule) EventSystem.current.currentInputModule;

            if (module.actionsAsset.enabled)
            {
                module.actionsAsset.Disable();
                Debug.Log("Disabling");
            }
            else
            {
                module.actionsAsset.Enable();
                Debug.Log("Enabling");
            }
        }
    }

    // Hooked up in the inspector on the button
    public void ButtonPressed()
    {
        Debug.Log("Button Pressed!");
    }
}

Input System Version: 1.2.0
Unity Version: 2020.3.17f1

It’s because when the leftClick action gets disabled it sends a ‘Canceled’ state to all registered callbacks. The Input Module then sets in its internal PointerModel.Button state to ‘ignoreNextClick’. The problem is that when the action is reenabled that bool is still set to true so you have to get through it in order to wipe it.

I can’t find a good way to clear the flag so this seems like a bug?

Attempted to enable/disable the EventSystem as a work around but a click while the EventSystem is disabled will be processed once it is immediately enabled (which also seems like a bug?) by the Input Module.

The last work around I can think of is to enabled a screen wide raycast target when I want to “disable” UI interactions and leave the EventSystem, InputModule, and InputActionMap alone

EDIT This will prevent the mouse from interacting but not buttons, so really what I want is still to disable the ui action map but it’s got the odd behavior :slight_smile:

I have a work around that appears to be working pretty alright but I’d love to still hear if there’s a better approach that I should be taking or if this is a bug (feels like unintended behavior to me)

The workaround:

InputSystemUIInputModule adds callbacks to each of the actions in the actionAsset. For clicks if the InputAction.CallbackContext.canceled is true then the module will ignore the next click. I have (currently) found two scenarios in which canceled is true. The first one is if the action map is disabled. This is a weird behavior I hit as it then requires two clicks once the action is reenabled. The second scenario is if the application loses focus. This scenario I think makes a lot of sense to ignore the next click as it’ll be when we give focus back to the application. As far as I could quickly tell I couldn’t see a way to distinguish between these two contexts.

The workaround I decided to go with is to enable/disable the input module itself instead of playing with the action map. For one the module seems to want to control the action’s enableness anyways (it turns then on/off during OnEnable/OnDisable) and it was the easiest way for me to figure out how to make a quick change to get it to do what I wanted.

The workaround requires a slight code change to InputSystemUIInputModule during its OnDisable function. It was changed to the following:

protected override void OnDisable()
{
    base.OnDisable();

    InputActionState.s_GlobalState.onActionControlsChanged.RemoveCallback(m_OnControlsChangedDelegate);

    DisableAllActions();
    UnhookActions();

    // ADDED BELOW
    // Disabling the button states AFTER we disable the actions
    // Will there be unexpected side effects?
    //
    // This isn't really what I want, I want to just disable the action map and leave
    // the module running but the disabling the action map causes the module to think it has lost
    // focus and ignore the next click
    for (int i = 0; i < m_PointerStates.length; i++)
    {
        PointerModel state = m_PointerStates[i];
        state = new PointerModel(state.eventData);

        m_PointerStates[i] = state;
    }
}

Basically I wipe the pointer data if the InputModule is disabled. It’s a pretty complex system so I have no idea if there are implications to this change but my current tests show it’s behaving ok. I still think disabling actions should behave in a more expected way.

I encountered the same issue and fixed it by upgrading to version 1.3.0 of InputSystem. This isn’t an official release so you have to update the manifest.json file.

3 Likes