# Все способы обработать ипут кнопки в модуле 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();
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://nexon3333.gitbook.io/gga/vse-sposoby-obrabotat-iput-knopki-v-module-editor.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
