# Все способы обработать ипут кнопки в модуле Editor

## Оверайд InputKey()

```
virtual bool InputKey(FEditorViewportClient* InViewportClient, FViewport* InViewport, FKey InKey, EInputEvent InEvent) override;
```

Базовая функция, оверайдить которую можно в любом модуле. Кей приходит только если никто другой не Занял кнопку, и фокус Модуль = модулю, где функция заоверайжена.

Что значит Занял кнопку ? Функция возращает боол. Если вернуть Тру - ипут остановится, и никаким другим модулем обрабатываться не будет. Т.е функции Ариала не сработают, например, Еск - не выполнит ничего, если инпут кей вернет тру.

## Добавить шорткат

### Создание команды

Самый "правильный" способ. Позволяет изменять кнопки в редакторе, и уже имеет все функции для лога информации о шорткате. Но при этом имеет ряд недостатков: 1. Поддерживает только `IE_Pressed` и `IE_Repeat`, но не `IE_Released`. 2. Работает, только если никто другой не заберет инпут Key, и не переключит фокус на себя.

Теперь как добавить. Каждый `FUICommandInfo` создается через переменную команды, которая регистрируется в:

```cpp
class FBuildingToolsEditorCommands : public TCommands<FBuildingToolsEditorCommands>
{
public:
	FBuildingToolsEditorCommands()
		: TCommands<FBuildingToolsEditorCommands>(
			"BuildingToolsEditor",
			// Context name for fast lookup
			NSLOCTEXT("Contexts", "BuildingToolsEditor", "Building Tools"),
			// Localized context name for displaying
			NAME_None,
			//"LevelEditor" // Parent
			FEditorStyle::GetStyleSetName() // Icon Style Set
			)
	{
	}
	virtual void RegisterCommands() override;
	TSharedPtr<FUICommandInfo> Command;
};
```

```cpp
void FBuildingToolsEditorCommands::RegisterCommands()
{
    // FInputChord() - сюда указывать кнопку, на которую запускать команду
    // EUserInterfaceActionType важна только для UI
    UI_COMMAND(Command, "UniqueIDName", "CommandDescription",
     EUserInterfaceActionType::Button, FInputChord());
     
   // А это если мы хотим не создавать все команды EditorCommands, а создавать
   // их где-то еще
   for (const auto Command : BuildingToolsModes::GetCommands())
	 {
	  	Command->RegisterUICommand(this);
	 }
}
```

### Регистрация модуля c Командами

Но от куда берется сама `FBuildingToolsEditorCommands` ? Она регистрируется в классе модуля через статическую функцию:

```cpp
void FBuildingToolsModule::StartupModule()
{
   FBuildingToolsEditorCommands::Register();
}

void FBuildingToolsModule::ShutdownModule()
{
	FBuildingToolsEditorCommands::Unregister();
}
```

### Добавление функции в шорткат

Теперь нужно добавить к `FUICommandInfo` функцию, которую она запустит. Для этого нужно получить `FBuildingToolsEdModeToolkit :`

```cpp
class FBuildingToolsEdModeToolkit : public FModeToolkit
{
public:
   FBuildingToolsEdModeToolkit();

   /** FModeToolkit interface */
   virtual void Init(const TSharedPtr<IToolkitHost>& InitToolkitHost) override;
};
```

```cpp
void FBuildingToolsEdModeToolkit::Init(const TSharedPtr<IToolkitHost>& InitToolkitHost)
{
	FBuildingToolsEdMode* BuildingToolsEdMode = GetEditorMode();
	TSharedRef<FUICommandList> CommandList = BuildingToolsEdMode->GetUICommandList();

	//FIsActionChecked Полезно только если нужно определить - выбран ли он в UI
	CommandList->MapAction(Command,
	                       FUIAction(
		                       FExecuteAction::CreateSP(this, &FBuildingToolsEdModeToolkit::OnChangeMode, FName(Command)),
		                       FCanExecuteAction::CreateSP(this, &FBuildingToolsEdModeToolkit::IsModeEnabled, FName(Command)),
		                       FIsActionChecked::CreateSP(this, &FBuildingToolsEdModeToolkit::IsModeActive, FName(Command))));
			FModeToolkit::Init(InitToolkitHost);
}

FBuildingToolsEdMode* FBuildingToolsEdModeToolkit::GetEditorMode() const
{
	return (FBuildingToolsEdMode*)GLevelEditorModeTools().GetActiveMode(FBuildingToolsEdMode::EM_BuildingTools);
}
```

