Все способы обработать ипут кнопки в модуле Editor
Оверайд InputKey()
virtual bool InputKey(FEditorViewportClient* InViewportClient, FViewport* InViewport, FKey InKey, EInputEvent InEvent) override;
Базовая функция, оверайдить которую можно в любом модуле. Кей приходит только если никто другой не Занял кнопку, и фокус Модуль = модулю, где функция заоверайжена.
Что значит Занял кнопку ? Функция возращает боол. Если вернуть Тру - ипут остановится, и никаким другим модулем обрабатываться не будет. Т.е функции Ариала не сработают, например, Еск - не выполнит ничего, если инпут кей вернет тру.
Добавить шорткат
Создание команды
Самый "правильный" способ. Позволяет изменять кнопки в редакторе, и уже имеет все функции для лога информации о шорткате. Но при этом имеет ряд недостатков: 1. Поддерживает только IE_Pressed
и IE_Repeat
, но не IE_Released
. 2. Работает, только если никто другой не заберет инпут Key, и не переключит фокус на себя.
Теперь как добавить. Каждый FUICommandInfo
создается через переменную команды, которая регистрируется в:
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;
};
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
? Она регистрируется в классе модуля через статическую функцию:
void FBuildingToolsModule::StartupModule()
{
FBuildingToolsEditorCommands::Register();
}
void FBuildingToolsModule::ShutdownModule()
{
FBuildingToolsEditorCommands::Unregister();
}
Добавление функции в шорткат
Теперь нужно добавить к FUICommandInfo
функцию, которую она запустит. Для этого нужно получить FBuildingToolsEdModeToolkit :
class FBuildingToolsEdModeToolkit : public FModeToolkit
{
public:
FBuildingToolsEdModeToolkit();
/** FModeToolkit interface */
virtual void Init(const TSharedPtr<IToolkitHost>& InitToolkitHost) override;
};
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
В основном модуле необходимо создать толкит, и засетить его:
void FBuildingToolsEdMode::Enter()
{
if (!Toolkit.IsValid())
{
const TSharedPtr<FBuildingToolsEdModeToolkit> BuildingToolsToolkit = MakeShareable(new FBuildingToolsEdModeToolkit);
Toolkit = BuildingToolsToolkit;
Toolkit->Init(Owner->GetToolkitHost());
}
}
Добавить шорткат не в Commands класс
Самая большая беда, что один КоммандИнфо должен быть зареган лишь единожды и никогда больше. Если зарегестрировать дважды - будет краш. Поэтому нужно убедиться, что каждый комманд инфо будет уникальный
Создание комманды-класса
Для этой уникальности поместим каждую комманду в класс обжект
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;
};
Теперь создаем наследника и указываем всю инфу о команде:
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
. Это нужно для того, чтобы регестрировать комманды именно в других функциях.
void FBuildingToolsEditorCommands::RegisterCommands()
{
HarvestCommands();
// А это если мы хотим не создавать все команды EditorCommands, а создавать
// их где-то еще
for (const auto Command : Commands)
{
Command->RegisterUICommand(this);
}
}
Но от куда у нас берется массив с командами:
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();
}
}
}
Добавление функции на команду-класс
Добавление функции и удалять можно в любое время и в любой момент. Для этого достаточно ссылки на модуль с тоолкитом
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();
})
);
Получение команды инфо по классу
НО для добавление МапАктион нужен КоммандИнфо класса. Для этого реалезуем Сатическую функцию получения:
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
Самый надежный способ принять инпут. Независимо от того, какой модуль сейчас в фокусе - Пре Инпут обработается перед этим. Для этого есть и ИнпутПроцессор. Создадим его:
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;
};
Теперь зарегистрируем его. Если функция, зареганная в делегате вернет true - ипут дальше не пойдет. Если false - ипут идет дальше
void FBuildingToolsEdMode::Enter()
{
FEdMode::Enter();
InputProcessor = MakeShareable(new FBTInputProcessor());
FSlateApplication::Get().RegisterInputPreProcessor(InputProcessor);
InputProcessor->PreInputKeyDelegate.BindSP(this, &FBuildingToolsEdMode::PreInputKey);
}
Более простой способ
Есть обычный Pre Input Pressed делегат. Но он только на Pressed работает
FSlateApplication::Get().OnApplicationPreInputKeyDownListener();
Last updated
Was this helpful?