Widget context menu extension

Hi,

We’re working on a custom tooling plugin for a project and I was looking into adding new entries to the context menu that is displayed when you right-click on an element in the Hierarchy tab of a widget.

From what we can see elsewhere in the engine the usual method seems to be to:

  • declare an array of MenuExtender delegates in a module’s header file
  • plugins/modules that want to extend the menu retrieve a module’s MenuExtender array and add new delegates to it
  • the menu that will be extended retrieves the module’s MenuExtender array, executes the delegates and adds FExtenders they return to the menu

The change we are proposing would be to add this code snippet to the UMGEditorModule.h file:

`public:
virtual TArray& GetAllWidgetContextMenuExtenders() { return WidgetContextMenuExtenders; }

private:
TArray WidgetContextMenuExtenders;`

And also add this code snippet to the FWidgetBlueprintEditorUtils::CreateWidgetContextMenu function in the WidgetBlueprintEditorUtils.cpp file like this:

`void FWidgetBlueprintEditorUtils::CreateWidgetContextMenu(FMenuBuilder& MenuBuilder, TSharedRef BlueprintEditor, FVector2D TargetLocation)
{
BlueprintEditor->PasteDropLocation = TargetLocation;

TSet Widgets = BlueprintEditor->GetSelectedWidgets();
UWidgetBlueprint* BP = BlueprintEditor->GetWidgetBlueprintObj();

//------------------------CHANGE START--------------------------------------------------------
IUMGEditorModule& UMGEditorModule = FModuleManager::LoadModuleChecked(“UMGEditor”);
const TArrayIUMGEditorModule::FWidgetContextMenuExtender& MenuExtenderDelegates = UMGEditorModule.GetAllWidgetContextMenuExtenders();

TArray<TSharedPtr> Extenders;
for (int32 i = 0; i < MenuExtenderDelegates.Num(); ++i)
{
if (MenuExtenderDelegates[i].IsBound())
{
Extenders.Add(MenuExtenderDelegates[i].Execute(BlueprintEditor));
}
}

TSharedPtr MenuExtender = FExtender::Combine(Extenders);
MenuBuilder.PushExtender(MenuExtender.ToSharedRef());
//------------------------CHANGE END--------------------------------------------------------

MenuBuilder.BeginSection(“Edit”, LOCTEXT(“Edit”, “Edit”));
{

}
MenuBuilder.EndSection();
}`

This change would benefit all other projects and users that want to add custom actions and functionalities to the widget context menu as you can see in the attached screenshot. Let me know if the change seems appropriate.

Hi,

Apologies for the delay in responding. We had a similar desire to extend the context menu from our UMG Viewmodel plugin, and exposed the necessary extension point in CL#40403357. That change will be in the 5.6 release, and it should be a fairly simple backport if you wanted it sooner (you could skip the bits that are part of the MVVM plugin and just grab the changes to UMGEditor). Our change isn’t too far off from what you proposed, with the biggest difference being the addition of an IWidgetContextMenuExtension interface and FWidgetContextMenuExtensibilityManager to manage extensions dynamically. Let me know if you have any concerns that prevent this approach from working for you.

Best,

Cody

Hi,

No specific plans here, but I can see about getting some of those exposed for 5.7. My guess is that a handful of internal functions were made private initially and other things ended up being added to the bottom of the class, so it should probably be fine to make some of those public.

Hey Cody! Thanks for the info: that’s exactly what I was looking for. I was able to port the change and get my menu extension logic working flawlessly. This approach works fine for me.

Quick follow up question: given that the engine will now allow users to extend the widget context menu, are there plans to expose certain private functions in the WidgetBlueprintEditorUtils class that are being used in the context menu logic like these (which would allow us to avoid duplicating code in order to achieve the same behaviors in custom tooling plugins):

`static void FindUsedVariablesForWidgets(const TSet& Widgets, const UWidgetBlueprint* BP, TArray<UWidget*>& UsedVariables, TArray& WidgetNames, bool bIncludeVariablesOnChildren);

static bool IsDesiredFocusWiget(TSharedRef BlueprintEditor, UWidget* Widget);

static FString FindNextValidName(UWidgetTree* WidgetTree, const FString& Name);

static FString RemoveSuffixFromName(const FString OldName);`It feels weird to have private static functions like this in a Utils class.

Ok sounds good. Thanks for all the info and for taking the time to answer my post!