How to open an Editor Widget Utility tab from code?

Hello!

I have created an UMG editor widget utility blueprint and would like to open it at some point from my plugin C++ code. How can I achieve that?

I took a look into how in Blutility such an editor widget utility is run when you right click it and select “Run Editor utility Widget” in AssetTypeActions_EditorUtilityWidgetBlueprint.cpp". Unfortunately, this module does not export any of its symbols so I can’t link against it… Here was my attempt (inspired from FAssetTypeActions_EditorUtilityWidgetBlueprint::ExecuteRun):

void ULogListWidgetBase::OpenEditorUtilityWidgetWindow(UWidgetBlueprint* Blueprint)
{
	if (Blueprint->GeneratedClass->IsChildOf(UEditorUtilityWidget::StaticClass()))
	{
		const UEditorUtilityWidget* CDO = Blueprint->GeneratedClass->GetDefaultObject<UEditorUtilityWidget>();
		if (CDO->ShouldAutoRunDefaultAction())
		{
			// This is an instant-run blueprint, just execute it
			UEditorUtilityWidget* Instance = NewObject<UEditorUtilityWidget>(GetTransientPackage(), Blueprint->GeneratedClass);
			Instance->ExecuteDefaultAction();
		}
		else
		{
			FName RegistrationName = FName(*(Blueprint->GetPathName() + LOCTEXT("ActiveTabSuffix", "_ActiveTab").ToString()));
			FText DisplayName = FText::FromString(Blueprint->GetName());
			FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked<FLevelEditorModule>(TEXT("LevelEditor"));
			TSharedPtr<FTabManager> LevelEditorTabManager = LevelEditorModule.GetLevelEditorTabManager();
			if (!LevelEditorTabManager->CanSpawnTab(RegistrationName))
			{
				IBlutilityModule* BlutilityModule = FModuleManager::GetModulePtr<IBlutilityModule>("Blutility");
				UEditorUtilityWidgetBlueprint* WidgetBlueprint = Cast<UEditorUtilityWidgetBlueprint>(Blueprint);
				WidgetBlueprint->SetRegistrationName(RegistrationName);
				LevelEditorTabManager->RegisterTabSpawner(RegistrationName, FOnSpawnTab::CreateUObject(WidgetBlueprint, &UEditorUtilityWidgetBlueprint::SpawnEditorUITab))
					.SetDisplayName(DisplayName)
					.SetGroup(BlutilityModule->GetMenuGroup().ToSharedRef());
				BlutilityModule->AddLoadedScriptUI(WidgetBlueprint);
			}
			TSharedRef<SDockTab> NewDockTab = LevelEditorTabManager->InvokeTab(RegistrationName);
		}
	}
}

As I said, I can write this but even when I add Blutility to my list of dependencies, I get the following link errors:

error LNK2019: unresolved external symbol "private: static class UClass * __cdecl UEditorUtilityWidgetBlueprint::GetPrivateStaticClass(void)" (?GetPrivateStaticClass@UEditorUtilityWidgetBlueprint@@CAPEAVUClass@@XZ) referenced in function "public: static void __cdecl ULogListWidgetBase::OpenEditorUtilityWidgetWindow(class UWidgetBlueprint *)" (?OpenEditorUtilityWidgetWindow@ULogListWidgetBase@@SAXPEAVUWidgetBlueprint@@@Z)

error LNK2019: unresolved external symbol "public: class TSharedRef<class SDockTab,0> __cdecl UEditorUtilityWidgetBlueprint::SpawnEditorUITab(class FSpawnTabArgs const &)" (?SpawnEditorUITab@UEditorUtilityWidgetBlueprint@@QEAA?AV?$TSharedRef@VSDockTab@@$0A@@@AEBVFSpawnTabArgs@@@Z) referenced in function "public: static void __cdecl ULogListWidgetBase::OpenEditorUtilityWidgetWindow(class UWidgetBlueprint *)" (?OpenEditorUtilityWidgetWindow@ULogListWidgetBase@@SAXPEAVUWidgetBlueprint@@@Z)

Is it something impossible to do at the moment? Come on, opening the window of a custom editor widget utility… I must have missed something :slight_smile:

1 Like

I have been searching for this forever and I found this here. For some reason I cannot currently seem to get it to work. If you can find a way to get this to work, please let me know what you do.

Hi,

Just had the same issue.
My workaround was the following:

  • In IBlutilityModule.h, I added following public method:

     virtual void StartEditorWidget(class UWidgetBlueprint* Blueprint) const = 0;
    
  • In AssetTypeActions_EditorUtilityWidgetBlueprint.h, I make the method ExecuteRun public

  • Then in BlutilityModule.cpp

    include “AssetTypeActions_Base.h”

    void StartEditorWidget(UWidgetBlueprint* Blueprint) const override
    {
    if (Blueprint != nullptr)
    {
    TArray<UObject*> BlueprintArray;
    BlueprintArray.Add(Blueprint);
    auto Blueprints = FAssetTypeActions_Base::GetTypedWeakObjectPtrs(BlueprintArray);

          	FAssetTypeActions_EditorUtilityWidgetBlueprint obj;
          	obj.ExecuteRun(Blueprints);
          }
    }
    

Now to call a your Editor Widget:

IBlutilityModule* BlutilityModule = FModuleManager::GetModulePtr<IBlutilityModule>("Blutility");

const FStringAssetReference MyWidget_AssetPath("/FolderOfMyWidget/WidgetName.WidgetName");

