Files
Brandyn / Techy fcc1b09210 init
2026-04-04 15:40:51 -05:00

547 lines
14 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "Widgets/Input/SEditableTextBox.h"
#include "Framework/Text/TextEditHelper.h"
#include "Widgets/SBoxPanel.h"
#include "Widgets/Layout/SBox.h"
#include "Widgets/Notifications/SPopUpErrorText.h"
#if WITH_ACCESSIBILITY
#include "Widgets/Accessibility/SlateAccessibleWidgets.h"
#endif
SEditableTextBox::SEditableTextBox()
{
#if WITH_ACCESSIBILITY
AccessibleBehavior = EAccessibleBehavior::Auto;
bCanChildrenBeAccessible = false;
#endif
}
SEditableTextBox::~SEditableTextBox() = default;
/**
* Construct this widget
*
* @param InArgs The declaration data for this widget
*/
void SEditableTextBox::Construct( const FArguments& InArgs )
{
check (InArgs._Style);
SetStyle(InArgs._Style);
PaddingOverride = InArgs._Padding;
FontOverride = InArgs._Font;
ForegroundColorOverride = InArgs._ForegroundColor;
BackgroundColorOverride = InArgs._BackgroundColor;
ReadOnlyForegroundColorOverride = InArgs._ReadOnlyForegroundColor;
FocusedForegroundColorOverride = InArgs._FocusedForegroundColor;
MaximumLength = InArgs._MaximumLength;
OnTextChanged = InArgs._OnTextChanged;
OnVerifyTextChanged = InArgs._OnVerifyTextChanged;
OnTextCommitted = InArgs._OnTextCommitted;
SBorder::Construct( SBorder::FArguments()
.BorderImage( this, &SEditableTextBox::GetBorderImage )
.BorderBackgroundColor( this, &SEditableTextBox::DetermineBackgroundColor )
.ForegroundColor( this, &SEditableTextBox::DetermineForegroundColor )
.Padding(0.f)
[
SAssignNew( Box, SHorizontalBox)
+ SHorizontalBox::Slot()
.VAlign(VAlign_Fill)
.HAlign(HAlign_Fill)
.FillWidth(1)
[
SAssignNew(PaddingBox, SBox)
.Padding( this, &SEditableTextBox::DeterminePadding )
.VAlign(VAlign_Center)
[
SAssignNew( EditableText, SEditableText )
.Text( InArgs._Text )
.HintText( InArgs._HintText )
.SearchText( InArgs._SearchText )
.Font( this, &SEditableTextBox::DetermineFont )
.IsReadOnly( InArgs._IsReadOnly )
.IsPassword( InArgs._IsPassword )
.IsCaretMovedWhenGainFocus( InArgs._IsCaretMovedWhenGainFocus )
.SelectAllTextWhenFocused( InArgs._SelectAllTextWhenFocused )
.RevertTextOnEscape( InArgs._RevertTextOnEscape )
.ClearKeyboardFocusOnCommit( InArgs._ClearKeyboardFocusOnCommit )
.Justification( InArgs._Justification )
.AllowContextMenu( InArgs._AllowContextMenu )
.OnContextMenuOpening( InArgs._OnContextMenuOpening )
.ContextMenuExtender( InArgs._ContextMenuExtender )
.OnBeginTextEdit(InArgs._OnBeginTextEdit)
.OnTextChanged(this, &SEditableTextBox::OnEditableTextChanged)
.OnTextCommitted(this, &SEditableTextBox::OnEditableTextCommitted)
.MinDesiredWidth( InArgs._MinDesiredWidth )
.SelectAllTextOnCommit( InArgs._SelectAllTextOnCommit )
.SelectWordOnMouseDoubleClick(InArgs._SelectWordOnMouseDoubleClick)
.OnKeyCharHandler( InArgs._OnKeyCharHandler )
.OnKeyDownHandler( InArgs._OnKeyDownHandler )
.VirtualKeyboardType( InArgs._VirtualKeyboardType )
.VirtualKeyboardOptions( InArgs._VirtualKeyboardOptions )
.VirtualKeyboardTrigger( InArgs._VirtualKeyboardTrigger )
.VirtualKeyboardDismissAction( InArgs._VirtualKeyboardDismissAction )
.TextShapingMethod(InArgs._TextShapingMethod)
.TextFlowDirection( InArgs._TextFlowDirection )
.OverflowPolicy(InArgs._OverflowPolicy)
]
]
]
);
ErrorReporting = InArgs._ErrorReporting;
if ( ErrorReporting.IsValid() )
{
Box->AddSlot()
.AutoWidth()
.Padding(3,0)
[
ErrorReporting->AsWidget()
];
}
}
void SEditableTextBox::SetStyle(const FEditableTextBoxStyle* InStyle)
{
Style = InStyle;
if ( Style == nullptr )
{
FArguments Defaults;
Style = Defaults._Style;
}
check(Style);
BorderImageNormal = &Style->BackgroundImageNormal;
BorderImageHovered = &Style->BackgroundImageHovered;
BorderImageFocused = &Style->BackgroundImageFocused;
BorderImageReadOnly = &Style->BackgroundImageReadOnly;
SetTextBlockStyle(&Style->TextStyle);
Invalidate(EInvalidateWidgetReason::Layout);
}
void SEditableTextBox::SetTextBlockStyle(const FTextBlockStyle* InTextStyle)
{
// The Construct() function will call this before EditableText exists,
// so we need a guard here to ignore that function call.
if (EditableText.IsValid())
{
EditableText->SetTextBlockStyle(InTextStyle);
}
}
void SEditableTextBox::SetText( const TAttribute< FText >& InNewText )
{
EditableText->SetText( InNewText );
}
void SEditableTextBox::SetError( const FText& InError )
{
SetError( InError.ToString() );
}
void SEditableTextBox::SetError( const FString& InError )
{
const bool bHaveError = !InError.IsEmpty();
if (!bHaveError)
{
if (ErrorReporting.IsValid())
{
ErrorReporting->AsWidget()->SetVisibility(EVisibility::Collapsed);
ErrorReporting->SetError(FString());
}
return;
}
if ( !ErrorReporting.IsValid() )
{
// No error reporting was specified; make a default one
TSharedPtr<SPopupErrorText> ErrorTextWidget;
Box->AddSlot()
.AutoWidth()
.Padding(3,0)
[
SAssignNew( ErrorTextWidget, SPopupErrorText )
];
ErrorReporting = ErrorTextWidget;
}
ErrorReporting->AsWidget()->SetVisibility(EVisibility::Visible);
ErrorReporting->SetError( InError );
}
void SEditableTextBox::SetOnKeyCharHandler(FOnKeyChar InOnKeyCharHandler)
{
EditableText->SetOnKeyCharHandler(InOnKeyCharHandler);
}
void SEditableTextBox::SetOnKeyDownHandler(FOnKeyDown InOnKeyDownHandler)
{
EditableText->SetOnKeyDownHandler(InOnKeyDownHandler);
}
void SEditableTextBox::SetTextShapingMethod(const TOptional<ETextShapingMethod>& InTextShapingMethod)
{
EditableText->SetTextShapingMethod(InTextShapingMethod);
}
void SEditableTextBox::SetTextFlowDirection(const TOptional<ETextFlowDirection>& InTextFlowDirection)
{
EditableText->SetTextFlowDirection(InTextFlowDirection);
}
void SEditableTextBox::SetOverflowPolicy(TOptional<ETextOverflowPolicy> InOverflowPolicy)
{
EditableText->SetOverflowPolicy(InOverflowPolicy);
}
bool SEditableTextBox::AnyTextSelected() const
{
return EditableText->AnyTextSelected();
}
void SEditableTextBox::SelectAllText()
{
EditableText->SelectAllText();
}
void SEditableTextBox::ClearSelection()
{
EditableText->ClearSelection();
}
FText SEditableTextBox::GetSelectedText() const
{
return EditableText->GetSelectedText();
}
void SEditableTextBox::GoTo(const FTextLocation& NewLocation)
{
EditableText->GoTo(NewLocation);
}
void SEditableTextBox::ScrollTo(const FTextLocation& NewLocation)
{
EditableText->ScrollTo(NewLocation);
}
void SEditableTextBox::BeginSearch(const FText& InSearchText, const ESearchCase::Type InSearchCase, const bool InReverse)
{
EditableText->BeginSearch(InSearchText, InSearchCase, InReverse);
}
void SEditableTextBox::AdvanceSearch(const bool InReverse)
{
EditableText->AdvanceSearch(InReverse);
}
bool SEditableTextBox::HasError() const
{
return ErrorReporting.IsValid() && ErrorReporting->HasError();
}
const FSlateBrush* SEditableTextBox::GetBorderImage() const
{
if ( EditableText->IsTextReadOnly() )
{
return BorderImageReadOnly;
}
else if ( EditableText->HasKeyboardFocus() )
{
return BorderImageFocused;
}
else
{
if ( EditableText->IsHovered() )
{
return BorderImageHovered;
}
else
{
return BorderImageNormal;
}
}
}
bool SEditableTextBox::SupportsKeyboardFocus() const
{
return StaticCastSharedPtr<SWidget>(EditableText)->SupportsKeyboardFocus();
}
bool SEditableTextBox::HasKeyboardFocus() const
{
// Since keyboard focus is forwarded to our editable text, we will test it instead
return SBorder::HasKeyboardFocus() || EditableText->HasKeyboardFocus();
}
FReply SEditableTextBox::OnFocusReceived( const FGeometry& MyGeometry, const FFocusEvent& InFocusEvent )
{
FReply Reply = FReply::Handled();
if ( InFocusEvent.GetCause() != EFocusCause::Cleared )
{
// Forward keyboard focus to our editable text widget
Reply.SetUserFocus(EditableText.ToSharedRef(), InFocusEvent.GetCause());
}
return Reply;
}
FReply SEditableTextBox::OnKeyDown(const FGeometry& MyGeometry, const FKeyEvent& InKeyEvent)
{
FKey Key = InKeyEvent.GetKey();
if (Key == EKeys::Escape && EditableText->HasKeyboardFocus())
{
// Clear focus
return FReply::Handled().SetUserFocus(SharedThis(this), EFocusCause::Cleared);
}
return FReply::Unhandled();
}
FMargin SEditableTextBox::DeterminePadding() const
{
check(Style);
return PaddingOverride.IsSet() ? PaddingOverride.Get() : Style->Padding;
}
FSlateFontInfo SEditableTextBox::DetermineFont() const
{
check(Style);
return FontOverride.IsSet() ? FontOverride.Get() : Style->TextStyle.Font;
}
FSlateColor SEditableTextBox::DetermineBackgroundColor() const
{
check(Style);
return BackgroundColorOverride.IsSet() ? BackgroundColorOverride.Get() : Style->BackgroundColor;
}
FSlateColor SEditableTextBox::DetermineForegroundColor() const
{
check(Style);
if (EditableText->IsTextReadOnly())
{
if (ReadOnlyForegroundColorOverride.IsSet())
{
return ReadOnlyForegroundColorOverride.Get();
}
if (ForegroundColorOverride.IsSet())
{
return ForegroundColorOverride.Get();
}
return Style->ReadOnlyForegroundColor;
}
else if(HasKeyboardFocus())
{
return FocusedForegroundColorOverride.IsSet() ? FocusedForegroundColorOverride.Get() : Style->FocusedForegroundColor;
}
else
{
return ForegroundColorOverride.IsSet() ? ForegroundColorOverride.Get() : Style->ForegroundColor;
}
}
void SEditableTextBox::SetHintText(const TAttribute< FText >& InHintText)
{
EditableText->SetHintText(InHintText);
}
void SEditableTextBox::SetSearchText(const TAttribute<FText>& InSearchText)
{
EditableText->SetSearchText(InSearchText);
}
FText SEditableTextBox::GetSearchText() const
{
return EditableText->GetSearchText();
}
void SEditableTextBox::SetIsReadOnly(TAttribute< bool > InIsReadOnly)
{
EditableText->SetIsReadOnly(InIsReadOnly);
}
void SEditableTextBox::SetIsPassword(TAttribute< bool > InIsPassword)
{
EditableText->SetIsPassword(InIsPassword);
}
void SEditableTextBox::SetFont(const TAttribute<FSlateFontInfo>& InFont)
{
FontOverride = InFont;
}
void SEditableTextBox::SetTextBoxForegroundColor(const TAttribute<FSlateColor>& InForegroundColor)
{
ForegroundColorOverride = InForegroundColor;
}
void SEditableTextBox::SetTextBoxBackgroundColor(const TAttribute<FSlateColor>& InBackgroundColor)
{
BackgroundColorOverride = InBackgroundColor;
}
void SEditableTextBox::SetReadOnlyForegroundColor(const TAttribute<FSlateColor>& InReadOnlyForegroundColor)
{
ReadOnlyForegroundColorOverride = InReadOnlyForegroundColor;
}
void SEditableTextBox::SetFocusedForegroundColor(const TAttribute<FSlateColor>& InFocusedForegroundColor)
{
FocusedForegroundColorOverride = InFocusedForegroundColor;
}
void SEditableTextBox::SetMaximumLength(const TAttribute<int32>& InMaximumLength)
{
MaximumLength = InMaximumLength;
}
void SEditableTextBox::SetMinimumDesiredWidth(const TAttribute<float>& InMinimumDesiredWidth)
{
EditableText->SetMinDesiredWidth(InMinimumDesiredWidth);
}
void SEditableTextBox::SetIsCaretMovedWhenGainFocus(const TAttribute<bool>& InIsCaretMovedWhenGainFocus)
{
EditableText->SetIsCaretMovedWhenGainFocus(InIsCaretMovedWhenGainFocus);
}
void SEditableTextBox::SetSelectAllTextWhenFocused(const TAttribute<bool>& InSelectAllTextWhenFocused)
{
EditableText->SetSelectAllTextWhenFocused(InSelectAllTextWhenFocused);
}
void SEditableTextBox::SetRevertTextOnEscape(const TAttribute<bool>& InRevertTextOnEscape)
{
EditableText->SetRevertTextOnEscape(InRevertTextOnEscape);
}
void SEditableTextBox::SetClearKeyboardFocusOnCommit(const TAttribute<bool>& InClearKeyboardFocusOnCommit)
{
EditableText->SetClearKeyboardFocusOnCommit(InClearKeyboardFocusOnCommit);
}
void SEditableTextBox::SetSelectAllTextOnCommit(const TAttribute<bool>& InSelectAllTextOnCommit)
{
EditableText->SetSelectAllTextOnCommit(InSelectAllTextOnCommit);
}
void SEditableTextBox::SetSelectWordOnMouseDoubleClick(const TAttribute<bool>& InSelectWordOnMouseDoubleClick)
{
EditableText->SetSelectWordOnMouseDoubleClick(InSelectWordOnMouseDoubleClick);
}
void SEditableTextBox::SetJustification(const TAttribute<ETextJustify::Type>& InJustification)
{
EditableText->SetJustification(InJustification);
}
void SEditableTextBox::SetAllowContextMenu(TAttribute<bool> InAllowContextMenu)
{
EditableText->SetAllowContextMenu(InAllowContextMenu);
}
void SEditableTextBox::SetVirtualKeyboardDismissAction(TAttribute<EVirtualKeyboardDismissAction> InVirtualKeyboardDismissAction)
{
EditableText->SetVirtualKeyboardDismissAction(InVirtualKeyboardDismissAction);
}
void SEditableTextBox::EnableTextInputMethodContext()
{
EditableText->EnableTextInputMethodContext();
}
#if WITH_ACCESSIBILITY
TSharedRef<FSlateAccessibleWidget> SEditableTextBox::CreateAccessibleWidget()
{
return MakeShareable<FSlateAccessibleWidget>(new FSlateAccessibleEditableTextBox(SharedThis(this)));
}
TOptional<FText> SEditableTextBox::GetDefaultAccessibleText(EAccessibleType AccessibleType) const
{
// The parent Construct() function will call this before EditableText exists,
// so we need a guard here to ignore that function call.
if (EditableText.IsValid())
{
return EditableText->GetHintText();
}
return TOptional<FText>();
}
#endif
void SEditableTextBox::OnEditableTextChanged(const FText& InText)
{
OnTextChanged.ExecuteIfBound(InText);
const int32 MaximumLengthValue = MaximumLength.Get();
if (OnVerifyTextChanged.IsBound() || MaximumLengthValue >= 0)
{
FText OutErrorMessage;
if (!FTextEditHelper::VerifyTextLength(InText, OutErrorMessage, MaximumLengthValue) ||
(OnVerifyTextChanged.IsBound() && !OnVerifyTextChanged.Execute(InText, OutErrorMessage)))
{
// Display as an error.
SetError(OutErrorMessage);
}
else
{
SetError(FText::GetEmpty());
}
}
}
void SEditableTextBox::OnEditableTextCommitted(const FText& InText, ETextCommit::Type InCommitType)
{
FText OutErrorMessage;
if (!FTextEditHelper::VerifyTextLength(InText, OutErrorMessage, MaximumLength.Get()) ||
(OnVerifyTextChanged.IsBound() && !OnVerifyTextChanged.Execute(InText, OutErrorMessage)))
{
// Display as an error.
if (InCommitType == ETextCommit::OnEnter)
{
SetError(OutErrorMessage);
}
return;
}
// Text commited without errors, so clear error text
SetError(FText::GetEmpty());
OnTextCommitted.ExecuteIfBound(InText, InCommitType);
}