using System;
using UnityEngine.InputSystem.Layouts;
using UnityEngine.InputSystem.Utilities;
namespace UnityEngine.InputSystem
{
///
/// Methods to change the setup of , ,
/// and objects.
///
///
/// Unlike the methods in , the methods here are
/// generally destructive, i.e. they will rearrange the data for actions.
///
public static class InputActionSetupExtensions
{
///
/// Create an action map with the given name and add it to the asset.
///
/// Asset to add the action map to
/// Name to assign to the
/// The newly added action map.
/// is null or
/// An action map with the given
/// already exists in .
/// is null or empty.
public static InputActionMap AddActionMap(this InputActionAsset asset, string name)
{
if (asset == null)
throw new ArgumentNullException(nameof(asset));
if (string.IsNullOrEmpty(name))
throw new ArgumentNullException(nameof(name));
if (asset.FindActionMap(name) != null)
throw new InvalidOperationException(
$"An action map called '{name}' already exists in the asset");
var map = new InputActionMap(name);
map.GenerateId();
asset.AddActionMap(map);
return map;
}
///
/// Add an action map to the asset.
///
/// Asset to add the map to.
/// A named action map.
/// or is null.
/// has no name or asset already contains a
/// map with the same name.
///
public static void AddActionMap(this InputActionAsset asset, InputActionMap map)
{
if (asset == null)
throw new ArgumentNullException(nameof(asset));
if (map == null)
throw new ArgumentNullException(nameof(map));
if (string.IsNullOrEmpty(map.name))
throw new InvalidOperationException("Maps added to an input action asset must be named");
if (map.asset != null)
throw new InvalidOperationException(
$"Cannot add map '{map}' to asset '{asset}' as it has already been added to asset '{map.asset}'");
////REVIEW: some of the rules here seem stupid; just replace?
if (asset.FindActionMap(map.name) != null)
throw new InvalidOperationException(
$"An action map called '{map.name}' already exists in the asset");
ArrayHelpers.Append(ref asset.m_ActionMaps, map);
map.m_Asset = asset;
}
///
/// Remove the given action map from the asset.
///
/// Asset to add the action map to.
/// An action map. If the given map is not part of the asset, the method
/// does nothing.
/// or is null.
/// is currently enabled (see ).
///
///
public static void RemoveActionMap(this InputActionAsset asset, InputActionMap map)
{
if (asset == null)
throw new ArgumentNullException(nameof(asset));
if (map == null)
throw new ArgumentNullException(nameof(map));
if (map.enabled)
throw new InvalidOperationException("Cannot remove an action map from the asset while it is enabled");
// Ignore if not part of this asset.
if (map.m_Asset != asset)
return;
ArrayHelpers.Erase(ref asset.m_ActionMaps, map);
map.m_Asset = null;
}
///
/// Remove the action map with the given name or ID from the asset.
///
/// Asset to remove the action map from.
/// The name or ID (see ) of a map in the
/// asset. Note that lookup is case-insensitive. If no map with the given name or ID is found,
/// the method does nothing.
/// or is null.
/// The map referenced by is currently enabled
/// (see ).
///
///
public static void RemoveActionMap(this InputActionAsset asset, string nameOrId)
{
if (asset == null)
throw new ArgumentNullException(nameof(asset));
if (nameOrId == null)
throw new ArgumentNullException(nameof(nameOrId));
var map = asset.FindActionMap(nameOrId);
if (map != null)
asset.RemoveActionMap(map);
}
////TODO: add method to add an existing InputAction to a map
public static InputAction AddAction(this InputActionMap map, string name, InputActionType type = default, string binding = null,
string interactions = null, string processors = null, string groups = null, string expectedControlLayout = null)
{
if (map == null)
throw new ArgumentNullException(nameof(map));
if (string.IsNullOrEmpty(name))
throw new ArgumentException("Action must have name", nameof(name));
if (map.enabled)
throw new InvalidOperationException(
$"Cannot add action '{name}' to map '{map}' while it the map is enabled");
if (map.FindAction(name) != null)
throw new InvalidOperationException(
$"Cannot add action with duplicate name '{name}' to set '{map.name}'");
// Append action to array.
var action = new InputAction(name, type)
{
expectedControlType = expectedControlLayout
};
action.GenerateId();
ArrayHelpers.Append(ref map.m_Actions, action);
action.m_ActionMap = map;
////TODO: make sure we blast out existing action map state
// Add binding, if supplied.
if (!string.IsNullOrEmpty(binding))
{
action.AddBinding(binding, interactions: interactions, processors: processors, groups: groups);
}
else
{
if (!string.IsNullOrEmpty(groups))
throw new ArgumentException(
$"No binding path was specified for action '{action}' but groups was specified ('{groups}'); cannot apply groups without binding",
nameof(groups));
// If no binding has been supplied but there are interactions and processors, they go on the action itself.
action.m_Interactions = interactions;
action.m_Processors = processors;
}
return action;
}
///
/// Remove the given action from its .
///
/// An input action that is part of an .
/// is null.
/// is part of an
/// that has at least one enabled action -or- is a standalone action
/// that is not part of an and thus cannot be removed from anything.
///
/// After removal, the action's will be set to null
/// and the action will effectively become a standalone action that is not associated with
/// any action map. Bindings on the action will be preserved. On the action map, the bindings
/// for the action will be removed.
///
///
public static void RemoveAction(this InputAction action)
{
if (action == null)
throw new ArgumentNullException(nameof(action));
var actionMap = action.actionMap;
if (actionMap == null)
throw new ArgumentException(
$"Action '{action}' does not belong to an action map; nowhere to remove from", nameof(action));
if (actionMap.enabled)
throw new ArgumentException($"Cannot remove action '{action}' while its action map is enabled");
var bindingsForAction = action.bindings.ToArray();
var index = ArrayHelpers.IndexOfReference(actionMap.m_Actions, action);
Debug.Assert(index != -1, "Could not find action in map");
ArrayHelpers.EraseAt(ref actionMap.m_Actions, index);
action.m_ActionMap = null;
action.m_SingletonActionBindings = bindingsForAction;
actionMap.ClearPerActionCachedBindingData();
// Remove bindings to action from map.
var newActionMapBindingCount = actionMap.m_Bindings.Length - bindingsForAction.Length;
if (newActionMapBindingCount == 0)
actionMap.m_Bindings = null;
else
{
var newActionMapBindings = new InputBinding[newActionMapBindingCount];
var oldActionMapBindings = actionMap.m_Bindings;
var bindingIndex = 0;
for (var i = 0; i < oldActionMapBindings.Length; ++i)
{
var binding = oldActionMapBindings[i];
if (bindingsForAction.IndexOf(b => b == binding) == -1)
newActionMapBindings[bindingIndex++] = binding;
}
actionMap.m_Bindings = newActionMapBindings;
}
}
///
/// Remove the action with the given name from the asset.
///
/// Asset to remove the action from.
/// Name or ID of the action. See for
/// details.
/// is null -or-
/// is null or empty.
///
public static void RemoveAction(this InputActionAsset asset, string nameOrId)
{
if (asset == null)
throw new ArgumentNullException(nameof(asset));
if (nameOrId == null)
throw new ArgumentNullException(nameof(nameOrId));
var action = asset.FindAction(nameOrId);
action?.RemoveAction();
}
///
/// Add a new binding to the given action.
///
/// Action to add the binding to. If the action is part of an ,
/// the newly added binding will be visible on .
/// Binding path string. See for details.
/// Optional list of interactions to apply to the binding. See for details.
/// Optional list of processors to apply to the binding. See for details.
/// Optional list of binding groups that should be assigned to the binding. See
/// for details.
/// Fluent-style syntax to further configure the binding.
public static BindingSyntax AddBinding(this InputAction action, string path, string interactions = null,
string processors = null, string groups = null)
{
return AddBinding(action, new InputBinding
{
path = path,
interactions = interactions,
processors = processors,
groups = groups
});
}
///
/// Add a binding that references the given and triggers
/// the given .
///
/// Action to trigger.
/// Control to bind to. The full of the control will
/// be used in the resulting binding.
/// Syntax to configure the binding further.
/// is null or is null.
///
public static BindingSyntax AddBinding(this InputAction action, InputControl control)
{
if (control == null)
throw new ArgumentNullException(nameof(control));
return AddBinding(action, control.path);
}
///
/// Add a new binding to the action.
///
/// An action to add the binding to.
/// Binding to add to the action or default. Binding can be further configured via
/// the struct returned by the method.
///
/// Returns a fluent-style syntax structure that allows performing additional modifications
/// based on the new binding.
///
///
/// This works both with actions that are part of an action set as well as with actions that aren't.
///
/// Note that actions must be disabled while altering their binding sets. Also, if the action belongs
/// to a set, all actions in the set must be disabled.
///
///
///
/// fireAction.AddBinding()
/// .WithPath("<Gamepad>/buttonSouth")
/// .WithGroup("Gamepad");
///
///
///
public static BindingSyntax AddBinding(this InputAction action, InputBinding binding = default)
{
if (action == null)
throw new ArgumentNullException(nameof(action));
////REVIEW: should this reference actions by ID?
Debug.Assert(action.m_Name != null || action.isSingletonAction);
binding.action = action.name;
var actionMap = action.GetOrCreateActionMap();
var bindingIndex = AddBindingInternal(actionMap, binding);
return new BindingSyntax(actionMap, action, bindingIndex);
}
public static BindingSyntax AddBinding(this InputActionMap actionMap, string path,
string interactions = null, string groups = null, string action = null)
{
if (path == null)
throw new ArgumentException("Binding path cannot be null", nameof(path));
return AddBinding(actionMap, new InputBinding
{
path = path,
interactions = interactions,
groups = groups,
action = action
});
}
public static BindingSyntax AddBinding(this InputActionMap actionMap, string path, InputAction action,
string interactions = null, string groups = null)
{
if (action != null && action.actionMap != actionMap)
throw new ArgumentException(
$"Action '{action}' is not part of action map '{actionMap}'", nameof(action));
if (action == null)
return AddBinding(actionMap, path: path, interactions: interactions, groups: groups);
return AddBinding(actionMap, path: path, interactions: interactions, groups: groups,
action: action.id);
}
public static BindingSyntax AddBinding(this InputActionMap actionMap, string path, Guid action,
string interactions = null, string groups = null)
{
if (action == Guid.Empty)
return AddBinding(actionMap, path: path, interactions: interactions, groups: groups);
return AddBinding(actionMap, path: path, interactions: interactions, groups: groups,
action: action.ToString());
}
public static BindingSyntax AddBinding(this InputActionMap actionMap, InputBinding binding)
{
if (actionMap == null)
throw new ArgumentNullException(nameof(actionMap));
if (binding.path == null)
throw new ArgumentException("Binding path cannot be null", nameof(binding));
var bindingIndex = AddBindingInternal(actionMap, binding);
return new BindingSyntax(actionMap, null, bindingIndex);
}
public static CompositeSyntax AddCompositeBinding(this InputAction action, string composite, string interactions = null, string processors = null)
{
if (action == null)
throw new ArgumentNullException(nameof(action));
if (string.IsNullOrEmpty(composite))
throw new ArgumentException("Composite name cannot be null or empty", nameof(composite));
var actionMap = action.GetOrCreateActionMap();
////REVIEW: use 'name' instead of 'path' field here?
var binding = new InputBinding {path = composite, interactions = interactions, processors = processors, isComposite = true, action = action.name};
var bindingIndex = AddBindingInternal(actionMap, binding);
return new CompositeSyntax(actionMap, action, bindingIndex);
}
private static int AddBindingInternal(InputActionMap map, InputBinding binding)
{
Debug.Assert(map != null);
// Make sure the binding has an ID.
if (string.IsNullOrEmpty(binding.m_Id))
binding.GenerateId();
// Append to bindings in set.
var bindingIndex = ArrayHelpers.Append(ref map.m_Bindings, binding);
// Invalidate per-action binding sets so that this gets refreshed if
// anyone queries it.
map.ClearPerActionCachedBindingData();
// Make sure bindings get re-resolved.
map.LazyResolveBindings();
// If we're looking at a singleton action, make sure m_Bindings is up to date just
// in case the action gets serialized.
if (map.m_SingletonAction != null)
map.m_SingletonAction.m_SingletonActionBindings = map.m_Bindings;
return bindingIndex;
}
public static BindingSyntax ChangeBinding(this InputAction action, int index)
{
if (action == null)
throw new ArgumentNullException(nameof(action));
var indexOnMap = action.BindingIndexOnActionToBindingIndexOnMap(index);
return new BindingSyntax(action.GetOrCreateActionMap(), action, indexOnMap);
}
public static BindingSyntax ChangeBindingWithId(this InputAction action, string id)
{
return action.ChangeBinding(new InputBinding {m_Id = id});
}
public static BindingSyntax ChangeBindingWithId(this InputAction action, Guid id)
{
return action.ChangeBinding(new InputBinding {id = id});
}
public static BindingSyntax ChangeBindingWithGroup(this InputAction action, string group)
{
return action.ChangeBinding(new InputBinding {groups = group});
}
public static BindingSyntax ChangeBindingWithPath(this InputAction action, string path)
{
return action.ChangeBinding(new InputBinding {path = path});
}
public static BindingSyntax ChangeBinding(this InputAction action, InputBinding match)
{
if (action == null)
throw new ArgumentNullException(nameof(action));
var actionMap = action.GetOrCreateActionMap();
var bindingIndex = actionMap.FindBinding(match);
if (bindingIndex == -1)
throw new ArgumentException($"Cannot find binding matching '{match}' in '{action}'", nameof(match));
return new BindingSyntax(actionMap, action, bindingIndex);
}
////TODO: update binding mask if necessary
///
/// Rename an existing action.
///
/// Action to assign a new name to. Can be singleton action or action that
/// is part of a map.
/// New name to assign to action. Cannot be empty.
/// is null or is
/// null or empty.
/// of
/// already contains an action called .
///
/// Renaming an action will also update the bindings that refer to the action.
///
public static void Rename(this InputAction action, string newName)
{
if (action == null)
throw new ArgumentNullException(nameof(action));
if (string.IsNullOrEmpty(newName))
throw new ArgumentNullException(nameof(newName));
if (action.name == newName)
return;
// Make sure name isn't already taken in map.
var actionMap = action.actionMap;
if (actionMap?.FindAction(newName) != null)
throw new InvalidOperationException(
$"Cannot rename '{action}' to '{newName}' in map '{actionMap}' as the map already contains an action with that name");
var oldName = action.m_Name;
action.m_Name = newName;
// Update bindings.
var bindings = action.GetOrCreateActionMap().m_Bindings;
var bindingCount = bindings.LengthSafe();
for (var i = 0; i < bindingCount; ++i)
if (string.Compare(bindings[i].action, oldName, StringComparison.InvariantCultureIgnoreCase) == 0)
bindings[i].action = newName;
}
///
/// Add a new control scheme to the asset.
///
/// Asset to add the control scheme to.
/// Control scheme to add.
/// has no name.
/// is null.
/// A control scheme with the same name as
/// already exists in the asset.
///
///
public static void AddControlScheme(this InputActionAsset asset, InputControlScheme controlScheme)
{
if (asset == null)
throw new ArgumentNullException(nameof(asset));
if (string.IsNullOrEmpty(controlScheme.name))
throw new ArgumentException("Cannot add control scheme without name to asset " + asset.name, nameof(controlScheme));
if (asset.FindControlScheme(controlScheme.name) != null)
throw new InvalidOperationException(
$"Asset '{asset.name}' already contains a control scheme called '{controlScheme.name}'");
ArrayHelpers.Append(ref asset.m_ControlSchemes, controlScheme);
}
///
/// Add a new control scheme to the given .
///
/// Asset to add the control scheme to.
/// Name to give to the control scheme. Must be unique within the control schemes of the
/// asset. Also used as default name of binding group associated
/// with the control scheme.
/// Syntax to allow providing additional configuration for the newly added control scheme.
/// is null -or-
/// is null or empty.
public static ControlSchemeSyntax AddControlScheme(this InputActionAsset asset, string name)
{
if (asset == null)
throw new ArgumentNullException(nameof(asset));
if (string.IsNullOrEmpty(name))
throw new ArgumentNullException(nameof(name));
var index = asset.controlSchemes.Count;
asset.AddControlScheme(new InputControlScheme(name));
return new ControlSchemeSyntax(asset, index);
}
///
/// Remove the control scheme with the given name from the asset.
///
/// Asset to remove the control scheme from.
/// Name of the control scheme. Matching is case-insensitive.
/// is null -or-
/// is null or empty.
///
/// If no control scheme with the given name can be found, the method does nothing.
///
public static void RemoveControlScheme(this InputActionAsset asset, string name)
{
if (asset == null)
throw new ArgumentNullException(nameof(asset));
if (string.IsNullOrEmpty(name))
throw new ArgumentNullException(nameof(name));
var index = asset.FindControlSchemeIndex(name);
if (index != -1)
ArrayHelpers.EraseAt(ref asset.m_ControlSchemes, index);
}
public static InputControlScheme WithBindingGroup(this InputControlScheme scheme, string bindingGroup)
{
return new ControlSchemeSyntax(scheme).WithBindingGroup(bindingGroup).Done();
}
public static InputControlScheme WithRequiredDevice(this InputControlScheme scheme, string controlPath)
{
return new ControlSchemeSyntax(scheme).WithRequiredDevice(controlPath).Done();
}
public static InputControlScheme WithOptionalDevice(this InputControlScheme scheme, string controlPath)
{
return new ControlSchemeSyntax(scheme).WithOptionalDevice(controlPath).Done();
}
public static InputControlScheme OrWithRequiredDevice(this InputControlScheme scheme, string controlPath)
{
return new ControlSchemeSyntax(scheme).OrWithRequiredDevice(controlPath).Done();
}
public static InputControlScheme OrWithOptionalDevice(this InputControlScheme scheme, string controlPath)
{
return new ControlSchemeSyntax(scheme).OrWithOptionalDevice(controlPath).Done();
}
///
/// Syntax to configure a binding added to an or an
/// .
///
///
public struct BindingSyntax
{
private readonly InputActionMap m_ActionMap;
private readonly InputAction m_Action;
internal readonly int m_BindingIndex;
internal BindingSyntax(InputActionMap map, InputAction action, int bindingIndex)
{
m_ActionMap = map;
m_Action = action;
m_BindingIndex = bindingIndex;
}
///
/// Set the of the binding.
///
/// Name for the binding.
/// The same binding syntax for further configuration.
///
///
public BindingSyntax WithName(string name)
{
m_ActionMap.m_Bindings[m_BindingIndex].name = name;
m_ActionMap.ClearPerActionCachedBindingData();
m_ActionMap.LazyResolveBindings();
return this;
}
///
/// Set the of the binding.
///
/// Path for the binding.
/// The same binding syntax for further configuration.
///
public BindingSyntax WithPath(string path)
{
m_ActionMap.m_Bindings[m_BindingIndex].path = path;
m_ActionMap.ClearPerActionCachedBindingData();
m_ActionMap.LazyResolveBindings();
return this;
}
public BindingSyntax WithGroup(string group)
{
if (string.IsNullOrEmpty(group))
throw new ArgumentException("Group name cannot be null or empty", nameof(group));
if (group.IndexOf(InputBinding.Separator) != -1)
throw new ArgumentException(
$"Group name cannot contain separator character '{InputBinding.Separator}'", nameof(group));
return WithGroups(group);
}
public BindingSyntax WithGroups(string groups)
{
if (string.IsNullOrEmpty(groups))
return this;
// Join with existing group, if any.
var currentGroups = m_ActionMap.m_Bindings[m_BindingIndex].groups;
if (!string.IsNullOrEmpty(currentGroups))
groups = string.Join(InputBinding.kSeparatorString, currentGroups, groups);
// Set groups on binding.
m_ActionMap.m_Bindings[m_BindingIndex].groups = groups;
m_ActionMap.ClearPerActionCachedBindingData();
m_ActionMap.LazyResolveBindings();
return this;
}
public BindingSyntax WithInteraction(string interaction)
{
if (string.IsNullOrEmpty(interaction))
throw new ArgumentException("Interaction cannot be null or empty", nameof(interaction));
if (interaction.IndexOf(InputBinding.Separator) != -1)
throw new ArgumentException(
$"Interaction string cannot contain separator character '{InputBinding.Separator}'", nameof(interaction));
return WithInteractions(interaction);
}
public BindingSyntax WithInteractions(string interactions)
{
if (string.IsNullOrEmpty(interactions))
return this;
// Join with existing interaction string, if any.
var currentInteractions = m_ActionMap.m_Bindings[m_BindingIndex].interactions;
if (!string.IsNullOrEmpty(currentInteractions))
interactions = string.Join(InputBinding.kSeparatorString, currentInteractions, interactions);
// Set interactions on binding.
m_ActionMap.m_Bindings[m_BindingIndex].interactions = interactions;
m_ActionMap.ClearPerActionCachedBindingData();
m_ActionMap.LazyResolveBindings();
return this;
}
public BindingSyntax WithInteraction()
where TInteraction : IInputInteraction
{
var interactionName = InputProcessor.s_Processors.FindNameForType(typeof(TInteraction));
if (interactionName.IsEmpty())
throw new NotSupportedException($"Type '{typeof(TInteraction)}' has not been registered as a processor");
return WithInteraction(interactionName);
}
public BindingSyntax WithProcessor(string processor)
{
if (string.IsNullOrEmpty(processor))
throw new ArgumentException("Processor cannot be null or empty", nameof(processor));
if (processor.IndexOf(InputBinding.Separator) != -1)
throw new ArgumentException(
$"Interaction string cannot contain separator character '{InputBinding.Separator}'", nameof(processor));
return WithProcessors(processor);
}
public BindingSyntax WithProcessors(string processors)
{
if (string.IsNullOrEmpty(processors))
return this;
// Join with existing processor string, if any.
var currentProcessors = m_ActionMap.m_Bindings[m_BindingIndex].processors;
if (!string.IsNullOrEmpty(currentProcessors))
processors = string.Join(InputBinding.kSeparatorString, currentProcessors, processors);
// Set processors on binding.
m_ActionMap.m_Bindings[m_BindingIndex].processors = processors;
m_ActionMap.ClearPerActionCachedBindingData();
m_ActionMap.LazyResolveBindings();
return this;
}
public BindingSyntax WithProcessor()
{
var processorName = InputProcessor.s_Processors.FindNameForType(typeof(TProcessor));
if (processorName.IsEmpty())
throw new NotSupportedException($"Type '{typeof(TProcessor)}' has not been registered as a processor");
return WithProcessor(processorName);
}
public BindingSyntax Triggering(InputAction action)
{
if (action == null)
throw new ArgumentNullException(nameof(action));
if (action.isSingletonAction)
throw new ArgumentException(
$"Cannot change the action a binding triggers on singleton action '{action}'", nameof(action));
m_ActionMap.m_Bindings[m_BindingIndex].action = action.name;
m_ActionMap.ClearPerActionCachedBindingData();
m_ActionMap.LazyResolveBindings();
return this;
}
public BindingSyntax To(InputBinding binding)
{
m_ActionMap.m_Bindings[m_BindingIndex] = binding;
m_ActionMap.ClearPerActionCachedBindingData();
m_ActionMap.LazyResolveBindings();
// If it's a singleton action, we force the binding to stay with the action.
if (m_ActionMap.m_SingletonAction != null)
m_ActionMap.m_Bindings[m_BindingIndex].action = m_Action.name;
return this;
}
public void Erase()
{
ArrayHelpers.EraseAt(ref m_ActionMap.m_Bindings, m_BindingIndex);
m_ActionMap.ClearPerActionCachedBindingData();
m_ActionMap.LazyResolveBindings();
// We have switched to a different binding array. For singleton actions, we need to
// sync up the reference that the action itself has.
if (m_ActionMap.m_SingletonAction != null)
m_ActionMap.m_SingletonAction.m_SingletonActionBindings = m_ActionMap.m_Bindings;
}
internal BindingSyntax And => throw new NotImplementedException();
}
public struct CompositeSyntax
{
private readonly InputAction m_Action;
private readonly InputActionMap m_ActionMap;
private int m_CompositeIndex;
internal CompositeSyntax(InputActionMap map, InputAction action, int compositeIndex)
{
m_Action = action;
m_ActionMap = map;
m_CompositeIndex = compositeIndex;
}
public CompositeSyntax With(string name, string binding, string groups = null)
{
////TODO: check whether non-composite bindings have been added in-between
int bindingIndex;
if (m_Action != null)
bindingIndex = m_Action.AddBinding(path: binding, groups: groups)
.m_BindingIndex;
else
bindingIndex = m_ActionMap.AddBinding(path: binding, groups: groups)
.m_BindingIndex;
m_ActionMap.m_Bindings[bindingIndex].name = name;
m_ActionMap.m_Bindings[bindingIndex].isPartOfComposite = true;
return this;
}
}
public struct ControlSchemeSyntax
{
private readonly InputActionAsset m_Asset;
private readonly int m_ControlSchemeIndex;
private InputControlScheme m_ControlScheme;
internal ControlSchemeSyntax(InputActionAsset asset, int index)
{
m_Asset = asset;
m_ControlSchemeIndex = index;
m_ControlScheme = new InputControlScheme();
}
internal ControlSchemeSyntax(InputControlScheme controlScheme)
{
m_Asset = null;
m_ControlSchemeIndex = -1;
m_ControlScheme = controlScheme;
}
public ControlSchemeSyntax WithBindingGroup(string bindingGroup)
{
if (string.IsNullOrEmpty(bindingGroup))
throw new ArgumentNullException(nameof(bindingGroup));
if (m_Asset == null)
m_ControlScheme.m_BindingGroup = bindingGroup;
else
m_Asset.m_ControlSchemes[m_ControlSchemeIndex].bindingGroup = bindingGroup;
return this;
}
public ControlSchemeSyntax WithRequiredDevice()
where TDevice : InputDevice
{
return WithRequiredDevice(DeviceTypeToControlPath());
}
public ControlSchemeSyntax WithOptionalDevice()
where TDevice : InputDevice
{
return WithOptionalDevice(DeviceTypeToControlPath());
}
public ControlSchemeSyntax OrWithRequiredDevice()
where TDevice : InputDevice
{
return WithRequiredDevice(DeviceTypeToControlPath());
}
public ControlSchemeSyntax OrWithOptionalDevice()
where TDevice : InputDevice
{
return WithOptionalDevice(DeviceTypeToControlPath());
}
public ControlSchemeSyntax WithRequiredDevice(string controlPath)
{
AddDeviceEntry(controlPath, InputControlScheme.DeviceRequirement.Flags.None);
return this;
}
public ControlSchemeSyntax WithOptionalDevice(string controlPath)
{
AddDeviceEntry(controlPath, InputControlScheme.DeviceRequirement.Flags.Optional);
return this;
}
public ControlSchemeSyntax OrWithRequiredDevice(string controlPath)
{
AddDeviceEntry(controlPath, InputControlScheme.DeviceRequirement.Flags.Or);
return this;
}
public ControlSchemeSyntax OrWithOptionalDevice(string controlPath)
{
AddDeviceEntry(controlPath,
InputControlScheme.DeviceRequirement.Flags.Optional |
InputControlScheme.DeviceRequirement.Flags.Or);
return this;
}
private string DeviceTypeToControlPath()
where TDevice : InputDevice
{
var layoutName = InputControlLayout.s_Layouts.TryFindLayoutForType(typeof(TDevice)).ToString();
if (string.IsNullOrEmpty(layoutName))
layoutName = typeof(TDevice).Name;
return $"<{layoutName}>";
}
public InputControlScheme Done()
{
if (m_Asset != null)
return m_Asset.m_ControlSchemes[m_ControlSchemeIndex];
return m_ControlScheme;
}
private void AddDeviceEntry(string controlPath, InputControlScheme.DeviceRequirement.Flags flags)
{
if (string.IsNullOrEmpty(controlPath))
throw new ArgumentNullException(nameof(controlPath));
var scheme = m_Asset != null ? m_Asset.m_ControlSchemes[m_ControlSchemeIndex] : m_ControlScheme;
ArrayHelpers.Append(ref scheme.m_DeviceRequirements,
new InputControlScheme.DeviceRequirement
{
m_ControlPath = controlPath,
m_Flags = flags,
});
if (m_Asset == null)
m_ControlScheme = scheme;
else
m_Asset.m_ControlSchemes[m_ControlSchemeIndex] = scheme;
}
}
}
}