UObject* MyWidgetObj = MyWidget_AssetPath.TryLoad();
UWidgetBlueprint* MyWidget = Cast<UWidgetBlueprint>(MyWidgetObj );
  StartEditorWidget(MyWidget);

The part where you find the path of your asset can be a bit tricky, fortunately, unreal editor provides an easy way to retrieve it using “Copy Reference” method:

281547-findpath.jpg

Now you just have to CTRL+V your asset path in your IDE and here you go.

And last thing, do not forget to add a few dependencies in your module:
“Blutility”,
“UMG”,
“UMGEditor”,

Hope this helps!

PS: you might need a few other includes and a few other modules to declare for your own plugin to make everything compile but it works!

Now if you find how to close this same window, I am interested!

That’s awesome! Unfortunately this method will not work for me because I am trying to create a Marketplace contribution (meaning I can’t mess with the engine’s source) but that is still awesome that you were able to figure it out.

Here the first snipped is about how to register your tab, into the unreals tab"s manager, not sure if I need the first part of the code, but meh!

void UPipelineStatics::OpenWidgetBlueprint(UWidgetBlueprint* Blueprint)
{
if (!Blueprint) return;

	if (Blueprint->GeneratedClass->IsChildOf(UEditorUtilityWidget::StaticClass()))
	{
		const UEditorUtilityWidget* CDO = Blueprint->GeneratedClass->GetDefaultObject<UEditorUtilityWidget>();
		if (CDO->ShouldAutoRunDefaultAction())
		{
			// This is an instant-run blueprint, just execute it
			UEditorUtilityWidget* Instance = NewObject<UEditorUtilityWidget>(GetTransientPackage(), Blueprint->GeneratedClass);
			Instance->ExecuteDefaultAction();
		}
		else
		{
			FName RegistrationName = FName(*(Blueprint->GetPathName() + TEXT("_ActiveTab")));
			FText DisplayName = FText::FromString(Blueprint->GetName());
			FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked<FLevelEditorModule>(TEXT("LevelEditor"));
			TSharedPtr<FTabManager> LevelEditorTabManager = LevelEditorModule.GetLevelEditorTabManager();
			if (!LevelEditorTabManager->CanSpawnTab(RegistrationName))
			{
				//UEditorUtilityWidgetBlueprint* WidgetBlueprint = Cast<UEditorUtilityWidgetBlueprint>(Blueprint);
				LevelEditorTabManager->RegisterTabSpawner(RegistrationName, FOnSpawnTab::CreateStatic(&UPipelineStatics::SpawnEditorUITab, Blueprint))
					.SetDisplayName(DisplayName)
					.SetMenuType(ETabSpawnerMenuType::Hidden);
			}

			TSharedRef<SDockTab> NewDockTab = LevelEditorTabManager->InvokeTab(RegistrationName);
		}
	}
}

and your will need this function to create your actual widget, looks like is possible create UMG widgets and bind that to any slate, check TakeWidget(); very straightforward

TSharedRef UPipelineStatics::SpawnEditorUITab(const FSpawnTabArgs& SpawnTabArgst, UWidgetBlueprint* Blueprint)
{
TSharedRef SpawnedTab = SNew(SDockTab);
TSubclassOf WidgetClass = Blueprint->GeneratedClass;
UWorld* World = GEditor->GetEditorWorldContext().World();
check(World);

UEditorUtilityWidget* CreatedUMGWidget = CreateWidget<UEditorUtilityWidget>(World, WidgetClass);
if (CreatedUMGWidget)
{
	TSharedRef<SWidget> CreatedSlateWidget = CreatedUMGWidget->TakeWidget();
	SpawnedTab->SetContent(CreatedSlateWidget);
}

return SpawnedTab;

}

Hi, here’s updated solution for UE5.0.3 : Version: 5.0.3-20979098

  1. In your .build.cs, find PrivateDependencyModuleNames.AddRange
    and add these:

“Blutility”,
“UMG”,
“UMGEditor”,

If “private” dont work, then try public.

  1. Delete VS solution and regenerate from the project .exe

  2. include these:

include “EditorUtilityWidget.h”
include “EditorUtilityWidgetBlueprint.h”
include “EditorUtilitySubsystem.h”

  1. Use this:
	// get the  path of the asset by R.Clicking on the asset and 'Copy Reference'
	const FStringAssetReference widgetAssetPath("/LevelValidationTools/EUW_LevelValidator.EUW_LevelValidator");

	UObject* widgetAssetLoaded = widgetAssetPath.TryLoad();
	if (widgetAssetLoaded == nullptr) {
		UE_LOG(LogTemp, Warning, TEXT("Missing Expected widget class at : /LevelValidationTools/EUW_LevelValidator.EUW_LevelValidator"));
		return;
	}

	UEditorUtilityWidgetBlueprint* widget = Cast<UEditorUtilityWidgetBlueprint>(widgetAssetLoaded);
	if (widget == nullptr) {
		UE_LOG(LogTemp, Warning, TEXT("Couldnt cast /LevelValidationTools/EUW_LevelValidator.EUW_LevelValidator to UEditorUtilityWidgetBlueprint"));
		return;
	}

	UEditorUtilitySubsystem* EditorUtilitySubsystem = GEditor->GetEditorSubsystem<UEditorUtilitySubsystem>();
	EditorUtilitySubsystem->SpawnAndRegisterTab(widget);

Compile and jump of happyness.

7 Likes

Thanks, this is very helpful to me

How do you dynamically dock the Editor Utility Widget?