### Как создать Toolkit&#x20;

В основном модуле необходимо создать толкит, и засетить его:

```cpp
void FBuildingToolsEdMode::Enter()
{
	if (!Toolkit.IsValid())
	{
		const TSharedPtr<FBuildingToolsEdModeToolkit> BuildingToolsToolkit = MakeShareable(new FBuildingToolsEdModeToolkit);
		Toolkit = BuildingToolsToolkit;
		Toolkit->Init(Owner->GetToolkitHost());
	}
}
```

## Добавить шорткат не в Commands класс

Самая большая беда, что один КоммандИнфо должен быть зареган лишь единожды и никогда больше. Если зарегестрировать дважды - будет краш. Поэтому нужно убедиться, что каждый комманд инфо будет уникальный

### Создание комманды-класса

Для этой уникальности поместим каждую комманду в класс обжект

```cpp
UCLASS(abstract, Transient)
class BUILDINGTOOLS_API UBTCommand : public UObject
{
	GENERATED_BODY()

public:
	/** Registers the UI command for this mesh editor command */
	virtual void RegisterUICommand(class FBindingContext* BindingContext) PURE_VIRTUAL(,);

	/** Gets the UI command info for this command */
	const TSharedPtr<FUICommandInfo>& GetUICommandInfo() const
	{
		return UICommandInfo;
	}

protected:
	/** Our UI command for this action */
	TSharedPtr<FUICommandInfo> UICommandInfo;
};
```

Теперь создаем наследника и указываем всю инфу о команде:

```cpp
void UBTInvertCommand::RegisterUICommand(FBindingContext* BindingContext)
{
	UI_COMMAND_EXT(BindingContext,
	               UICommandInfo,
	               "Invert Assets",
	               "Invert Assets",
	               "Invert Assets.",
	               EUserInterfaceActionType::Button,
	               FInputChord( EKeys::I, UBuildingToolsLibrary::GetAdditKey()));
}
```

### Регестрируем комманды классы

Так же как и раньше, но не совсем. Как можно видеть в прошлом блоке - использовался именно `UI_COMMAND_EXT`. Это нужно для того, чтобы регестрировать комманды именно в других функциях.&#x20;

```cpp
void FBuildingToolsEditorCommands::RegisterCommands()
{
	HarvestCommands();
   // А это если мы хотим не создавать все команды EditorCommands, а создавать
   // их где-то еще
   for (const auto Command : Commands)
	 {
	  	Command->RegisterUICommand(this);
	 }
}
```

Но от куда у нас берется массив с командами:

```cpp
TArray<UBTCommand*> Commands;

void FBuildingToolsEditorCommands::HarvestCommands()
{
	Commands.Reset();
	for (TObjectIterator<UBTCommand> CommandCDOIter(RF_NoFlags); CommandCDOIter; ++CommandCDOIter)
	{
		const UBTCommand* CommandCDO = *CommandCDOIter;
		if (!(CommandCDO->GetClass()->GetClassFlags() & CLASS_Abstract))
		{
			Commands.Add(NewObject<UBTCommand>(GetTransientPackage(), CommandCDO->GetClass()));
			Commands.Last()->AddToRoot();
		}
	}
}

```

### Добавление функции на команду-класс

Добавление функции и удалять можно в любое время и в любой момент. Для этого достаточно ссылки на модуль с тоолкитом

```cpp
	TSharedRef<FUICommandList> CommandList = (FBuildingToolsEdMode*)GLevelEditorModeTools().GetActiveMode(FBuildingToolsEdMode::EM_BuildingTools);
	for (auto& Command : FBuildingToolsEditorCommands::Get().Commands)
	{
		CommandList->UnmapAction(Command->GetUICommandInfo());
	}
	CommandList->MapAction(
		FBuildingToolsEditorCommands::GetUICommandInfo<UBTInvertCommand>(),
		FExecuteAction::CreateWeakLambda(this,
										[&]()
										{
											FloorActor->SetFloorMode(EBTFloorMode::Invert);
											UpdateConstructionModeDelegate.Execute();
										})
		);
```

