Skip to content

Advanced Gamepad controls

Artem Shpynov edited this page Jan 14, 2025 · 7 revisions

Content.GameControllerHoldCommand

This command may be used to define different commands to short and long press of gamepad button

Due to the Command may have only one parameter Array of 2 is used to specify Command and CommantParameter if needed:

Warning: Usage of commands require to use Binding Proxy (from Playnite namespace)

<Grid.Resources>
    <p:BindingProxy x:Key="MAIN_Commands" Data="{Binding}" />
    <p:BindingProxy x:Key="THEMEOPTIONS_Commands" Data="{Binding Content, ElementName=ThemeOptions_Command}" />
</Grid.Resources>
<Grid.InputBindings>
    <pin:GameControllerInputBinding
        Button="Back"
        Command="{Binding Content.GameControllerHoldCommand, ElementName=ThemeOptions_Command}">
        <pin:GameControllerInputBinding.CommandParameter>
            <x:Array Type="{x:Type Button}">
                <Button
                    Command="{Binding Data.OpenMainMenuCommand, Source={StaticResource MAIN_Commands}}"
                />
                <Button
                    Command="{Binding Data.TouchTag, Source={StaticResource THEMEOPTIONS_Commands}}"
                    CommandParameter="FILTER_Show"/>
            </x:Array>
        </pin:GameControllerInputBinding.CommandParameter>
    </pin:GameControllerInputBinding>
<Grid.InputBindings>

Content.GameControllerAltCommand

Command may be used to define Gamepad binding only act on short button press (on KeyUp event). This is mandatory requirement if you would like to assign alternative binding using some button as modifier (button holding), but keep some action on short press.

CommandParameter may be either IComand action to be called if you don't need any parameter otherwise you may wrap command into Button object and specify both Command and CommandParameter:

<Grid.Resources>
    <p:BindingProxy x:Key="THEMEOPTIONS_Commands" Data="{Binding Content, ElementName=ThemeOptions_Command}" />
</Grid.Resources>
<Grid.InputBindings>
    <pin:GameControllerInputBinding
        Button="Back"
        Command="{Binding Data.GameControllerAltCommand, Source={StaticResource THEMEOPTIONS_Commands}}"
        CommandParameter="{Binding Data.TouchTag, Source={StaticResource THEMEOPTIONS_Commands}}" />
    </pin:GameControllerInputBinding>
<Grid.InputBindings>

or

<Grid.Resources>
    <p:BindingProxy x:Key="THEMEOPTIONS_Commands" Data="{Binding Content, ElementName=ThemeOptions_Command}" />
</Grid.Resources>
<Grid.InputBindings>
    <pin:GameControllerInputBinding
        Button="Back"
        Command="{Binding Content.GameControllerAltCommand, ElementName=ThemeOptions_Command}">
        <pin:GameControllerInputBinding.CommandParameter>
            <Button
                Command="{Binding Data.TouchTag, Source={StaticResource THEMEOPTIONS_Commands}}"
                CommandParameter="FILTER_Show"/>
        </pin:GameControllerInputBinding.CommandParameter>
    </pin:GameControllerInputBinding>
<Grid.InputBindings>

Current Gamepad state information

ThemeOptions plugin proxify current gamepad state using PluginSettings under GamepadState object. It is contain boolean value indicating is specified button is pressed or not:

Tag="{PluginSettings Plugin=ThemeOptions, Path=GamepadState.Back}">

The 'Back' here is one of gamepad buttons name.

Here the list of buttons name:

  • Start (Menu on XBox terms),
  • Back (View in XBox terms),
  • LeftStick,
  • RightStick,
  • LeftShoulder,
  • RightShoulder,
  • Guide,
  • A,
  • B,
  • X,
  • Y,
  • DPadLeft,
  • DPadRight,
  • DPadUp,
  • DPadDown,
  • TriggerLeft,
  • TriggerRight,
  • LeftStickLeft,
  • LeftStickRight,
  • LeftStickUp,
  • LeftStickDown,
  • RightStickLeft,
  • RightStickRight,
  • RightStickUp,
  • RightStickDown

