Files
UnrealEngineUWP/Engine/Source/Editor/EditorWidgets/Private/SEnumCombo.cpp
sebastian nordgren c9017e4b69 SEnumCombo now only dismisses its own menu when an option is selected.
This was causing issues when an SEnumCombo was embedded into a menu builder, because the top-level menu was keeping the SEnumCombo alive, causing the OnEnumSelectionChangedDelegate to never fire.

Reported from UDN: https://epicgames.lightning.force.com/lightning/r/Case/5004z00001eEsHSAA0/view

#review-20322055 @lauren.barnes, @mikko.mononen
#preflight 628b9e7d573a7de2c4272968

[CL 20328054 by sebastian nordgren in ue5-main branch]
2022-05-23 10:54:07 -04:00

203 lines
5.2 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "SEnumCombo.h"
#include "Styling/AppStyle.h"
#include "Types/SlateEnums.h"
#include "Widgets/SToolTip.h"
#include "Framework/MultiBox/MultiBoxBuilder.h"
#define LOCTEXT_NAMESPACE "EditorWidgets"
void SEnumComboBox::Construct(const FArguments& InArgs, const UEnum* InEnum)
{
static const FName UseEnumValuesAsMaskValuesInEditorName(TEXT("UseEnumValuesAsMaskValuesInEditor"));
Enum = InEnum;
CurrentValue = InArgs._CurrentValue;
check(CurrentValue.IsBound());
OnEnumSelectionChangedDelegate = InArgs._OnEnumSelectionChanged;
OnGetToolTipForValue = InArgs._OnGetToolTipForValue;
Font = InArgs._Font;
bUpdatingSelectionInternally = false;
bIsBitflagsEnum = Enum->HasMetaData(TEXT("Bitflags"));
if (bIsBitflagsEnum)
{
const bool bUseEnumValuesAsMaskValues = Enum->GetBoolMetaData(UseEnumValuesAsMaskValuesInEditorName);
const int32 BitmaskBitCount = sizeof(int32) << 3;
for (int32 i = 0; i < Enum->NumEnums() - 1; i++)
{
// Note: SEnumComboBox API prior to bitflags only supports 32 bit values, truncating the value here to keep the old API.
int32 Value = Enum->GetValueByIndex(i);
const bool bIsHidden = Enum->HasMetaData(TEXT("Hidden"), i);
if (Value >= 0 && !bIsHidden)
{
if (bUseEnumValuesAsMaskValues)
{
if (Value >= MAX_int32 || !FMath::IsPowerOfTwo(Value))
{
continue;
}
}
else
{
if (Value >= BitmaskBitCount)
{
continue;
}
Value = 1 << Value;
}
FText DisplayName = Enum->GetDisplayNameTextByIndex(i);
FText TooltipText = Enum->GetToolTipTextByIndex(i);
if (TooltipText.IsEmpty())
{
TooltipText = FText::Format(LOCTEXT("BitmaskDefaultFlagToolTipText", "Toggle {0} on/off"), DisplayName);
}
VisibleEnums.Emplace(i, Value, DisplayName, TooltipText);
}
}
}
else
{
for (int32 i = 0; i < Enum->NumEnums() - 1; i++)
{
if (Enum->HasMetaData(TEXT("Hidden"), i) == false)
{
VisibleEnums.Emplace(i, Enum->GetValueByIndex(i), Enum->GetDisplayNameTextByIndex(i), Enum->GetToolTipTextByIndex(i));
}
}
}
SComboButton::Construct(SComboButton::FArguments()
.ButtonStyle(InArgs._ButtonStyle)
.ContentPadding(InArgs._ContentPadding)
.OnGetMenuContent(this, &SEnumComboBox::OnGetMenuContent)
.ButtonContent()
[
SNew(STextBlock)
.Font(Font)
.Text(this, &SEnumComboBox::GetCurrentValueText)
.ToolTipText(this, &SEnumComboBox::GetCurrentValueTooltip)
]);
}
FText SEnumComboBox::GetCurrentValueText() const
{
if (bIsBitflagsEnum)
{
if (CurrentValue.IsSet())
{
const int32 BitmaskValue = CurrentValue.Get();
if (BitmaskValue != 0)
{
TArray<FText> SetFlags;
SetFlags.Reserve(VisibleEnums.Num());
for (const FEnumInfo& FlagInfo : VisibleEnums)
{
if ((BitmaskValue & FlagInfo.Value) != 0)
{
SetFlags.Add(FlagInfo.DisplayName);
}
}
if (SetFlags.Num() > 3)
{
SetFlags.SetNum(3);
SetFlags.Add(FText::FromString("..."));
}
return FText::Join(FText::FromString(" | "), SetFlags);
}
return LOCTEXT("BitmaskButtonContentNoFlagsSet", "(No Flags Set)");
}
return FText::GetEmpty();
}
const int32 ValueNameIndex = Enum->GetIndexByValue(CurrentValue.Get());
return Enum->GetDisplayNameTextByIndex(ValueNameIndex);
}
FText SEnumComboBox::GetCurrentValueTooltip() const
{
if (bIsBitflagsEnum)
{
const int32 BitmaskValue = CurrentValue.Get();
if (BitmaskValue != 0)
{
TArray<FText> SetFlags;
SetFlags.Reserve(VisibleEnums.Num());
for (const FEnumInfo& FlagInfo : VisibleEnums)
{
if ((BitmaskValue & FlagInfo.Value) != 0)
{
if (OnGetToolTipForValue.IsBound())
{
SetFlags.Add(OnGetToolTipForValue.Execute(FlagInfo.Index));
}
else
{
SetFlags.Add(FlagInfo.DisplayName);
}
}
}
return FText::Join(FText::FromString(" | "), SetFlags);
}
return FText::GetEmpty();
}
const int32 ValueNameIndex = Enum->GetIndexByValue(CurrentValue.Get());
if (OnGetToolTipForValue.IsBound())
{
return OnGetToolTipForValue.Execute(ValueNameIndex);
}
return Enum->GetToolTipTextByIndex(ValueNameIndex);
}
TSharedRef<SWidget> SEnumComboBox::OnGetMenuContent()
{
const bool bCloseAfterSelection = !bIsBitflagsEnum;
FMenuBuilder MenuBuilder(bCloseAfterSelection, nullptr, nullptr, true);
for (const FEnumInfo& FlagInfo : VisibleEnums)
{
MenuBuilder.AddMenuEntry(
FlagInfo.DisplayName,
FlagInfo.TooltipText,
FSlateIcon(),
FUIAction
(
FExecuteAction::CreateLambda([this, FlagInfo]()
{
if (bIsBitflagsEnum)
{
// Toggle value
const int32 Value = CurrentValue.Get() ^ FlagInfo.Value;
OnEnumSelectionChangedDelegate.ExecuteIfBound(Value, ESelectInfo::Direct);
}
else
{
// Set value
OnEnumSelectionChangedDelegate.ExecuteIfBound(FlagInfo.Value, ESelectInfo::OnMouseClick);
}
}),
FCanExecuteAction(),
FIsActionChecked::CreateLambda([this, FlagInfo]() -> bool
{
return (CurrentValue.Get() & FlagInfo.Value) != 0;
})
),
NAME_None,
bIsBitflagsEnum ? EUserInterfaceActionType::Check : EUserInterfaceActionType::None);
}
return MenuBuilder.MakeWidget();
}
#undef LOCTEXT_NAMESPACE