// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "Input/Events.h" #include "Input/Reply.h" #include "Layout/Visibility.h" #include "Widgets/DeclarativeSyntaxSupport.h" #include "Styling/SlateColor.h" #include "Layout/Margin.h" #include "Widgets/SCompoundWidget.h" #include "Widgets/SBoxPanel.h" #include "Styling/SlateTypes.h" #include "Styling/CoreStyle.h" #include "Framework/SlateDelegates.h" #include "Widgets/Images/SImage.h" #include "Widgets/SOverlay.h" #include "Widgets/Input/SEditableTextBox.h" #include "Widgets/Input/SButton.h" #include "Widgets/Input/SComboBox.h" #define LOCTEXT_NAMESPACE "SEditableComboBox" /** * Delegate type for getting the editable combo box text. */ DECLARE_DELEGATE_RetVal(FString, FOnGetEditableComboBoxText) /** * Implements an editable combo box. */ template class SEditableComboBox : public SCompoundWidget { public: typedef typename TSlateDelegates::FOnGenerateWidget FOnGenerateWidget; typedef typename TSlateDelegates::FOnSelectionChanged FOnSelectionChanged; SLATE_BEGIN_ARGS(SEditableComboBox) : _ButtonStyle( &FCoreStyle::Get().GetWidgetStyle< FButtonStyle >( "Button" ) ) , _Content() , _ContentPadding(FMargin(4.0, 2.0)) , _InitiallySelectedItem(nullptr) , _MaxListHeight(450.0f) , _IsRenameVisible(EVisibility::Visible) , _OnGetEditableText() , _OnRemoveClicked() , _OnSelectionChanged() , _OnSelectionRenamed() , _OptionsSource() { } SLATE_ATTRIBUTE(FText, AddButtonToolTip) SLATE_STYLE_ARGUMENT(FButtonStyle, ButtonStyle) SLATE_NAMED_SLOT(FArguments, Content) SLATE_ATTRIBUTE(FMargin, ContentPadding) SLATE_ARGUMENT(OptionType, InitiallySelectedItem) SLATE_ARGUMENT(float, MaxListHeight) SLATE_ARGUMENT(EVisibility, IsRenameVisible) SLATE_EVENT(FOnClicked, OnAddClicked) SLATE_EVENT(FOnGenerateWidget, OnGenerateWidget) SLATE_EVENT(FOnGetEditableComboBoxText, OnGetEditableText) SLATE_EVENT(FOnClicked, OnRemoveClicked) SLATE_EVENT(FOnSelectionChanged, OnSelectionChanged) SLATE_EVENT(FOnTextCommitted, OnSelectionRenamed) SLATE_ARGUMENT(const TArray*, OptionsSource) SLATE_ATTRIBUTE(FText, RemoveButtonToolTip) SLATE_ATTRIBUTE(FText, RenameButtonToolTip) SLATE_END_ARGS() public: /** Clears the combo box selection. */ void ClearSelection( ) { ComboBox->ClearSelection(); } /** * Constructs the widget. * * @param InArgs The Slate argument list. */ void Construct( const FArguments& InArgs ) { OnSelectionRenamed = InArgs._OnSelectionRenamed; OnGetEditableText = InArgs._OnGetEditableText; ChildSlot [ SNew(SHorizontalBox) + SHorizontalBox::Slot() .FillWidth(1.0) [ SNew(SOverlay) + SOverlay::Slot() [ SNew(SHorizontalBox) .Visibility(this, &SEditableComboBox::HandleNormalModeBoxVisibility) + SHorizontalBox::Slot() .FillWidth(1.0) [ // combo box SAssignNew(ComboBox, SComboBox) .ButtonStyle(InArgs._ButtonStyle) .ContentPadding(InArgs._ContentPadding) .InitiallySelectedItem(InArgs._InitiallySelectedItem) .MaxListHeight(InArgs._MaxListHeight) .OptionsSource(InArgs._OptionsSource) .OnGenerateWidget(InArgs._OnGenerateWidget) .OnSelectionChanged(InArgs._OnSelectionChanged) .Content() [ InArgs._Content.Widget ] ] + SHorizontalBox::Slot() .AutoWidth() .Padding(1.0) [ // rename button SNew(SButton) .ContentPadding(2.f) .ForegroundColor(FSlateColor::UseForeground()) .IsEnabled(this, &SEditableComboBox::HandleRemoveRenameButtonIsEnabled) .ButtonStyle( FCoreStyle::Get(), "NoBorder" ) .OnClicked(this, &SEditableComboBox::HandleRenameButtonClicked) .ToolTipText(InArgs._RenameButtonToolTip) .VAlign(VAlign_Center) .Visibility(InArgs._IsRenameVisible) .Content() [ SNew(SImage) .Image(FCoreStyle::Get().GetBrush("EditableComboBox.Rename")) .ColorAndOpacity(FSlateColor::UseForeground()) ] ] ] + SOverlay::Slot() [ SNew(SHorizontalBox) .Visibility(this, &SEditableComboBox::HandleEditModeBoxVisibility) + SHorizontalBox::Slot() .FillWidth(1.0) .Padding(0.0, 0.0, 0.0, 3.0) [ // text box SAssignNew(TextBox, SEditableTextBox) .OnTextCommitted(this, &SEditableComboBox::HandleTextBoxTextCommitted) ] + SHorizontalBox::Slot() .AutoWidth() .Padding(1.0) [ // accept button SNew(SButton) .ContentPadding(2.f) .ForegroundColor(FSlateColor::UseForeground()) .ButtonStyle( FCoreStyle::Get(), "NoBorder" ) .OnClicked(this, &SEditableComboBox::HandleAcceptButtonClicked) .ToolTipText(LOCTEXT("AcceptButtonTooltip", "Accept")) .VAlign(VAlign_Center) .Content() [ SNew(SImage) .Image(FCoreStyle::Get().GetBrush("EditableComboBox.Accept")) .ColorAndOpacity(FSlateColor::UseForeground()) ] ] ] ] + SHorizontalBox::Slot() .AutoWidth() .Padding(1.0) [ // add button SNew(SButton) .ContentPadding(2.f) .ForegroundColor(FSlateColor::UseForeground()) .IsEnabled(this, &SEditableComboBox::HandleAddButtonIsEnabled) .ButtonStyle( FCoreStyle::Get(), "NoBorder" ) .OnClicked(InArgs._OnAddClicked) .ToolTipText(InArgs._AddButtonToolTip) .VAlign(VAlign_Center) .Content() [ SNew(SImage) .Image(FCoreStyle::Get().GetBrush("EditableComboBox.Add")) .ColorAndOpacity(FSlateColor::UseForeground()) ] ] + SHorizontalBox::Slot() .AutoWidth() .Padding(1.0) [ // remove button SNew(SButton) .ContentPadding(2.f) .ForegroundColor(FSlateColor::UseForeground()) .IsEnabled(this, &SEditableComboBox::HandleRemoveRenameButtonIsEnabled) .ButtonStyle( FCoreStyle::Get(), "NoBorder" ) .OnClicked(InArgs._OnRemoveClicked) .ToolTipText(InArgs._RemoveButtonToolTip) .VAlign(VAlign_Center) .Content() [ SNew(SImage) .Image(FCoreStyle::Get().GetBrush("EditableComboBox.Delete")) .ColorAndOpacity(FSlateColor::UseForeground()) ] ] ]; } /** * Gets the item that is currently selected in the combo box. * * @return The selected item, or nullptr if no item is selected. */ OptionType GetSelectedItem( ) { return ComboBox->GetSelectedItem(); } /** * Requests a list refresh after updating options. * * Call SetSelectedItem to update the selected item if required * * @see SetSelectedItem */ void RefreshOptions( ) { EditedItem.Reset(); ComboBox->RefreshOptions(); } /** * Sets the item that is selected in the combo box. * * @param InSelectedItem The item to select. */ void SetSelectedItem( OptionType InSelectedItem ) { EditedItem.Reset(); ComboBox->SetSelectedItem(InSelectedItem); } private: // Callback for getting the enabled state of the 'Add' button. bool HandleAddButtonIsEnabled( ) const { return !EditedItem.IsValid(); } // Callback for clicking the 'Accept' button. FReply HandleAcceptButtonClicked( ) { return FReply::Handled().SetUserFocus(ComboBox->AsShared(), EFocusCause::Mouse); } // Callback for getting the visibility of the edit mode box. EVisibility HandleEditModeBoxVisibility( ) const { return EditedItem.IsValid() ? EVisibility::Visible : EVisibility::Hidden; } // Callback for getting the visibility of the normal mode box. EVisibility HandleNormalModeBoxVisibility( ) const { return EditedItem.IsValid() ? EVisibility::Hidden : EVisibility::Visible; } // Callback for getting the enabled state of the 'Remove' and 'Rename' buttons. bool HandleRemoveRenameButtonIsEnabled( ) const { return (!EditedItem.IsValid() && ComboBox->GetSelectedItem().IsValid()); } // Callback for clicking the 'Rename' button. FReply HandleRenameButtonClicked( ) { if (OnGetEditableText.IsBound()) { EditedItem = ComboBox->GetSelectedItem(); TextBox->SetText( FText::FromString( OnGetEditableText.Execute() )); } return FReply::Handled().SetUserFocus(TextBox->AsShared(), EFocusCause::Mouse); } // Callback for committing the text in the text box. void HandleTextBoxTextCommitted( const FText& CommittedText, ETextCommit::Type CommitType ) { if (EditedItem.IsValid()) { if (CommitType != ETextCommit::OnCleared) { OnSelectionRenamed.ExecuteIfBound(TextBox->GetText(), CommitType); } EditedItem.Reset(); } } private: // Holds the combo box. TSharedPtr > ComboBox; // Holds the currently edited item. OptionType EditedItem; // Holds the text box. TSharedPtr TextBox; private: // Holds a delegate to be invoked before the editable text box is populated and shown. FOnGetEditableComboBoxText OnGetEditableText; // Holds a delegate to be invoked after the text changes have been committed. FOnTextCommitted OnSelectionRenamed; }; #undef LOCTEXT_NAMESPACE