349 lines
11 KiB
C++
349 lines
11 KiB
C++
// Copyright Sam Bonifacio. All Rights Reserved.
|
|
|
|
#include "InputMappingGroup.h"
|
|
#include "KeyMappingTypes.h"
|
|
#include "Misc/AutoSettingsInputConfig.h"
|
|
#include "AutoSettingsError.h"
|
|
#include "Misc/AutoSettingsInputProjectConfig.h"
|
|
|
|
void FInputMappingGroup::SetConfig(TWeakInterfacePtr<const IAutoSettingsInputConfigInterface> InConfig)
|
|
{
|
|
Config = InConfig;
|
|
}
|
|
|
|
TArray<FInputActionKeyMapping> FInputMappingGroup::GetAllActions(FName ActionName, FGameplayTag KeyGroup) const
|
|
{
|
|
check(Config.IsValid());
|
|
|
|
const bool bAnyKeyGroup = !KeyGroup.IsValid();
|
|
|
|
if(!bAnyKeyGroup && !Config->IsKeyGroupDefined(KeyGroup))
|
|
{
|
|
FAutoSettingsError::LogError(FString::Printf(TEXT("Undefined Key Group with tag '%s'. Please add a Key Group with the tag in the Auto Settings page of the project settings."), *KeyGroup.ToString()));
|
|
}
|
|
|
|
TArray<FInputActionKeyMapping> Result;
|
|
for (FInputActionKeyMapping Action : ActionMappings)
|
|
{
|
|
if (Action.ActionName == ActionName && (bAnyKeyGroup || Config->GetKeyGroupOfKey(Action.Key) == KeyGroup))
|
|
{
|
|
Result.Add(Action);
|
|
}
|
|
}
|
|
return Result;
|
|
}
|
|
|
|
TArray<FInputAxisKeyMapping> FInputMappingGroup::GetAllAxes(FName AxisName, float Scale, FGameplayTag KeyGroup, bool bAnyScale) const
|
|
{
|
|
check(Config.IsValid());
|
|
|
|
const bool bAnyKeyGroup = !KeyGroup.IsValid();
|
|
|
|
if (!bAnyKeyGroup && !Config->IsKeyGroupDefined(KeyGroup))
|
|
{
|
|
FAutoSettingsError::LogError(FString::Printf(TEXT("Undefined Key Group with tag '%s'. Please add a Key Group with the tag in the Auto Settings page of the project settings."), *KeyGroup.ToString()));
|
|
}
|
|
|
|
TArray<FInputAxisKeyMapping> Result;
|
|
for (FInputAxisKeyMapping Axis : AxisMappings)
|
|
{
|
|
if (Axis.AxisName == AxisName && (bAnyScale || Axis.Scale == Scale || Config->IsAxisKey(Axis.Key)) && (bAnyKeyGroup || Config->GetKeyGroupOfKey(Axis.Key) == KeyGroup))
|
|
{
|
|
Result.Add(Axis);
|
|
}
|
|
}
|
|
return Result;
|
|
}
|
|
|
|
FInputActionKeyMapping FInputMappingGroup::GetAction(FName ActionName, FGameplayTag KeyGroup) const
|
|
{
|
|
TArray<FInputActionKeyMapping> Actions = GetAllActions(ActionName, KeyGroup);
|
|
if(Actions.Num() == 0)
|
|
{
|
|
return FInputActionKeyMapping();
|
|
}
|
|
// Return last, to prioritize mappings that were added more recently
|
|
return Actions.Last();
|
|
}
|
|
|
|
FInputAxisKeyMapping FInputMappingGroup::GetAxis(FName AxisName, float Scale, FGameplayTag KeyGroup) const
|
|
{
|
|
TArray<FInputAxisKeyMapping> Axes = GetAllAxes(AxisName, Scale, KeyGroup);
|
|
if(Axes.Num() == 0)
|
|
{
|
|
return FInputAxisKeyMapping();
|
|
}
|
|
// Return last, to prioritize mappings that were added more recently
|
|
return Axes.Last();
|
|
}
|
|
|
|
FInputMappingGroup FInputMappingGroup::ReplaceAction(FConfigActionKeyMapping Action, bool bAnyKeyGroup)
|
|
{
|
|
// Filter key group unless using any
|
|
const FGameplayTag KeyGroup = bAnyKeyGroup ? FGameplayTag() : Config->GetKeyGroupOfKey(Action.Key);
|
|
|
|
FInputMappingGroup MappingsToRemove = FInputMappingGroup(Config);
|
|
|
|
for (FInputActionKeyMapping& ExistingAction : GetAllActions(Action.ActionName, KeyGroup))
|
|
{
|
|
MappingsToRemove.ActionMappings.Add(ExistingAction);
|
|
}
|
|
|
|
RemoveMappings(MappingsToRemove);
|
|
ActionMappings.Add(FConfigActionKeyMapping(Action));
|
|
|
|
// Remove unbound mapping
|
|
RemoveAction(Action.ActionName, KeyGroup, true);
|
|
|
|
return MappingsToRemove.ToUnboundMappings();
|
|
}
|
|
|
|
FInputMappingGroup FInputMappingGroup::ReplaceAxis(FConfigAxisKeyMapping Axis, bool bAnyKeyGroup)
|
|
{
|
|
const bool bIsAxisKey = Config->IsAxisKey(Axis.Key);
|
|
|
|
// Filter key group unless using any
|
|
const FGameplayTag KeyGroup = bAnyKeyGroup ? FGameplayTag() : Config->GetKeyGroupOfKey(Axis.Key);
|
|
|
|
FInputMappingGroup MappingsToRemove = FInputMappingGroup(Config);
|
|
|
|
for (FInputAxisKeyMapping& ExistingAxis : GetAllAxes(Axis.AxisName, Axis.Scale, KeyGroup, bIsAxisKey))
|
|
{
|
|
MappingsToRemove.AxisMappings.Add(ExistingAxis);
|
|
}
|
|
|
|
RemoveMappings(MappingsToRemove);
|
|
AxisMappings.Add(FConfigAxisKeyMapping(Axis));
|
|
|
|
// Remove unbound mapping, but if the new mapping is not an axis key then leave any existing axis key unbound mappings, because the unbound mapping should still apply to other scales
|
|
// If the new mapping is an axis key, we can just remove all unbound mappings from all scales
|
|
const bool bIgnoreAxisKeys = !bIsAxisKey;
|
|
const bool bAnyScale = bIsAxisKey;
|
|
RemoveAxis(Axis.AxisName, Axis.Scale, KeyGroup, true, bIgnoreAxisKeys, bAnyScale);
|
|
|
|
return MappingsToRemove.ToUnboundMappings();
|
|
}
|
|
|
|
FInputMappingGroup FInputMappingGroup::UnbindChord(FKey Key, bool ShiftDown, bool CtrlDown, bool AltDown, bool CmdDown)
|
|
{
|
|
FInputMappingGroup MappingsToRemove = FInputMappingGroup(Config);
|
|
|
|
// Remove all action mappings with same chord
|
|
for(FInputActionKeyMapping& Action : ActionMappings)
|
|
{
|
|
if(Action.Key == Key && Action.bShift == ShiftDown && Action.bCtrl == CmdDown && Action.bAlt == AltDown && Action.bCmd == CmdDown
|
|
&& !Config->GetPreservedActions().Contains(Action.ActionName)) // Skip preserved actions
|
|
{
|
|
MappingsToRemove.ActionMappings.Add(Action);
|
|
}
|
|
}
|
|
|
|
// Since axis cannot have modifiers, only unbind axis with same key if no modifiers are down
|
|
if(!ShiftDown && !CtrlDown && !AltDown && !CmdDown)
|
|
{
|
|
for(FInputAxisKeyMapping& Axis : AxisMappings)
|
|
{
|
|
if(Axis.Key == Key
|
|
&& !Config->GetPreservedAxes().Contains(Axis.AxisName)) // Skip preserved axes
|
|
{
|
|
MappingsToRemove.AxisMappings.Add(Axis);
|
|
}
|
|
}
|
|
}
|
|
|
|
RemoveMappings(MappingsToRemove);
|
|
|
|
return MappingsToRemove.ToUnboundMappings();
|
|
}
|
|
|
|
void FInputMappingGroup::RemoveAction(FName ActionName, FGameplayTag KeyGroup, bool bRemoveFromUnbound)
|
|
{
|
|
const bool bAnyKeyGroup = !KeyGroup.IsValid();
|
|
FInputMappingGroup MappingsToRemove = FInputMappingGroup(Config);
|
|
|
|
TArray<FConfigActionKeyMapping>& TargetArray = bRemoveFromUnbound ? UnboundActionMappings : ActionMappings;
|
|
TArray<FConfigActionKeyMapping>& RemovalArray = bRemoveFromUnbound ? MappingsToRemove.UnboundActionMappings : MappingsToRemove.ActionMappings;
|
|
|
|
// Remove all action mappings with same chord
|
|
for (FInputActionKeyMapping& Action : TargetArray)
|
|
{
|
|
if (Action.ActionName == ActionName && (bAnyKeyGroup || Config->GetKeyGroupOfKey(Action.Key) == KeyGroup))
|
|
{
|
|
RemovalArray.Add(Action);
|
|
}
|
|
}
|
|
|
|
RemoveMappings(MappingsToRemove);
|
|
}
|
|
|
|
void FInputMappingGroup::RemoveAxis(FName AxisName, float Scale, FGameplayTag KeyGroup, bool bRemoveFromUnbound, bool bIgnoreAxisKeys, bool bAnyScale)
|
|
{
|
|
const bool bAnyKeyGroup = !KeyGroup.IsValid();
|
|
FInputMappingGroup MappingsToRemove = FInputMappingGroup(Config);
|
|
|
|
TArray<FConfigAxisKeyMapping>& TargetArray = bRemoveFromUnbound ? UnboundAxisMappings : AxisMappings;
|
|
TArray<FConfigAxisKeyMapping>& RemovalArray = bRemoveFromUnbound ? MappingsToRemove.UnboundAxisMappings : MappingsToRemove.AxisMappings;
|
|
|
|
// Remove all action mappings with same chord
|
|
for (FInputAxisKeyMapping& Axis : TargetArray)
|
|
{
|
|
const bool bIsAxisKey = Config->IsAxisKey(Axis.Key);
|
|
const bool bIgnore = bIsAxisKey && bIgnoreAxisKeys;
|
|
if (!bIgnore && Axis.AxisName == AxisName && ( bAnyScale || Axis.Scale == Scale) && (bAnyKeyGroup || Config->GetKeyGroupOfKey(Axis.Key) == KeyGroup))
|
|
{
|
|
RemovalArray.Add(Axis);
|
|
}
|
|
}
|
|
|
|
RemoveMappings(MappingsToRemove);
|
|
}
|
|
|
|
void FInputMappingGroup::RemoveMappings(FInputMappingGroup& MappingsToRemove)
|
|
{
|
|
for(FInputActionKeyMapping& Action : MappingsToRemove.ActionMappings)
|
|
{
|
|
ActionMappings.Remove(Action);
|
|
}
|
|
for(FInputAxisKeyMapping& Axis : MappingsToRemove.AxisMappings)
|
|
{
|
|
AxisMappings.Remove(Axis);
|
|
}
|
|
for(FInputActionKeyMapping& Action : MappingsToRemove.UnboundActionMappings)
|
|
{
|
|
UnboundActionMappings.Remove(Action);
|
|
}
|
|
for(FInputAxisKeyMapping& Axis : MappingsToRemove.UnboundAxisMappings)
|
|
{
|
|
UnboundAxisMappings.Remove(Axis);
|
|
}
|
|
}
|
|
|
|
FInputMappingGroup FInputMappingGroup::FindUnboundMappings(const FInputMappingGroup& SourceMappingGroup) const
|
|
{
|
|
FInputMappingGroup UnboundMappings = FInputMappingGroup(Config);
|
|
for(const FInputActionKeyMapping& Action : SourceMappingGroup.ActionMappings)
|
|
{
|
|
if(GetAllActions(Action.ActionName, FGameplayTag()).Num() == 0)
|
|
{
|
|
UnboundMappings.UnboundActionMappings.Add(Action);
|
|
}
|
|
}
|
|
for(const FInputAxisKeyMapping& Axis : SourceMappingGroup.AxisMappings)
|
|
{
|
|
if(GetAllAxes(Axis.AxisName, Axis.Scale, FGameplayTag(), false).Num() == 0)
|
|
{
|
|
UnboundMappings.UnboundAxisMappings.Add(Axis);
|
|
}
|
|
}
|
|
return UnboundMappings;
|
|
}
|
|
|
|
FInputMappingGroup FInputMappingGroup::ToUnboundMappings() const
|
|
{
|
|
FInputMappingGroup Result = FInputMappingGroup(Config);
|
|
Result.UnboundActionMappings.Append(ActionMappings);
|
|
Result.UnboundAxisMappings.Append(AxisMappings);
|
|
return Result;
|
|
}
|
|
|
|
void FInputMappingGroup::RemoveUnboundMappings()
|
|
{
|
|
FInputMappingGroup MappingsToRemove(Config);
|
|
for(FInputActionKeyMapping& Action : UnboundActionMappings)
|
|
{
|
|
if(!Action.Key.IsValid())
|
|
{
|
|
MappingsToRemove.UnboundActionMappings.Add(Action);
|
|
}
|
|
}
|
|
for(FInputAxisKeyMapping& Axis : UnboundAxisMappings)
|
|
{
|
|
if(!Axis.Key.IsValid())
|
|
{
|
|
MappingsToRemove.UnboundAxisMappings.Add(Axis);
|
|
}
|
|
}
|
|
RemoveMappings(MappingsToRemove);
|
|
}
|
|
|
|
void FInputMappingGroup::RemoveRedundantMappings(const FInputMappingGroup& BaseMappingGroup)
|
|
{
|
|
FInputMappingGroup MappingsToRemove(Config);
|
|
for(FInputActionKeyMapping& Action : ActionMappings)
|
|
{
|
|
if(BaseMappingGroup.ActionMappings.Contains(Action))
|
|
{
|
|
// Other mapping group contains the same mapping, so remove
|
|
MappingsToRemove.ActionMappings.Add(Action);
|
|
}
|
|
}
|
|
for(FConfigAxisKeyMapping& Axis : AxisMappings)
|
|
{
|
|
if(BaseMappingGroup.AxisMappings.Contains(Axis))
|
|
{
|
|
// Other mapping group contains the same mapping
|
|
|
|
// Edge case: the mapping is not redundant if it's overriding an unbound axis
|
|
bool bOverridingUnboundAxis = false;
|
|
for(FInputAxisKeyMapping& UnboundAxisMapping : UnboundAxisMappings)
|
|
{
|
|
if(UnboundAxisMapping.AxisName == Axis.AxisName && Config->IsAxisKey(UnboundAxisMapping.Key))
|
|
{
|
|
bOverridingUnboundAxis = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(!bOverridingUnboundAxis)
|
|
{
|
|
// Just remove normally
|
|
MappingsToRemove.AxisMappings.Add(Axis);
|
|
}
|
|
}
|
|
}
|
|
|
|
// For unbound mappings, we don't check the key of the unbound mapping itself against the other mapping group
|
|
// The unbound mapping is used as a flag to say "there was something unbound here, so don't show anything from the base preset, even if there are no overrides"
|
|
|
|
for(FInputActionKeyMapping& UnboundAction : UnboundActionMappings)
|
|
{
|
|
const FGameplayTag KeyGroup = Config->GetKeyGroupOfKey(UnboundAction.Key);
|
|
if(!BaseMappingGroup.GetAction(UnboundAction.ActionName, KeyGroup).Key.IsValid())
|
|
{
|
|
// Already not bound on the other mapping group, so remove
|
|
MappingsToRemove.UnboundActionMappings.Add(UnboundAction);
|
|
}
|
|
}
|
|
for(FInputAxisKeyMapping& UnboundAxis : UnboundAxisMappings)
|
|
{
|
|
const FGameplayTag KeyGroup = Config->GetKeyGroupOfKey(UnboundAxis.Key);
|
|
if(!BaseMappingGroup.GetAxis(UnboundAxis.AxisName, UnboundAxis.Scale, KeyGroup).Key.IsValid())
|
|
{
|
|
// Already not bound on the other mapping group, so remove
|
|
MappingsToRemove.UnboundAxisMappings.Add(UnboundAxis);
|
|
}
|
|
}
|
|
RemoveMappings(MappingsToRemove);
|
|
}
|
|
|
|
void FInputMappingGroup::MarkAllMappingsDefault()
|
|
{
|
|
for(FConfigActionKeyMapping& Action : ActionMappings)
|
|
{
|
|
Action.bIsDefault = true;
|
|
}
|
|
for(FConfigAxisKeyMapping& Axis : AxisMappings)
|
|
{
|
|
Axis.bIsDefault = true;
|
|
}
|
|
}
|
|
|
|
FInputMappingGroup FInputMappingGroup::operator+=(const FInputMappingGroup& Other)
|
|
{
|
|
ActionMappings.Append(Other.ActionMappings);
|
|
AxisMappings.Append(Other.AxisMappings);
|
|
UnboundActionMappings.Append(Other.UnboundActionMappings);
|
|
UnboundAxisMappings.Append(Other.UnboundAxisMappings);
|
|
return *this;
|
|
}
|