[UE5.1.1, Feature Request] Avoid handling input for critical editor controls?

I wish to make an exception in my input system for the key used to stop PIE (editor shortcut “stop simulation?”).

Is there a library / class to pull these things from, before I start digging in ini files?

I tried this when I mapped it to the numlock key for testing, it returns 0 results on it:

TMap<FName, TSet<FName>> RelevantContexts;
TArray<TSharedPtr<FBindingContext>> OutContexts;
FInputBindingManager::Get().GetKnownInputContexts(OutContexts);
for (const TSharedPtr<FBindingContext> CX : OutContexts) {
	TSharedPtr<FUICommandInfo> InfoX = FInputBindingManager::Get().FindCommandInContext(CX->GetContextName(), FInputChord(EKeys::NumLock), true);
	if (InfoX != nullptr) {
		TSet<FName>& SetX = RelevantContexts.FindOrAdd(InfoX->GetBindingContext());
		SetX.Add(InfoX->GetCommandName());
	}
}

up

To handle exceptions in your input system for the key used to stop PIE, you can use the FInputProcessor::ProcessKeyEventArgs() function. This function is called whenever a key is pressed or released, and allows you to handle the input event before it is processed by the engine.

Here’s an example that should help you get started

class FMyInputProcessor : public FInputProcessor
{
public:
    virtual bool ProcessKeyEventArgs(FKey Key, EInputEvent EventType, float AmountDepressed, bool bGamepad) override
    {
        // Check if the key is the one used to stop PIE
        if (Key == EKeys::Escape && EventType == IE_Pressed)
        {
            // Do something here to handle the exception
            return true; // return true to indicate that the input event has been handled
        }

        // If the key is not the one used to stop PIE, let the engine handle it
        return false;
    }
};

// Create an instance of the input processor and register it with the input system
TSharedPtr<FMyInputProcessor> MyInputProcessor = MakeShared<FMyInputProcessor>();
FSlateApplication::Get().RegisterInputPreProcessor(MyInputProcessor);

Here, we create a custom input processor that overrides the ProcessKeyEventArgs() function.

We check if the key pressed is the one used to stop PIE (Escape key), and if it is, we handle the exception and return true to indicate that the input event has been handled.
If the key is not the one used to stop PIE, we return false to let the engine handle it.

Finally, we create an instance of our input processor and register it with the input system using FSlateApplication::Get().RegisterInputPreProcessor().

This will ensure that our input processor is called before the engine processes any input events.

1 Like

Thanks for the detailed answer :slight_smile: . The input preprocessor is a great place to implement this. The part I was stuck on however is pulling the key used to stop PIE from the editor settings, since it’s for a plugin and people can set that key to anything. Is there a library / setting manager class to pull the key from, or do I need to parse some ini through GConfig? That is what I tried to do in my first post, the “numlock” just being a dummy in an attempt to find the context / command for “stop pie” but got no results.

1 Like

you can use the UEditorPerProjectUserSettings class. This class provides access to the per-project user settings in the editor, including the key binding for stopping PIE. Here’s an example of how you can use it:

#include "Editor/EditorPerProjectUserSettings.h"

// Get the per-project user settings
UEditorPerProjectUserSettings* PerProjUserSettings = GetMutableDefault<UEditorPerProjectUserSettings>();

// Get the key binding for stopping PIE
const FInputChord& StopPIEChord = PerProjUserSettings->PlayWorldStop;

// Use the key binding as needed...

The StopPIEChord variable will contain the key binding for stopping PIE as set by the user in the editor settings. You can then use this information in your input preprocessor to make the necessary exceptions.

:>

1 Like

I just checked, UE5.1.1 UEditorPerProjectUserSettings does not contain “PlayWorldStop” and does not provide access to the keybindings? My scanners don’t find “PlayWorldStop” in engine source.

Sadly not :confused: this retrieves 3 of the contexts of which I discoverd in the first post:
afbeelding

I can’t find anything similar to “stop PIE” in any bindings of the contexts

Oh you’re right, I thought that would contain all shortcut keys…

I signed up for chatgpt yesterday so I asked it - it just made up some random UE classes that don’t exist…

Maybe parsing the ini file IS the way to go…

I swear ChatGPT is going to make people dry cats in microwaves again :slight_smile:

1 Like

After exporting the ini from the editor settings I could read the context and the command in the export, then implemented this.

bool FHandleEditorKeysPreProcessor::GetHandleKeyEvent(const FKeyEvent& InKeyEvent) {
	auto HandleCommand = [&InKeyEvent](const FName& InContext, const FName& InCommand) -> bool {
		TSharedPtr<FUICommandInfo> InfoX = FInputBindingManager::Get().FindCommandInContext(InContext, InCommand);
		if (!InfoX.IsValid()) {
			CUR_LOG(LogCorePluginEditor, Warning, "Could not find UI command: %s, in context: %s", *InCommand.ToString(), *InContext.ToString());
			return false;
		}
		if (InfoX->HasActiveChord(FInputChord(InKeyEvent.GetKey(), InKeyEvent.IsShiftDown(), InKeyEvent.IsControlDown(), InKeyEvent.IsAltDown(), InKeyEvent.IsCommandDown()))) {
			CUR_LOG(LogCorePluginEditor, Verbose, "Handling input for editor action, UI command: %s, in context: %s", *InCommand.ToString(), *InContext.ToString());
			return true;
		}
		return false;
	};

	return HandleCommand(TEXT("PlayWorld"), TEXT("StopPlaySession"));
}

bool FHandleEditorKeysPreProcessor::HandleKeyDownEvent(FSlateApplication& InSlateApp, const FKeyEvent& InKeyEvent) {
	return GetHandleKeyEvent(InKeyEvent);
}

bool FHandleEditorKeysPreProcessor::HandleKeyUpEvent(FSlateApplication& InSlateApp, const FKeyEvent& InKeyEvent) {
	return GetHandleKeyEvent(InKeyEvent);
}

It works, it handles the input, but it was a huge facepalm moment because I realized the handled input will now not reach the editor itself. Got to rethink this.

Where I was handling all input, I now made an workaround to return unhandled if the key shares an input chord with a specific editor action. It’s not ideal but this is a very specific case where an input preprocessor also turned out not to be ideal.

#if WITH_EDITOR

	// We can't "handle" the situation there as the input would not reach the editor.

	auto CanDoInEditor = [&InInputChord](const FName& ContextX, const FName& CommandX) -> bool {
		TSharedPtr<FUICommandInfo> InfoX = FInputBindingManager::Get().FindCommandInContext(ContextX, CommandX);
		if (InfoX.IsValid() && InfoX->HasActiveChord(InInputChord)) {
			CUR_LOG(LogCorePlugin, Verbose, "Processing input which shares an input chord with a specific editor action. Aborting and must return unhandled. UI command: %s, in context: %s", *CommandX.ToString(), *ContextX.ToString());
			return false;
		}
		return true;
	};

	if (!CanDoInEditor(TEXT("PlayWorld"), TEXT("StopPlaySession"))) {
		return FReply::Unhandled();
	}

#endif // WITH_EDITOR

I think that we are missing engine functionality here, it would be nice to be able to switch between editor commands and in-game input bindings on a single key so that they don’t conflict. Right now if you handle all input on a widget you will lose all control over the editor (in my case the button to stop PIE). For this reason I’m moving the post to a feature request section.