Reproduce Details Panel UI in C++

Problem: reproduce part of the UI from the Details panel in C++ (Image 1).

Image 1:

Full Details panel for reference:

Instead of the “delete all” button, I would like to have a delete button for each entry, like in the following Details panel:

Question: how can I re-use the UI from the editor to create a window with the elements shown above? The purpose is to enable the user to create pairs of labels-colors that can later be accessed from Blueprint. I’m using UE 5.3.

Here is what has been achieved so far:

Code
SlateWindowLabels.h

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Slate.h"
#include "SlateWindowLabels.generated.h"
/**
 * 
 */
UCLASS()
class POINTANNOTATIONTOOL_API ASlateWindowLabels : public AActor
{
	GENERATED_BODY()

public:
	ASlateWindowLabels();

	UFUNCTION(BlueprintCallable, Category = "LabelCreation")
	void OpenLabelDialog();

	UFUNCTION(BlueprintCallable, Category = "LabelCreation")
    TMap<FString, FLinearColor> GetLabelEntries() const;

private:
    TSharedPtr<class SSlateWidgetLabels> WidgetLabels;
	TSharedPtr<SWindow> Window;
};

SlateWindowLabels.cpp

#include "SlateWindowLabels.h"
#include "SSlateWidgetLabels.h"

ASlateWindowLabels::ASlateWindowLabels()
{
}

void ASlateWindowLabels::OpenLabelDialog()
{
    if (GEngine)
    {
        if (GEngine->GameViewport)
        {
            Window = SNew(SWindow)
            .ClientSize(FVector2D(800, 500))
            .Title(FText::FromString(TEXT("Labels")))
            [
                SAssignNew(WidgetLabels, SSlateWidgetLabels)
            ];
            
            FSlateApplication::Get().AddWindow(Window.ToSharedRef());
        }
    }
}

TMap<FString, FLinearColor> ASlateWindowLabels::GetLabelEntries() const
{
    if (WidgetLabels.IsValid())
    {
        return WidgetLabels->GetEntries();
    }

    return TMap<FString, FLinearColor>();
}

SSlateWidgetLabels.h

#pragma once

#include "CoreMinimal.h"
#include "Widgets/SCompoundWidget.h"
#include "Widgets/DeclarativeSyntaxSupport.h"
#include "Widgets/Input/SEditableTextBox.h"
#include "Widgets/Input/SButton.h"
#include "Widgets/Colors/SColorPicker.h"
#include "Widgets/Layout/SBox.h"
#include "Widgets/Views/SListView.h"

class POINTANNOTATIONTOOL_API SSlateWidgetLabels : public SCompoundWidget
{
public:
	SLATE_BEGIN_ARGS(SSlateWidgetLabels)
	{}
	SLATE_END_ARGS()

	void Construct(const FArguments& InArgs);

	TMap<FString, FLinearColor> GetEntries() const
    {
        return Entries;
    }

private:
    // TMap to store the entries
    TMap<FString, FLinearColor> Entries;

    // Array of shared pointers to display the map entries in the list view
    TArray<TSharedPtr<FString>> EntryKeys;

    TSharedPtr<SListView<TSharedPtr<FString>>> ListView;

    // Function to generate a row for each entry
    TSharedRef<ITableRow> OnGenerateRow(TSharedPtr<FString> InKey, const TSharedRef<STableViewBase>& OwnerTable);

    // Function to add a new entry
    void AddEntry();

    // Function to remove an existing entry
    void RemoveEntry(const FString& Key);

    void PrintEntriesToLog();

};

SSlateWidgetLabels.cpp

#include "SSlateWidgetLabels.h"
#include "Widgets/Text/STextBlock.h"
#include "SlateOptMacros.h"


BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
void SSlateWidgetLabels::Construct(const FArguments& InArgs)
{
    // Add initial entries to the map
    Entries.Add("Label 1", FLinearColor::Red);
    // Populate the EntryKeys array with keys from the map
    for (const auto& Entry : Entries)
    {
        EntryKeys.Add(MakeShareable(new FString(Entry.Key)));
    }
    // Set up the widget layout
    ChildSlot
    [
        SNew(SVerticalBox)

        + SVerticalBox::Slot()
        .AutoHeight()
        [
            SNew(SButton)
            .Text(FText::FromString("Add Label"))
            .OnClicked_Lambda([this]() -> FReply {
                AddEntry();
                return FReply::Handled();
            })
        ]

        + SVerticalBox::Slot()
        .FillHeight(1.0f)
        [
            SAssignNew(ListView, SListView<TSharedPtr<FString>>)
            .ItemHeight(24)
            .ListItemsSource(&EntryKeys)
            .OnGenerateRow(this, &SSlateWidgetLabels::OnGenerateRow)
        ]

        + SVerticalBox::Slot()
        .AutoHeight()
        [
            SNew(SButton)
            .Text(FText::FromString("Next"))
            .OnClicked_Lambda([this]() -> FReply {
                PrintEntriesToLog();
                return FReply::Handled();
            })
        ]
    ];
}

void SSlateWidgetLabels::PrintEntriesToLog()
{
    for (const auto& Entry : Entries)
    {
        UE_LOG(LogTemp, Log, TEXT("Label: %s, Color: %s"), *Entry.Key, *Entry.Value.ToString());
    }
}

TSharedRef<ITableRow> SSlateWidgetLabels::OnGenerateRow(TSharedPtr<FString> InKey, const TSharedRef<STableViewBase>& OwnerTable)
{
    FString Key = *InKey;
    FLinearColor Value = Entries[Key];

    return SNew(STableRow<TSharedPtr<FString>>, OwnerTable)
    [
        SNew(SHorizontalBox)
        + SHorizontalBox::Slot()
        .AutoWidth()
        [
            SNew(SEditableTextBox)
            .Text(FText::FromString(Key))
            .OnTextCommitted_Lambda([this, InKey](const FText& NewText, ETextCommit::Type CommitType) {
                FString NewKey = NewText.ToString();
                if (NewKey != *InKey)
                {
                    FLinearColor Color = Entries[*InKey];
                    Entries.Remove(*InKey);
                    Entries.Add(NewKey, Color);
                    *InKey = NewKey;
                    ListView->RequestListRefresh();
                }
            })
        ]
        + SHorizontalBox::Slot()
        .AutoWidth()
        [
            SNew(SColorPicker)
            .OnColorCommitted_Lambda([this, InKey](FLinearColor NewColor) {
                Entries[*InKey] = NewColor;
            })
        ]
        + SHorizontalBox::Slot()
        .AutoWidth()
        [
            SNew(SButton)
            .Text(FText::FromString("Remove"))
            .OnClicked_Lambda([this, InKey]() -> FReply {
                RemoveEntry(*InKey);
                return FReply::Handled();
            })
        ]
    ];
}

void SSlateWidgetLabels::AddEntry()
{
    FString NewKey = "Label";
    int32 Suffix = 1;
    while (Entries.Contains(NewKey))
    {
        NewKey = FString::Printf(TEXT("Label %d"), Suffix++);
    }

    Entries.Add(NewKey, FLinearColor::White);
    EntryKeys.Add(MakeShareable(new FString(NewKey)));
    ListView->RequestListRefresh();
}

void SSlateWidgetLabels::RemoveEntry(const FString& Key)
{
    Entries.Remove(Key);
    EntryKeys.RemoveAll([&Key](const TSharedPtr<FString>& EntryKey) {
        return *EntryKey == Key;
    });
    ListView->RequestListRefresh();
}

END_SLATE_FUNCTION_BUILD_OPTIMIZATION

Sorry for the long post and thank you for your time!

I just wanted to clarify that by “reproduce” I mean replicate.

Update: I have been able to replicate the UI. I used the tool Widget Reflector to take snapshots of the desired windows, I then picked the widgets and navigated through the widget hierarchy to locate the source code of each element.

I hope this helps someone in the future looking for replicating specific UI elements from the editor.