Alternative gamepad mapping

Theme options implement 4 controls:

  • ThemeOptions_GamepadAdd
  • ThemeOptions_GamepadAlt
  • ThemeOptions_GamepadAddGlobal
  • ThemeOptions_GamepadAltGlobal

to specify and redefine controller bindings.

Difference is that 'Add' series call default processing if Control itself does not have specific bind, 'Alt' don't.

'Global' series does not check is owner window has focus within or not (you should define your logic in trigger, otherwise redefinition may have side effects). Otherwise, control will be deactivated if host window lost focus. And activated if Tag specified and Keyboard somewhere within its owner window. For example if 'Main menu' is opened - The Main control deactivated and control will be also.

By default binding is disabled. You had to specify Tag={True} or Tag={StaticResource someInputBindingCollectionName} if you would like to activate it.

It can be used to define 'Chord' bindings like 'Back + Y' to specify some additional command.

To implement it you had decide which button you like to use as trigger. Let it be 'Back' (main menu button). First stage will be define original behavior on short press (and disable it on hold) using GameControllerAltCommand.

<Grid.InputBindings>
    <pin:GameControllerInputBinding
        Button="Back"
        Command="{Binding Content.GameControllerAltCommand, ElementName=ThemeOptions_Command}"
        CommandParameter="{Binding Data.OpenMainMenuCommand, Source={StaticResource MAIN_Commands}}">
    </pin:GameControllerInputBinding>
</Grid.InputBindings>

Then you need to create ContentControl to hold alt binding, and activate it in case if 'Back' button is pressed. Also to prevent double call for ThemeOptions_GamepadAdd you also need to disable command on trigger button.

<ContentControl
    x:Name="ThemeOptions_GamepadAdd"
    Tag="{PluginSettings Plugin=ThemeOptions, Path=GamepadState.Back}">
    <ContentControl.InputBindings>
        <pin:GameControllerInputBinding
            Button="Y"
            Command="{Binding Data.Toggle, Source={StaticResource THEMEOPTIONS_Commands}}"
            CommandParameter="ToggleViewButton"
        />
        <pin:GameControllerInputBinding
            Button="Back"
            Command="{x:Null}"
        />
    </ContentControl.InputBindings>
</ContentControl>

Multiple binding collection swappable by Data trigger.

Second option to create 'Chords' is using Input binding collections. To do it:

  1. Define collections as resources of ContentControl Style
  2. Define DataTrigger and Set Tags to desired collection on Trigger:
<ContentControl x:Name="ThemeOptions_GamepadAdd">
    <ContentControl.Style>
        <Style TargetType="ContentControl">
        <Style.Resources>
            <InputBindingCollection x:Key="PrimaryBindings">
                <pin:GameControllerInputBinding
                    Button="Start"
                    Command="{Binding Data.GameControllerAltCommand, Source={StaticResource THEMEOPTIONS_Commands}}"
                    CommandParameter="{Binding Data.OpenMainMenuCommand, Source={StaticResource MAIN_Commands}}" />
                <pin:GameControllerInputBinding
                    Button="Back"
                    Command="{x:Null}"
                />
            </InputBindingCollection>
            <InputBindingCollection x:Key="SecondaryBindings">
                <pin:GameControllerInputBinding
                    Button="Start"
                    Command="{Binding Data.Toggle, Source={StaticResource THEMEOPTIONS_Commands}}"
                    CommandParameter="ToggleViewButton"
                />
                <pin:GameControllerInputBinding
                    Button="Back"
                    Command="{x:Null}"
                />
            </InputBindingCollection>
        </Style.Resources>
            <Setter Property="Tag" Value="{StaticResource PrimaryBindings}" />
            <Style.Triggers>
                <DataTrigger Binding="{PluginSettings Plugin=ThemeOptions, Path=GamepadState.Back}" Value="True">
                    <Setter Property="Tag" Value="{StaticResource SecondaryBindings}" />
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </ContentControl.Style>
</ContentControl>

Clone this wiki locally