Mysterious UI Stacking behavior when creating drop down menus using SComboButton within Slate UI C++

I’m attempting to create a custom SCompoundWidget which will act as an easy to use drop down menu within Slate UI. I’m doing this in c++.

As it stands, the project compiles and runs. It shows the drop down menu normally enough at first. The issue is that when I click one of the options displayed within the drop down menu, it appears to create a new drop down and stack it on top of the old.

For example, suppose I have the following items within my drop down:

Example
Test
p1

The drop down will open with Example as it is the first and default item within the list. The drop down menu appears with AutoWidth() so it is only as wide as it needs to be. Notice how ‘Example’ is the longest (width wise) item within our drop down menu.

If I then click the drop down, the menu will appear as normal.

Once I select one of the options, say ‘Test’, it looks like a new drop down menu appears stacked on top of the previous menu. It appears stacked because the width of this new menu is shorter as ‘Test’ is shorter width-wise than ‘Example’.

I cannot interact with the previous menu, and in fact, when using the Unreal Engine ‘Widget Inspector’ tool, there is truly only one SComboButton on my widget even though two seem to be there.

The same is true if I then open the drop down and select ‘p1’ which is shorter than ‘Test’. Now it appears that there are three drop down menus stacked on top of each other.

Any help would be appreciated. Below is my code:

/* SDropDown.h */

#pragma once

#include "CoreMinimal.h"

// Represents a single entry within a dropdown menu
struct FMenuEntry
{
	FMenuEntry(FText Label, TFunction<void()> Action = (), FText Tooltip = FText.GetEmpty(), FSlateIcon Icon = FSlateIcon())
	{
		this->Label = Label;
		this->Action = Action;
		this->Tooltip = Tooltip;
		this->Icon = Icon;
	}

	// The text that appears within the drop down menu entry
	FText Label;

	// An optional function to fire when the user selects the given entry
	TFunction<void()> Action;

	// An optional tooltip that appears upon hover
	FText Tooltip;

	// An optional icon to appear to the left
	FSlateIcon Icon;
};

class MYPROJ_API SDropDown : public SCompoundWidget
{
public:
	SLATE_BEGIN_ARGS(SDropDown) :
		_MenuEntries(TArray<FMenuEntry>())
		{ }

		SLATE_ARGUMENT(TArray<FMenuEntry>, MenuEntries)

	SLATE_END_ARGS()

	void Construct(const FArguments& InArgs);
	virtual bool SupportsKeyboardFocus() const override { return true; }

private:
	// Represents all entries within the drop down menu.  Passed in via SLATE_ARGUMENT
	TArray<FMenuEntry> MenuEntries;

	// This is the text shown on the drop down button
	FText FeaturedItem;

	// This is the drop down menu slate widget
	TSharedPtr<SComboButton> ComboButton;

	// This function is for binding the ComboButtons Button Text to the FeaturedItem
	FText GetFeaturedItem() const;
};
/* SDropDown.cpp */

#include "SDropDown.h"

void SDropDown::Construct(const FArguments& InArgs)
{
	// Bind the arguments to member variable
	this->MenuEntries = InArgs._MenuEntries;

	if(!MenuEntries.Num())
	{
		UE_LOG(LogSlate, Fatal, TEXT("You didn't include any menu entries within your menu.  Enter at least one..."));
		return;
	}

	// Set the default value of the drop down menu to the first item in the list
	FeaturedItem = MenuEntries[0].Label;

	FMenuBuilder Menu(true, nullptr);
	for(const FMenuEntry Entry : MenuEntries)
	{
		// Add a menu entry for every item in MenuEntries
		Menu.AddMenuEntry(Entry.Label, Entry.Tooltip, Entry.Icon, FUIAction(FExecuteAction::CreateLambda( [this, Entry] ()
			{
				// Set the FeaturedItem to the clicked on Label
				// This changes because we bind the ButtonContent STextBlock Text to be whatever FeaturedItem is
				if(ComboButton.IsValid())
				{
					FeaturedItem = Entry.Label;
				}

				// Execute whatever other function was passed in
				if(Entry.Action)
				{
					Entry.Action();
				}
			}
		)));
	}

	ChildSlot
	[
		SAssignNew(ComboButton, SComboButton)
		.ButtonContent()
		[
			SNew(STextBlock)
			.Text(this, &SDropDown::GetFeaturedItem) // This line binds the FeaturedItem to this text so whenever the variable changes, so does this text
		]
		.MenuContent()
		[
			Menu.MakeWidget()
		]
	];
}

FText SDropDown::GetFeaturedItem() const
{
	return FeaturedItem;
}

I figured it out, and it actually had nothing to do with what was happening in my code here. When I created the SWindow that used this SDropDown widget, the EWindoType was set to Menu. Apparently, and I don’t know why, that causes or is somehow involved in that stacking behavior. To fix, I had to set the EWindowType to Normal. Again I don’t know why, but this worked fine.

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.