### Получение команды инфо по классу

НО для добавление МапАктион нужен КоммандИнфо класса. Для этого реалезуем Сатическую функцию получения:

```cpp
template<typename InBTCommand>
const TSharedPtr<FUICommandInfo>& FBuildingToolsEditorCommands::GetUICommandInfo()
{
	static TSharedPtr<FUICommandInfo> UICommandInfoInvalid;
	for (UBTCommand* Command : Get().Commands)
	{
		if (Command->IsA(InBTCommand::StaticClass()))
		{
			return Command->GetUICommandInfo();
		}
	}
	return UICommandInfoInvalid;
}
```

## Добавление IInputProcessor

Самый надежный способ принять инпут. Независимо от того, какой модуль сейчас в фокусе - Пре Инпут обработается перед этим. Для этого есть и ИнпутПроцессор. Создадим его:

{% tabs %}
{% tab title="h" %}

```cpp
class FBTInputProcessor : public IInputProcessor, public TSharedFromThis<FBTInputProcessor>
{
public:
	FBTInputProcessor(){};
	virtual ~FBTInputProcessor(){};

//Если не добавить тик, он будет считаться абстрактным
	virtual void Tick(const float DeltaTime, FSlateApplication& SlateApp, TSharedRef<ICursor> Cursor) override;
	
	/** Key down input */
	virtual bool HandleKeyDownEvent(FSlateApplication& SlateApp, const FKeyEvent& InKeyEvent) override;

	/** Key up input */
	virtual bool HandleKeyUpEvent(FSlateApplication& SlateApp, const FKeyEvent& InKeyEvent) override;

public:
	TDelegate<bool(FKey InKey, EInputEvent InEvent)> PreInputKeyDelegate;
};

```

{% endtab %}

{% tab title="cpp" %}

```
#include "FBTInputProcessor.h"

void FBTInputProcessor::Tick(const float DeltaTime, FSlateApplication& SlateApp, TSharedRef<ICursor> Cursor)
{
}

bool FBTInputProcessor::HandleKeyDownEvent(FSlateApplication& SlateApp, const FKeyEvent& InKeyEvent)
{
	if(PreInputKeyDelegate.IsBound())
	{
		return PreInputKeyDelegate.Execute(InKeyEvent.GetKey(), IE_Pressed);
	}
	return false;
}

bool FBTInputProcessor::HandleKeyUpEvent(FSlateApplication& SlateApp, const FKeyEvent& InKeyEvent)
{
	if(PreInputKeyDelegate.IsBound())
	{
		return PreInputKeyDelegate.Execute(InKeyEvent.GetKey(), IE_Released);
	}
	return false;
}

```

{% endtab %}
{% endtabs %}

Теперь зарегистрируем его. Если функция, зареганная в делегате вернет true - ипут дальше не пойдет. Если false - ипут идет дальше

{% tabs %}
{% tab title="Enter" %}

```cpp
	void FBuildingToolsEdMode::Enter()
{
	FEdMode::Enter();

	InputProcessor = MakeShareable(new FBTInputProcessor());
	FSlateApplication::Get().RegisterInputPreProcessor(InputProcessor);
	InputProcessor->PreInputKeyDelegate.BindSP(this, &FBuildingToolsEdMode::PreInputKey);
	}

```

{% endtab %}

{% tab title="Exit" %}

```
void FBuildingToolsEdMode::Exit()
{	
if (InputProcessor.IsValid() && FSlateApplication::IsInitialized())
	{
		FSlateApplication::Get().UnregisterInputPreProcessor(InputProcessor);
		InputProcessor.Reset();
	}

	// Call base Exit method to ensure proper cleanup
	FEdMode::Exit();
}

```

{% endtab %}
{% endtabs %}

### Более простой способ

Есть обычный Pre Input Pressed делегат. Но он только на Pressed работает

```cpp
FSlateApplication::Get().OnApplicationPreInputKeyDownListener();
```
