LifePlay-Revival/Source/LifePlay/LPKeyboardSubsystem.cpp
edujiro5972 91259316a6 Clavier : LPKeyboardSubsystem (bouton et panneau Clavier, touches dans Saved/Config/Windows/LPKeyboard.ini)
Phase Clavier (CHG-156, 164, 165).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 15:49:09 +02:00

958 lines
27 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Menu Clavier. Voir LPKeyboardSubsystem.h.
#include "LPKeyboardSubsystem.h"
#include "Components/Button.h"
#include "Components/ScrollBox.h"
#include "Components/ScrollBoxSlot.h"
#include "Components/TextBlock.h"
#include "Engine/GameInstance.h"
#include "Engine/GameViewportClient.h"
#include "Engine/World.h"
#include "Framework/Application/IInputProcessor.h"
#include "Framework/Application/SlateApplication.h"
#include "GameFramework/InputSettings.h"
#include "GameFramework/PlayerController.h"
#include "GameFramework/PlayerInput.h"
#include "HAL/FileManager.h"
#include "LifePlay.h"
#include "Misc/ConfigCacheIni.h"
#include "Misc/FileHelper.h"
#include "Misc/Paths.h"
#include "Styling/CoreStyle.h"
#include "Widgets/Input/SButton.h"
#include "Widgets/Layout/SBorder.h"
#include "Widgets/Layout/SBox.h"
#include "Widgets/Layout/SScrollBox.h"
#include "Widgets/SBoxPanel.h"
#include "Widgets/Text/STextBlock.h"
namespace
{
const FName ButtonName(TEXT("LPKeyboardRow"));
const TCHAR* InputSection = TEXT("/Script/Engine.InputSettings");
FString KeyboardIniPath()
{
return FPaths::Combine(FPaths::ProjectSavedDir(), TEXT("Config/Windows/LPKeyboard.ini"));
}
FString ExportAction(const FInputActionKeyMapping& Map)
{
FString Text;
FInputActionKeyMapping::StaticStruct()->ExportText(Text, &Map, nullptr, nullptr, PPF_None, nullptr);
return Text;
}
FString ExportAxis(const FInputAxisKeyMapping& Map)
{
FString Text;
FInputAxisKeyMapping::StaticStruct()->ExportText(Text, &Map, nullptr, nullptr, PPF_None, nullptr);
return Text;
}
struct FBindRow
{
FName Name;
FString Label;
bool bAxis = false;
float Scale = 0.f;
FKey Key;
bool bShift = false;
bool bCtrl = false;
bool bAlt = false;
bool bCmd = false;
};
struct FLiveKeyboard
{
TWeakObjectPtr<ULPKeyboardSubsystem> Owner;
TSharedPtr<class SLPKeyboardMenu> Menu;
TSharedPtr<IInputProcessor> Processor;
bool bEmbedded = false;
};
bool IsRemappable(const FKey& Key)
{
return Key.IsValid() && !Key.IsAnalog() && !Key.IsAxis1D() && !Key.IsGamepadKey();
}
FString ActionLabel(FName Name)
{
static const TCHAR* Known[][2] = {
{TEXT("Pause"), TEXT("Pause")},
{TEXT("Journal"), TEXT("Journal")},
{TEXT("Inventory"), TEXT("Inventory")},
{TEXT("MainMenu"), TEXT("Main Menu")},
{TEXT("CharacterMenu"), TEXT("Character Menu")},
{TEXT("ModuleMenu"), TEXT("Modules")},
{TEXT("CityMenu"), TEXT("City")},
{TEXT("StatMenu"), TEXT("Statistics")},
{TEXT("ContactsMenu"), TEXT("Contacts")},
{TEXT("RotateCamera"), TEXT("Rotate Camera")},
{TEXT("ManualAnimating"), TEXT("Manual Animation")},
{TEXT("SpeedUp"), TEXT("Speed Up")},
{TEXT("SpeedDown"), TEXT("Slow Down")},
{TEXT("SwapRoles"), TEXT("Swap Roles")},
{TEXT("SwitchCamera"), TEXT("Switch Camera")},
{TEXT("NextBuilding"), TEXT("Next Building")},
{TEXT("PreviousBuilding"), TEXT("Previous Building")},
{TEXT("TestScenes"), TEXT("Test Scenes")},
{TEXT("Recommended"), TEXT("Recommended")},
{TEXT("QuickSave"), TEXT("Quick Save")},
{TEXT("Quickload"), TEXT("Quick Load")},
{TEXT("SearchBarToggle"), TEXT("Search")},
{TEXT("Interact"), TEXT("Interact")},
};
const FString Raw = Name.ToString();
for (int32 Index = 0; Index < UE_ARRAY_COUNT(Known); ++Index)
{
if (Raw == Known[Index][0])
{
return Known[Index][1];
}
}
if (Raw.StartsWith(TEXT("Favorite")))
{
return TEXT("Favorite ") + Raw.RightChop(8);
}
return Raw;
}
FString AxisLabel(FName Name, float Scale)
{
const bool bPositive = Scale >= 0.f;
if (Name == TEXT("LeftRight"))
{
return bPositive ? TEXT("Right") : TEXT("Left");
}
if (Name == TEXT("DownUp"))
{
return bPositive ? TEXT("Forward") : TEXT("Back");
}
if (Name == TEXT("MoveForward"))
{
return bPositive ? TEXT("Walk Forward") : TEXT("Walk Back");
}
if (Name == TEXT("MoveRight"))
{
return bPositive ? TEXT("Walk Right") : TEXT("Walk Left");
}
if (Name == TEXT("PenisRotation"))
{
return bPositive ? TEXT("Rotation +") : TEXT("Rotation −");
}
return Name.ToString() + (bPositive ? TEXT(" +") : TEXT(" −"));
}
struct FBindGroup
{
int32 Order = 9;
const TCHAR* Title = TEXT("Other");
};
FBindGroup GroupFor(FName Name)
{
const FString Raw = Name.ToString();
if (Raw.StartsWith(TEXT("Favorite")))
{
return {4, TEXT("Favorites")};
}
if (Name == TEXT("MoveForward") || Name == TEXT("MoveRight"))
{
return {0, TEXT("Movement")};
}
if (Name == TEXT("LeftRight") || Name == TEXT("DownUp") || Name == TEXT("ScrollDownUp")
|| Name == TEXT("RotateCamera") || Name == TEXT("SwitchCamera"))
{
return {1, TEXT("Camera")};
}
if (Name == TEXT("Interact") || Name == TEXT("SwapRoles"))
{
return {2, TEXT("Interaction")};
}
if (Name == TEXT("Pause") || Name == TEXT("Journal") || Name == TEXT("Inventory")
|| Name == TEXT("MainMenu") || Name == TEXT("CharacterMenu") || Name == TEXT("ModuleMenu")
|| Name == TEXT("CityMenu") || Name == TEXT("StatMenu") || Name == TEXT("ContactsMenu")
|| Name == TEXT("SearchBarToggle"))
{
return {3, TEXT("Menus")};
}
if (Name == TEXT("SpeedUp") || Name == TEXT("SpeedDown"))
{
return {5, TEXT("Time")};
}
if (Name == TEXT("NextBuilding") || Name == TEXT("PreviousBuilding"))
{
return {6, TEXT("World")};
}
if (Name == TEXT("QuickSave") || Name == TEXT("Quickload"))
{
return {7, TEXT("Save")};
}
if (Name == TEXT("ManualAnimating") || Name == TEXT("PenisRotation") || Name == TEXT("TestScenes") || Name == TEXT("Recommended"))
{
return {8, TEXT("Scene")};
}
return {};
}
FString ChordText(const FKey& Key, bool bShift, bool bCtrl, bool bAlt)
{
FString Text;
if (bCtrl)
{
Text += TEXT("Ctrl+");
}
if (bShift)
{
Text += TEXT("Shift+");
}
if (bAlt)
{
Text += TEXT("Alt+");
}
Text += Key.GetDisplayName().ToString();
return Text;
}
TSharedRef<STextBlock> MakeText(const FString& Value, int32 Size, const FLinearColor& Color)
{
FSlateFontInfo Font = FCoreStyle::GetDefaultFontStyle("Regular", Size);
Font.OutlineSettings.OutlineSize = 2;
Font.OutlineSettings.bApplyOutlineToDropShadows = true;
Font.OutlineSettings.OutlineColor = FLinearColor(0.f, 0.f, 0.f, 0.8f);
return SNew(STextBlock)
.Text(FText::FromString(Value))
.Font(Font)
.ColorAndOpacity(FSlateColor(Color));
}
class SLPKeyboardMenu : public SCompoundWidget
{
public:
SLATE_BEGIN_ARGS(SLPKeyboardMenu) {}
SLATE_ARGUMENT(bool, bEmbedded)
SLATE_END_ARGS()
void Construct(const FArguments& InArgs, ULPKeyboardSubsystem* InOwner)
{
Owner = InOwner;
bEmbedded = InArgs._bEmbedded;
ChildSlot
[
SNew(SBox)
.HAlign(bEmbedded ? HAlign_Fill : HAlign_Center)
.VAlign(bEmbedded ? VAlign_Fill : VAlign_Center)
[
SNew(SBorder)
.BorderImage(bEmbedded ? FCoreStyle::Get().GetBrush("NoBrush") : FCoreStyle::Get().GetBrush("ToolPanel.GroupBorder"))
.Padding(bEmbedded ? 0.f : 16.f)
[
SNew(SBox)
.WidthOverride(bEmbedded ? FOptionalSize() : FOptionalSize(560.f))
.HeightOverride(bEmbedded ? FOptionalSize() : FOptionalSize(640.f))
[
SNew(SVerticalBox)
+ SVerticalBox::Slot().AutoHeight().Padding(0.f, 0.f, 0.f, 8.f)
[
SNew(STextBlock)
.Text(FText::FromString(TEXT("Change a key, add one, or remove one. Escape cancels the capture. Saved for this version of the game.")))
.AutoWrapText(true)
.Font(MakeText(TEXT(""), 16, FLinearColor::White)->GetFont())
.ColorAndOpacity(FSlateColor(FLinearColor(1.f, 1.f, 1.f, 0.95f)))
]
+ SVerticalBox::Slot().FillHeight(1.f)
[
SNew(SScrollBox)
+ SScrollBox::Slot()
[
SAssignNew(Rows, SVerticalBox)
]
]
+ SVerticalBox::Slot().AutoHeight().Padding(0.f, 8.f, 0.f, 8.f)
[
SAssignNew(StatusBlock, STextBlock)
.Font(MakeText(TEXT(""), 16, FLinearColor::White)->GetFont())
.ColorAndOpacity(FSlateColor(FLinearColor(0.95f, 0.85f, 0.4f)))
]
+ SVerticalBox::Slot().AutoHeight()
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot().FillWidth(1.f).Padding(0.f, 0.f, 8.f, 0.f)
[
SNew(SButton)
.OnClicked(this, &SLPKeyboardMenu::OnReset)
[
MakeText(TEXT("Reset"), 20, FLinearColor::White)
]
]
+ SHorizontalBox::Slot().FillWidth(1.f)
[
SNew(SButton)
.Visibility(bEmbedded ? EVisibility::Collapsed : EVisibility::Visible)
.OnClicked(this, &SLPKeyboardMenu::OnClose)
[
MakeText(TEXT("Close"), 20, FLinearColor::White)
]
]
]
]
]
]
];
Rebuild();
}
void Rebuild()
{
if (!Rows.IsValid())
{
return;
}
Rows->ClearChildren();
ULPKeyboardSubsystem* Sub = Owner.Get();
const UInputSettings* Settings = UInputSettings::GetInputSettings();
if (!Sub || !Settings)
{
return;
}
TArray<FBindRow> Binds;
for (const FInputActionKeyMapping& Map : Settings->GetActionMappings())
{
if (!IsRemappable(Map.Key))
{
continue;
}
FBindRow Row;
Row.Name = Map.ActionName;
Row.Label = ActionLabel(Map.ActionName);
Row.Key = Map.Key;
Row.bShift = Map.bShift;
Row.bCtrl = Map.bCtrl;
Row.bAlt = Map.bAlt;
Row.bCmd = Map.bCmd;
Binds.Add(Row);
}
for (const FInputAxisKeyMapping& Map : Settings->GetAxisMappings())
{
if (!IsRemappable(Map.Key))
{
continue;
}
FBindRow Row;
Row.Name = Map.AxisName;
Row.Label = AxisLabel(Map.AxisName, Map.Scale);
Row.bAxis = true;
Row.Scale = Map.Scale;
Row.Key = Map.Key;
Binds.Add(Row);
}
Binds.Sort([](const FBindRow& A, const FBindRow& B)
{
const int32 GroupA = GroupFor(A.Name).Order;
const int32 GroupB = GroupFor(B.Name).Order;
if (GroupA != GroupB)
{
return GroupA < GroupB;
}
if (A.Label != B.Label)
{
return A.Label < B.Label;
}
return A.Key.GetDisplayName().ToString() < B.Key.GetDisplayName().ToString();
});
int32 PreviousGroup = INDEX_NONE;
FString Previous;
for (const FBindRow& Row : Binds)
{
const FBindGroup Section = GroupFor(Row.Name);
if (Section.Order != PreviousGroup)
{
PreviousGroup = Section.Order;
Previous.Reset();
Rows->AddSlot().AutoHeight().Padding(0.f, 12.f, 0.f, 4.f)
[
MakeText(Section.Title, 20, FLinearColor(1.f, 0.92f, 0.45f))
];
}
if (Row.Label != Previous)
{
Previous = Row.Label;
const FBindRow Group = Row;
Rows->AddSlot().AutoHeight().Padding(0.f, 10.f, 0.f, 2.f)
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot().FillWidth(1.f).VAlign(VAlign_Center)
[
MakeText(Row.Label, 20, FLinearColor(1.f, 1.f, 1.f, 0.95f))
]
+ SHorizontalBox::Slot().AutoWidth()
[
SNew(SButton)
.OnClicked_Lambda([this, Group]()
{
if (ULPKeyboardSubsystem* Sub = Owner.Get())
{
Sub->BeginWait(Group.Name, Group.bAxis, Group.Scale, EKeys::Invalid, false, false, false, false, true);
Sub->RefreshMenu();
}
return FReply::Handled();
})
[
MakeText(TEXT("Add"), 16, FLinearColor::White)
]
]
];
}
const FBindRow Current = Row;
Rows->AddSlot().AutoHeight().Padding(12.f, 2.f, 0.f, 2.f)
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot().FillWidth(1.f).VAlign(VAlign_Center)
[
MakeText(ChordText(Current.Key, Current.bShift, Current.bCtrl, Current.bAlt), 16, FLinearColor(0.85f, 0.9f, 1.f))
]
+ SHorizontalBox::Slot().AutoWidth().Padding(4.f, 0.f)
[
SNew(SButton)
.OnClicked_Lambda([this, Current]()
{
if (ULPKeyboardSubsystem* Sub = Owner.Get())
{
Sub->BeginWait(Current.Name, Current.bAxis, Current.Scale, Current.Key, Current.bShift, Current.bCtrl, Current.bAlt, Current.bCmd, false);
Sub->RefreshMenu();
}
return FReply::Handled();
})
[
MakeText(TEXT("Change"), 16, FLinearColor::White)
]
]
+ SHorizontalBox::Slot().AutoWidth()
[
SNew(SButton)
.OnClicked_Lambda([this, Current]()
{
if (ULPKeyboardSubsystem* Sub = Owner.Get())
{
Sub->RemoveBinding(Current.Name, Current.bAxis, Current.Scale, Current.Key, Current.bShift, Current.bCtrl, Current.bAlt, Current.bCmd);
}
return FReply::Handled();
})
[
MakeText(TEXT("Remove"), 16, FLinearColor::White)
]
]
];
}
if (StatusBlock.IsValid() && Sub)
{
StatusBlock->SetText(FText::FromString(Sub->GetStatus()));
}
}
private:
FReply OnReset()
{
if (ULPKeyboardSubsystem* Sub = Owner.Get())
{
Sub->ResetToDefault();
}
return FReply::Handled();
}
FReply OnClose()
{
if (ULPKeyboardSubsystem* Sub = Owner.Get())
{
Sub->CloseKeyboardMenu();
}
return FReply::Handled();
}
TWeakObjectPtr<ULPKeyboardSubsystem> Owner;
TSharedPtr<SVerticalBox> Rows;
TSharedPtr<STextBlock> StatusBlock;
bool bEmbedded = false;
};
FLiveKeyboard& Live()
{
static FLiveKeyboard State;
return State;
}
class FKeyboardProcessor : public IInputProcessor
{
public:
virtual void Tick(const float, FSlateApplication&, TSharedRef<ICursor>) override {}
virtual bool HandleKeyDownEvent(FSlateApplication&, const FKeyEvent& Event) override
{
ULPKeyboardSubsystem* Sub = Live().Owner.Get();
if (!Sub || !Live().Menu.IsValid())
{
return false;
}
const FKey Key = Event.GetKey();
if (Sub->IsWaiting())
{
if (Key == EKeys::Escape)
{
Sub->CancelWait();
}
else if (!Key.IsModifierKey())
{
Sub->CommitKey(Key, Event.IsShiftDown(), Event.IsControlDown(), Event.IsAltDown(), Event.IsCommandDown());
}
return true;
}
if (Live().bEmbedded)
{
return false;
}
if (Key == EKeys::Escape)
{
Sub->CloseKeyboardMenu();
return true;
}
return true;
}
};
}
bool ULPKeyboardSubsystem::IsMenuOpen() const
{
return Live().Menu.IsValid() && Live().Owner.Get() == this;
}
void ULPKeyboardSubsystem::Initialize(FSubsystemCollectionBase& Collection)
{
Super::Initialize(Collection);
if (!LoadSaved())
{
SaveSaved();
}
TickHandle = FTSTicker::GetCoreTicker().AddTicker(FTickerDelegate::CreateUObject(this, &ULPKeyboardSubsystem::Tick), 0.5f);
}
void ULPKeyboardSubsystem::Deinitialize()
{
if (Live().Owner.Get() == this)
{
CloseKeyboardMenu();
}
FTSTicker::GetCoreTicker().RemoveTicker(TickHandle);
Super::Deinitialize();
}
bool ULPKeyboardSubsystem::Tick(float DeltaTime)
{
(void)DeltaTime;
UGameInstance* GI = GetGameInstance();
bool bFound = false;
for (TObjectIterator<UScrollBox> It; It; ++It)
{
UScrollBox* Scroll = *It;
if (!Scroll || Scroll->GetFName() != TEXT("SettingsScrollBox") || Scroll->HasAnyFlags(RF_ClassDefaultObject | RF_ArchetypeObject))
{
continue;
}
const UUserWidget* Owner = Scroll->GetTypedOuter<UUserWidget>();
if (!Owner || Owner->HasAnyFlags(RF_ClassDefaultObject) || Owner->GetGameInstance() != GI)
{
continue;
}
bFound = true;
}
if (!bFound && !bLoggedMissing)
{
bLoggedMissing = true;
UE_LOG(LogLifePlay, Display, TEXT("Clavier : liste des reglages pas encore ouverte"));
}
return true;
}
void ULPKeyboardSubsystem::AttachButton(UScrollBox* Scroll)
{
for (int32 Index = 0; Index < Scroll->GetChildrenCount(); ++Index)
{
if (Scroll->GetChildAt(Index) && Scroll->GetChildAt(Index)->GetFName() == ButtonName)
{
return;
}
}
UButton* Button = NewObject<UButton>(Scroll, ButtonName);
UTextBlock* Label = NewObject<UTextBlock>(Button, TEXT("LPKeyboardLabel"));
Label->SetText(FText::FromString(TEXT("Keyboard")));
Label->SetColorAndOpacity(FSlateColor(FLinearColor::White));
FSlateFontInfo Font = FCoreStyle::GetDefaultFontStyle("Regular", 16);
Label->SetFont(Font);
Button->SetContent(Label);
if (UScrollBoxSlot* Slot = Cast<UScrollBoxSlot>(Scroll->AddChild(Button)))
{
Slot->SetPadding(FMargin(0.f, 12.f, 0.f, 4.f));
Slot->SetHorizontalAlignment(HAlign_Fill);
}
Button->OnClicked.AddDynamic(this, &ULPKeyboardSubsystem::OpenKeyboardMenu);
UE_LOG(LogLifePlay, Display, TEXT("Clavier : bouton ajoute dans les reglages"));
}
TSharedRef<SWidget> ULPKeyboardSubsystem::TakeEmbeddedPage()
{
if (Live().Menu.IsValid() && Live().Owner.Get() == this)
{
return Live().Menu.ToSharedRef();
}
Status = TEXT("Choose Change or Add, then press a key.");
bWaiting = false;
Live().bEmbedded = true;
TSharedRef<SLPKeyboardMenu> Panel = SNew(SLPKeyboardMenu, this).bEmbedded(true);
Live().Owner = this;
Live().Menu = Panel;
if (!Live().Processor.IsValid())
{
Live().Processor = MakeShared<FKeyboardProcessor>();
FSlateApplication::Get().RegisterInputPreProcessor(Live().Processor.ToSharedRef(), 0);
}
return Panel;
}
void ULPKeyboardSubsystem::OpenKeyboardMenu()
{
if (Live().Menu.IsValid() || !FSlateApplication::IsInitialized() || !GEngine || !GEngine->GameViewport)
{
return;
}
Status = TEXT("Choose Change or Add, then press a key.");
bWaiting = false;
Live().bEmbedded = false;
TSharedRef<SLPKeyboardMenu> Panel = SNew(SLPKeyboardMenu, this).bEmbedded(false);
Live().Owner = this;
Live().Menu = Panel;
Live().Processor = MakeShared<FKeyboardProcessor>();
GEngine->GameViewport->AddViewportWidgetContent(Panel, 200);
FSlateApplication::Get().RegisterInputPreProcessor(Live().Processor.ToSharedRef(), 0);
}
void ULPKeyboardSubsystem::CloseKeyboardMenu()
{
if (Live().Owner.Get() != this && Live().Owner.IsValid())
{
return;
}
bWaiting = false;
if (Live().Processor.IsValid() && FSlateApplication::IsInitialized())
{
FSlateApplication::Get().UnregisterInputPreProcessor(Live().Processor.ToSharedRef());
}
if (Live().Menu.IsValid() && GEngine && GEngine->GameViewport)
{
GEngine->GameViewport->RemoveViewportWidgetContent(Live().Menu.ToSharedRef());
}
Live().Menu.Reset();
Live().Processor.Reset();
Live().Owner.Reset();
Live().bEmbedded = false;
}
void ULPKeyboardSubsystem::BeginWait(FName Name, bool bAxis, float Scale, FKey OldKey, bool bShift, bool bCtrl, bool bAlt, bool bCmd, bool bAdd)
{
bWaiting = true;
bWaitAdd = bAdd;
bWaitAxis = bAxis;
WaitScale = Scale;
WaitName = Name;
WaitOldKey = OldKey;
bWaitShift = bShift;
bWaitCtrl = bCtrl;
bWaitAlt = bAlt;
bWaitCmd = bCmd;
Status = bAdd
? FString::Printf(TEXT("New key for %s. Escape cancels."), bAxis ? *AxisLabel(Name, Scale) : *ActionLabel(Name))
: FString::Printf(TEXT("Replaces %s. Escape cancels."), *OldKey.GetDisplayName().ToString());
}
void ULPKeyboardSubsystem::CancelWait()
{
bWaiting = false;
Status = TEXT("Capture cancelled.");
RefreshMenu();
}
bool ULPKeyboardSubsystem::CommitKey(const FKey& Key, bool bShift, bool bCtrl, bool bAlt, bool bCmd)
{
bWaiting = false;
if (!IsRemappable(Key))
{
Status = TEXT("This key cannot be assigned.");
RefreshMenu();
return false;
}
UInputSettings* Settings = UInputSettings::GetInputSettings();
if (!Settings)
{
return false;
}
TArray<FString> Cleared;
if (!bWaitAdd && WaitOldKey.IsValid())
{
if (bWaitAxis)
{
Settings->RemoveAxisMapping(FInputAxisKeyMapping(WaitName, WaitOldKey, WaitScale), false);
}
else
{
Settings->RemoveActionMapping(FInputActionKeyMapping(WaitName, WaitOldKey, bWaitShift, bWaitCtrl, bWaitAlt, bWaitCmd), false);
}
}
const TArray<FInputActionKeyMapping> Actions = Settings->GetActionMappings();
for (const FInputActionKeyMapping& Map : Actions)
{
if (Map.Key == Key && Map.bShift == bShift && Map.bCtrl == bCtrl && Map.bAlt == bAlt && Map.bCmd == bCmd)
{
Settings->RemoveActionMapping(Map, false);
Cleared.Add(ActionLabel(Map.ActionName));
}
}
TArray<FInputAxisKeyMapping> Axes = Settings->GetAxisMappings();
for (const FInputAxisKeyMapping& Map : Axes)
{
if (Map.Key == Key && !bShift && !bCtrl && !bAlt && !bCmd)
{
Settings->RemoveAxisMapping(Map, false);
Cleared.Add(AxisLabel(Map.AxisName, Map.Scale));
}
}
if (bWaitAxis)
{
Settings->AddAxisMapping(FInputAxisKeyMapping(WaitName, Key, WaitScale), false);
}
else
{
Settings->AddActionMapping(FInputActionKeyMapping(WaitName, Key, bShift, bCtrl, bAlt, bCmd), false);
}
ApplyToPlayer();
Status = Cleared.Num() > 0
? FString::Printf(TEXT("%s saved. Removed from: %s."), *Key.GetDisplayName().ToString(), *FString::Join(Cleared, TEXT(", ")))
: FString::Printf(TEXT("%s saved."), *Key.GetDisplayName().ToString());
RefreshMenu();
return true;
}
void ULPKeyboardSubsystem::RemoveBinding(FName Name, bool bAxis, float Scale, FKey Key, bool bShift, bool bCtrl, bool bAlt, bool bCmd)
{
bWaiting = false;
UInputSettings* Settings = UInputSettings::GetInputSettings();
if (!Settings)
{
return;
}
if (bAxis)
{
Settings->RemoveAxisMapping(FInputAxisKeyMapping(Name, Key, Scale), false);
}
else
{
Settings->RemoveActionMapping(FInputActionKeyMapping(Name, Key, bShift, bCtrl, bAlt, bCmd), false);
}
ApplyToPlayer();
Status = FString::Printf(TEXT("%s removed."), *Key.GetDisplayName().ToString());
RefreshMenu();
}
bool ULPKeyboardSubsystem::LoadDefaults(TArray<FInputActionKeyMapping>& Actions, TArray<FInputAxisKeyMapping>& Axes) const
{
const FString Path = FPaths::Combine(FPaths::ProjectConfigDir(), TEXT("DefaultInput.ini"));
TArray<FString> Lines;
if (!FFileHelper::LoadFileToStringArray(Lines, *Path))
{
return false;
}
for (FString Line : Lines)
{
Line.TrimStartAndEndInline();
if (Line.StartsWith(TEXT("+ActionMappings=")))
{
FInputActionKeyMapping Map;
const FString Inner = Line.Mid(16);
if (FInputActionKeyMapping::StaticStruct()->ImportText(*Inner, &Map, nullptr, PPF_None, nullptr, TEXT("ActionMappings")) && Map.Key.IsValid())
{
Actions.Add(Map);
}
}
else if (Line.StartsWith(TEXT("+AxisMappings=")))
{
FInputAxisKeyMapping Map;
const FString Inner = Line.Mid(14);
if (FInputAxisKeyMapping::StaticStruct()->ImportText(*Inner, &Map, nullptr, PPF_None, nullptr, TEXT("AxisMappings")) && Map.Key.IsValid())
{
Axes.Add(Map);
}
}
}
return Actions.Num() > 0 || Axes.Num() > 0;
}
void ULPKeyboardSubsystem::ResetToDefault()
{
bWaiting = false;
TArray<FInputActionKeyMapping> Actions;
TArray<FInputAxisKeyMapping> Axes;
UInputSettings* Settings = UInputSettings::GetInputSettings();
if (!Settings || !LoadDefaults(Actions, Axes))
{
Status = TEXT("The original keys could not be found.");
RefreshMenu();
return;
}
const TArray<FInputActionKeyMapping> CurrentActions = Settings->GetActionMappings();
for (const FInputActionKeyMapping& Map : CurrentActions)
{
Settings->RemoveActionMapping(Map, false);
}
const TArray<FInputAxisKeyMapping> CurrentAxes = Settings->GetAxisMappings();
for (const FInputAxisKeyMapping& Map : CurrentAxes)
{
Settings->RemoveAxisMapping(Map, false);
}
for (const FInputActionKeyMapping& Map : Actions)
{
Settings->AddActionMapping(Map, false);
}
for (const FInputAxisKeyMapping& Map : Axes)
{
Settings->AddAxisMapping(Map, false);
}
ApplyToPlayer();
Status = TEXT("Original keys restored.");
RefreshMenu();
}
bool ULPKeyboardSubsystem::LoadSaved() const
{
const FString Path = KeyboardIniPath();
if (!FPaths::FileExists(Path))
{
UE_LOG(LogLifePlay, Display, TEXT("Clavier : %s absent, creation avec les touches actuelles"), *Path);
return false;
}
FConfigFile File;
File.Read(Path);
TArray<FString> ActionLines;
TArray<FString> AxisLines;
File.GetArray(InputSection, TEXT("ActionMappings"), ActionLines);
File.GetArray(InputSection, TEXT("AxisMappings"), AxisLines);
TArray<FInputActionKeyMapping> Actions;
TArray<FInputAxisKeyMapping> Axes;
for (const FString& Line : ActionLines)
{
FInputActionKeyMapping Map;
if (FInputActionKeyMapping::StaticStruct()->ImportText(*Line, &Map, nullptr, PPF_None, nullptr, TEXT("ActionMappings")) && Map.Key.IsValid())
{
Actions.Add(Map);
}
}
for (const FString& Line : AxisLines)
{
FInputAxisKeyMapping Map;
if (FInputAxisKeyMapping::StaticStruct()->ImportText(*Line, &Map, nullptr, PPF_None, nullptr, TEXT("AxisMappings")) && Map.Key.IsValid())
{
Axes.Add(Map);
}
}
UInputSettings* Settings = UInputSettings::GetInputSettings();
if (!Settings || (Actions.Num() == 0 && Axes.Num() == 0))
{
UE_LOG(LogLifePlay, Warning, TEXT("Clavier : %s illisible, les touches du jeu sont gardees"), *Path);
return false;
}
if (Actions.Num() > 0)
{
const TArray<FInputActionKeyMapping> Current = Settings->GetActionMappings();
for (const FInputActionKeyMapping& Map : Current)
{
Settings->RemoveActionMapping(Map, false);
}
for (const FInputActionKeyMapping& Map : Actions)
{
Settings->AddActionMapping(Map, false);
}
}
if (Axes.Num() > 0)
{
const TArray<FInputAxisKeyMapping> Current = Settings->GetAxisMappings();
for (const FInputAxisKeyMapping& Map : Current)
{
Settings->RemoveAxisMapping(Map, false);
}
for (const FInputAxisKeyMapping& Map : Axes)
{
Settings->AddAxisMapping(Map, false);
}
}
Settings->ForceRebuildKeymaps();
UE_LOG(LogLifePlay, Display, TEXT("Clavier : %d actions et %d axes relus depuis %s"), Actions.Num(), Axes.Num(), *Path);
return true;
}
void ULPKeyboardSubsystem::SaveSaved() const
{
UInputSettings* Settings = UInputSettings::GetInputSettings();
if (!Settings)
{
return;
}
const FString Path = KeyboardIniPath();
IFileManager::Get().MakeDirectory(*FPaths::GetPath(Path), true);
FConfigFile File;
int32 ActionCount = 0;
int32 AxisCount = 0;
for (const FInputActionKeyMapping& Map : Settings->GetActionMappings())
{
if (Map.Key.IsValid())
{
File.AddToSection(InputSection, TEXT("+ActionMappings"), ExportAction(Map));
++ActionCount;
}
}
for (const FInputAxisKeyMapping& Map : Settings->GetAxisMappings())
{
if (Map.Key.IsValid())
{
File.AddToSection(InputSection, TEXT("+AxisMappings"), ExportAxis(Map));
++AxisCount;
}
}
const bool bWrote = File.Write(Path);
UE_LOG(LogLifePlay, Display, TEXT("Clavier : %d actions et %d axes %s dans %s"), ActionCount, AxisCount, bWrote ? TEXT("enregistres") : TEXT("non ecrits"), *Path);
}
void ULPKeyboardSubsystem::ApplyToPlayer() const
{
if (UInputSettings* Settings = UInputSettings::GetInputSettings())
{
Settings->ForceRebuildKeymaps();
SaveSaved();
}
if (UWorld* World = GetWorld())
{
if (APlayerController* PC = World->GetFirstPlayerController())
{
if (UPlayerInput* Input = PC->PlayerInput)
{
Input->ForceRebuildingKeyMaps(false);
}
}
}
}
void ULPKeyboardSubsystem::RefreshMenu()
{
if (Live().Owner.Get() == this && Live().Menu.IsValid())
{
Live().Menu->Rebuild();
}
}