Editable Text Box With History

[ ue4  widget  ]
Written on October 13, 2022

In the game I’m working on we use the chat window also to enter cheat commands, so I found myself in need of an editable text box with history, as you sometimes need enter the same command repeatedly. Or maybe you just want to spam the chat in the most efficient manner possible.

The Widget

I didn’t find a suitable widget in Unreal Engine, but it’s easy enough to add, as a subclass of UEditableTextBox. I need to maintain a history buffer and handle the up/down arrow keys and set the text to the appropriate entry from the that buffer.

There is no need to create a separate Slate widget for this - the SEditableTextBox widget used by the UEditableTextBox widget has all the functionality I need, and allows me to set a dynamic handler for OnKeyDown.

UCLASS()
class UNREALCHAT_API UEditableTextBoxWithHistory : public UEditableTextBox
{
	GENERATED_BODY()

protected:
	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	TArray<FText> History;

	int32 HistoryIndex = -1;
	
	virtual void OnWidgetRebuilt() override;

	FReply OnKeyDown(const FGeometry& MyGeometry, const FKeyEvent& KeyEvent);

	virtual void HandleOnTextCommitted(const FText& Text, ETextCommit::Type CommitMethod) override;
};

The History buffer is populated when text is committed:

void UEditableTextBoxWithHistory::HandleOnTextCommitted(const FText& InText, ETextCommit::Type CommitMethod)
{
	Super::HandleOnTextCommitted(InText, CommitMethod);
	if (CommitMethod == ETextCommit::OnEnter)
	{
		History.Add(InText);
		if (History.Num() > 10)
		{
			History.RemoveAt(0);
		}
		HistoryIndex = History.Num();
	}
}

The UMG widget doesn’t have an OnKeyDown method - the underlying Slate widget does, so I hook into that in OnWidgetRebuilt:

void UEditableTextBoxWithHistory::OnWidgetRebuilt()
{
	Super::OnWidgetRebuilt();

	MyEditableTextBlock->SetOnKeyDownHandler(FOnKeyDown::CreateUObject(this, &UEditableTextBoxWithHistory::OnKeyDown));
}

Finally, the OnKeyDown method I attached to the Slate widget monitors keystrokes, looking for the up/down arrow keys:

FReply UEditableTextBoxWithHistory::OnKeyDown(const FGeometry& MyGeometry, const FKeyEvent& KeyEvent)
{
	if (History.IsEmpty())
	{
		return FReply::Unhandled();
	}
	
	if (KeyEvent.GetKey() == EKeys::Up)
	{
		HistoryIndex--;
		if (HistoryIndex < 0)
		{
			HistoryIndex = 0;
		}
		SetText(History[HistoryIndex]);
	} else if (KeyEvent.GetKey() == EKeys::Down)
	{
		HistoryIndex++;
		if (HistoryIndex >= History.Num())
		{
			SetText(FText());
			HistoryIndex = History.Num();
		} else
		{
			SetText(History[HistoryIndex]);
		}
	}

	return FReply::Unhandled();
}

This widget can be used in place of the regular UEditableTextBox widget, but it remembers its history and allows you to scroll through with the up/down arrow keys.