Files
UnrealEngineUWP/Engine/Source/Runtime/UMG/Private/Components/ComboBoxString.cpp
daren cheng 4af0ec6765 Fix editor utility widget style changes not saving due to UMG style CDO comparisions reverting changes.
Also fixes issue where any 32x32 button texture would get shrunk to zero on editor utility widgets.

#preflight 620441552f56bd0ebf9fc84e
#rb vincent.gauthier lauren.barnes
#lockdown JeanMichel.Dignard


#ROBOMERGE-AUTHOR: daren.cheng
#ROBOMERGE-SOURCE: CL 18926528 via CL 18926776 via CL 18928248 via CL 18929495
#ROBOMERGE-BOT: UE5 (Release-Engine-Test -> Main) (v916-18915374)

[CL 18929501 by daren cheng in ue5-main branch]
2022-02-09 19:39:40 -05: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>("EditorUtilityComboBox"));
// 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 = ItemStyle.TextColor;
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