SubscribeToOnInputForScreen

Tracking Issue: #501

Tags: ui

Mods may want to intercept mouse/keyboard/controller input on certain screens and instead run their own code. For example, the Highlander adds a text to the main menu that has small pop-up accessible by pressing the right controller stick.

The API consists of a delegate definition and two functions:

delegate bool CHOnInputDelegateImproved(UIScreen Screen, int iInput, int ActionMask);
function SubscribeToOnInputForScreen(UIScreen Screen, delegate<CHOnInputDelegateImproved> Callback);
function UnsubscribeFromOnInputForScreen(UIScreen Screen, delegate<CHOnInputDelegateImproved> Callback);

In a nutshell, with SubscribeToOnInputForScreen you ask the UIScreenStack "when screen Screen would receive input, ask me first". The CHOnInputDelegateImproved delegate defines the signature of the callback function called when the targeted screen would receive input.

Your function will be called with three arguments: The screen that would have received the input (Screen), the button that was pressed (iInput), and the action that occured (ActionMask, button press/release). The button and action are numeric values that correspond to constants in UIUtilities_Input.uc. If your function returns true, the ScreenStack will consider the input handled and immediately stop processing the input event. If your function returns false, the ScreenStack will continue calling other subscribers and, if unhandled, will finally notify the screen itself.

You can manually unsubscribe from receiving input, but this is generally not necessary as your callback will only be called when the screen would have received input and will automatically be unsubscribed upon removal of the targeted screen.

The following simplified example is taken from Covert Infiltration:

class UIListener_Mission extends UIScreenListener;

event OnInit (UIScreen Screen)
{
    local UIMission MissionScreen;

    MissionScreen = UIMission(Screen);
    if (MissionScreen == none) return;

    // This is a UIMission screen, register
    MissionScreen.Movie.Stack.SubscribeToOnInputForScreen(MissionScreen, OnMissionScreenInput);
}

simulated protected function bool OnMissionScreenInput (UIScreen Screen, int iInput, int ActionMask)
{
    if (!Screen.CheckInputIsReleaseOrDirectionRepeat(iInput, ActionMask))
    {
        return false;
    }

    switch (iInput)
    {
    case class'UIUtilities_Input'.const.FXS_BUTTON_RTRIGGER:
        // The right controller trigger was just released, show custom screen
        // ...
        // Tell the ScreenStack that this input was handled
        return true;
        break;
    }

    return false;
}

CheckInputIsReleaseOrDirectionRepeat ensures that the button was just released (or, if directional button, held for a long time), making input behavior more consistent with base game screens.

Although all mouse events can be inspected, Flash usually provides its own handlers that run even if the callback indicates to the ScreenStack that the input was handled. As a result, the only mouse event that can reliably be stopped with SubscribeToOnInputForScreen is the already navigation-relevant right click.

This feature is a more convenient version of SubscribeToOnInput, which receives events for any screen and has to be manually unsubscribed. SubscribeToOnInput offers lower-level interaction with the input system at the cost of ergonomics.

Source code references