Files
UnrealEngineUWP/Engine/Source/Runtime/UMG/Private/Components/ComboBoxString.cpp
daren cheng 36cbbda965 Fix checkbox style on editor utility widgets.
#jira UE-117887
#rb Vincent.Gauthier
#preflight 6148f759315f54000134958f

[CL 17576768 by daren cheng in ue5-main branch]
2021-09-20 18:38:24 -04:00

388 lines
9.0 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "Components/ComboBoxString.h"
#include "Widgets/SNullWidget.h"
#include "UObject/EditorObjectVersion.h"
#include "UObject/ConstructorHelpers.h"
#include "Engine/Font.h"
#include "Styling/UMGCoreStyle.h"
#define LOCTEXT_NAMESPACE "UMG"
/////////////////////////////////////////////////////
// UComboBoxString
static FComboBoxStyle* DefaultComboBoxStyle = nullptr;
static FTableRowStyle* DefaultComboBoxRowStyle = nullptr;
#if WITH_EDITOR
static FComboBoxStyle* EditorComboBoxStyle = nullptr;
static FTableRowStyle* EditorComboBoxRowStyle = nullptr;
#endif
UComboBoxString::UComboBoxString(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
if (DefaultComboBoxStyle == nullptr)
{
DefaultComboBoxStyle = new FComboBoxStyle(FUMGCoreStyle::Get().GetWidgetStyle<FComboBoxStyle>("ComboBox"));
// Unlink UMG default colors.
DefaultComboBoxStyle->UnlinkColors();
}
if (DefaultComboBoxRowStyle == nullptr)
{
DefaultComboBoxRowStyle = new FTableRowStyle(FUMGCoreStyle::Get().GetWidgetStyle<FTableRowStyle>("TableView.Row"));
// Unlink UMG default colors.
DefaultComboBoxRowStyle->UnlinkColors();
}
WidgetStyle = *DefaultComboBoxStyle;
ItemStyle = *DefaultComboBoxRowStyle;
#if WITH_EDITOR
if (EditorComboBoxStyle == nullptr)
{
EditorComboBoxStyle = new FComboBoxStyle(FCoreStyle::Get().GetWidgetStyle<FComboBoxStyle>("ComboBox"));
// Unlink UMG Editor colors from the editor settings colors.
EditorComboBoxStyle->UnlinkColors();
}
if (EditorComboBoxRowStyle == nullptr)
{
EditorComboBoxRowStyle = new FTableRowStyle(FCoreStyle::Get().GetWidgetStyle<FTableRowStyle>("TableView.Row"));
// Unlink UMG Editor colors from the editor settings colors.
EditorComboBoxRowStyle->UnlinkColors();
}
if (IsEditorWidget())
{
WidgetStyle = *EditorComboBoxStyle;
ItemStyle = *EditorComboBoxRowStyle;
// The CDO isn't an editor widget and thus won't use the editor style, call post edit change to mark difference from CDO
PostEditChange();
}
#endif // WITH_EDITOR
WidgetStyle.UnlinkColors();
ItemStyle.UnlinkColors();
ForegroundColor = FLinearColor::Black;
bIsFocusable = true;
ContentPadding = FMargin(4.0, 2.0);
MaxListHeight = 450.0f;
HasDownArrow = true;
EnableGamepadNavigationMode = true;
// We don't want to try and load fonts on the server.
if ( !IsRunningDedicatedServer() )
{
static ConstructorHelpers::FObjectFinder<UFont> RobotoFontObj(*UWidget::GetDefaultFontName());
Font = FSlateFontInfo(RobotoFontObj.Object, 16, FName("Bold"));
}
}
void UComboBoxString::PostInitProperties()
{
Super::PostInitProperties();
// Initialize the set of options from the default set only once.
for (const FString& DefaultOption : DefaultOptions)
{
AddOption(DefaultOption);
}
}
void UComboBoxString::ReleaseSlateResources(bool bReleaseChildren)
{
Super::ReleaseSlateResources(bReleaseChildren);
MyComboBox.Reset();
ComboBoxContent.Reset();
}
void UComboBoxString::Serialize(FArchive& Ar)
{
Super::Serialize(Ar);
Ar.UsingCustomVersion(FEditorObjectVersion::GUID);
}
void UComboBoxString::PostLoad()
{
Super::PostLoad();
// Initialize the set of options from the default set only once.
for (const FString& DefaultOption : DefaultOptions)
{
AddOption(DefaultOption);
}
if (GetLinkerCustomVersion(FEditorObjectVersion::GUID) < FEditorObjectVersion::ComboBoxControllerSupportUpdate)
{
EnableGamepadNavigationMode = false;
}
}
TSharedRef<SWidget> UComboBoxString::RebuildWidget()
{
int32 InitialIndex = FindOptionIndex(SelectedOption);
if ( InitialIndex != -1 )
{
CurrentOptionPtr = Options[InitialIndex];
}
MyComboBox =
SNew(SComboBox< TSharedPtr<FString> >)
.ComboBoxStyle(&WidgetStyle)
.ItemStyle(&ItemStyle)
.ForegroundColor(ForegroundColor)
.OptionsSource(&Options)
.InitiallySelectedItem(CurrentOptionPtr)
.ContentPadding(ContentPadding)
.MaxListHeight(MaxListHeight)
.HasDownArrow(HasDownArrow)
.EnableGamepadNavigationMode(EnableGamepadNavigationMode)
.OnGenerateWidget(BIND_UOBJECT_DELEGATE(SComboBox< TSharedPtr<FString> >::FOnGenerateWidget, HandleGenerateWidget))
.OnSelectionChanged(BIND_UOBJECT_DELEGATE(SComboBox< TSharedPtr<FString> >::FOnSelectionChanged, HandleSelectionChanged))
.OnComboBoxOpening(BIND_UOBJECT_DELEGATE(FOnComboBoxOpening, HandleOpening))
.IsFocusable(bIsFocusable)
[
SAssignNew(ComboBoxContent, SBox)
];
if ( InitialIndex != -1 )
{
// Generate the widget for the initially selected widget if needed
UpdateOrGenerateWidget(CurrentOptionPtr);
}
return MyComboBox.ToSharedRef();
}
void UComboBoxString::AddOption(const FString& Option)
{
Options.Add(MakeShareable(new FString(Option)));
RefreshOptions();
}
bool UComboBoxString::RemoveOption(const FString& Option)
{
int32 OptionIndex = FindOptionIndex(Option);
if ( OptionIndex != -1 )
{
if ( Options[OptionIndex] == CurrentOptionPtr )
{
ClearSelection();
}
Options.RemoveAt(OptionIndex);
RefreshOptions();
return true;
}
return false;
}
int32 UComboBoxString::FindOptionIndex(const FString& Option) const
{
for ( int32 OptionIndex = 0; OptionIndex < Options.Num(); OptionIndex++ )
{
const TSharedPtr<FString>& OptionAtIndex = Options[OptionIndex];
if ( ( *OptionAtIndex ) == Option )
{
return OptionIndex;
}
}
return -1;
}
FString UComboBoxString::GetOptionAtIndex(int32 Index) const
{
if (Index >= 0 && Index < Options.Num())
{
return *(Options[Index]);
}
return FString();
}
void UComboBoxString::ClearOptions()
{
ClearSelection();
Options.Empty();
if ( MyComboBox.IsValid() )
{
MyComboBox->RefreshOptions();
}
}
void UComboBoxString::ClearSelection()
{
CurrentOptionPtr.Reset();
if ( MyComboBox.IsValid() )
{
MyComboBox->ClearSelection();
}
if ( ComboBoxContent.IsValid() )
{
ComboBoxContent->SetContent(SNullWidget::NullWidget);
}
}
void UComboBoxString::RefreshOptions()
{
if ( MyComboBox.IsValid() )
{
MyComboBox->RefreshOptions();
}
}
void UComboBoxString::SetSelectedOption(FString Option)
{
int32 InitialIndex = FindOptionIndex(Option);
SetSelectedIndex(InitialIndex);
}
void UComboBoxString::SetSelectedIndex(const int32 Index)
{
if (Options.IsValidIndex(Index))
{
CurrentOptionPtr = Options[Index];
// Don't select item if its already selected
if (SelectedOption != *CurrentOptionPtr)
{
SelectedOption = *CurrentOptionPtr;
if (ComboBoxContent.IsValid())
{
MyComboBox->SetSelectedItem(CurrentOptionPtr);
UpdateOrGenerateWidget(CurrentOptionPtr);
}
else
{
HandleSelectionChanged(CurrentOptionPtr, ESelectInfo::Direct);
}
}
}
}
FString UComboBoxString::GetSelectedOption() const
{
if (CurrentOptionPtr.IsValid())
{
return *CurrentOptionPtr;
}
return FString();
}
int32 UComboBoxString::GetSelectedIndex() const
{
if (CurrentOptionPtr.IsValid())
{
for (int32 OptionIndex = 0; OptionIndex < Options.Num(); ++OptionIndex)
{
if (Options[OptionIndex] == CurrentOptionPtr)
{
return OptionIndex;
}
}
}
return -1;
}
int32 UComboBoxString::GetOptionCount() const
{
return Options.Num();
}
bool UComboBoxString::IsOpen() const
{
return MyComboBox.IsValid() && MyComboBox->IsOpen();
}
void UComboBoxString::UpdateOrGenerateWidget(TSharedPtr<FString> Item)
{
// If no custom widget was supplied and the default STextBlock already exists,
// just update its text instead of rebuilding the widget.
if (DefaultComboBoxContent.IsValid() && (IsDesignTime() || OnGenerateWidgetEvent.IsBound()))
{
const FString StringItem = Item.IsValid() ? *Item : FString();
DefaultComboBoxContent.Pin()->SetText(FText::FromString(StringItem));
}
else
{
DefaultComboBoxContent.Reset();
ComboBoxContent->SetContent(HandleGenerateWidget(Item));
}
}
TSharedRef<SWidget> UComboBoxString::HandleGenerateWidget(TSharedPtr<FString> Item) const
{
FString StringItem = Item.IsValid() ? *Item : FString();
// Call the user's delegate to see if they want to generate a custom widget bound to the data source.
if ( !IsDesignTime() && OnGenerateWidgetEvent.IsBound() )
{
UWidget* Widget = OnGenerateWidgetEvent.Execute(StringItem);
if ( Widget != NULL )
{
return Widget->TakeWidget();
}
}
// If a row wasn't generated just create the default one, a simple text block of the item's name.
return SNew(STextBlock)
.Text(FText::FromString(StringItem))
.Font(Font);
}
void UComboBoxString::HandleSelectionChanged(TSharedPtr<FString> Item, ESelectInfo::Type SelectionType)
{
CurrentOptionPtr = Item;
SelectedOption = CurrentOptionPtr.IsValid() ? CurrentOptionPtr.ToSharedRef().Get() : FString();
// When the selection changes we always generate another widget to represent the content area of the combobox.
if ( ComboBoxContent.IsValid() )
{
UpdateOrGenerateWidget(CurrentOptionPtr);
}
if ( !IsDesignTime() )
{
OnSelectionChanged.Broadcast(Item.IsValid() ? *Item : FString(), SelectionType);
}
}
void UComboBoxString::HandleOpening()
{
OnOpening.Broadcast();
}
#if WITH_EDITOR
const FText UComboBoxString::GetPaletteCategory()
{
return LOCTEXT("Input", "Input");
}
#endif
/////////////////////////////////////////////////////
#undef LOCTEXT_NAMESPACE