UE5 c++将自定义UserWdiget添加到对应菜单栏

前言:

为了实现与UserWidget一致的右键添加,便有了此章
注:这里使用的是UE5.3

目标内容:

这里可以参考UserWidget的源码,拷贝一份属于自己的就ok(本篇章只是全改成了属于自己的CommonUserWidget)

Runtime模块需要添加内容

首先创建一份自己Runtime模块的对象,我这取名是UCommonUserWidget

.h

#pragma once

#include "Blueprint/UserWidget.h"
#include "CommonUserWidget.generated.h"

UCLASS(BlueprintType, Blueprintable)
class DIVINEPROLOGUE_API UCommonUserWidget : public UUserWidget
{
	GENERATED_BODY()
public:
	}
};

主要实现内容是:UFactory
这里需要创建一个Editor模块,添加以下代码:

.h

//这个创建是因为本着改了都改,就创建了一份
#pragma once
#include "WidgetBlueprint.h"
#include "CommonWidgetBlueprint.generated.h"

/**
 * The widget blueprint enables extending UCommonWidgetBlueprint the user extensible UWidget.
 */
UCLASS(BlueprintType)
class UCommonWidgetBlueprint : public UWidgetBlueprint
{
	GENERATED_BODY()
public:
	UCommonWidgetBlueprint(){}
};

.h

// Copyright Epic Games, Inc. All Rights Reserved.

#pragma once

#include "CoreMinimal.h"
#include "UObject/ObjectMacros.h"
#include "Templates/SubclassOf.h"
#include "Factories/Factory.h"
#include "Engine/Blueprint.h"
#include "CommonUserWidgetFactory.generated.h"

UCLASS(HideCategories=Object, MinimalAPI)
class UCommonUserWidgetFactory : public UFactory
{
	GENERATED_UCLASS_BODY()

	// The type of blueprint that will be created
	UPROPERTY(EditAnywhere, Category=WidgetBlueprintFactory)
	TEnumAsByte<enum EBlueprintType> BlueprintType;

	// The parent class of the created blueprint
	UPROPERTY(EditAnywhere, Category=WidgetBlueprintFactory, meta=(AllowAbstract = ""))
	TSubclassOf<class UUserWidget> ParentClass;

	//~ Begin UFactory Interface
	virtual bool ConfigureProperties() override;
	virtual bool ShouldShowInNewMenu() const override;
	virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn, FName CallingContext) override;
	virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override;
	//~ Begin UFactory Interface	

	private:
	UPROPERTY(Transient)
	TObjectPtr<UClass> RootWidgetClass;
};

.cpp

// Copyright Epic Games, Inc. All Rights Reserved.

#include "CommonUserWidgetFactory.h"
#include "UObject/Interface.h"
#include "Misc/MessageDialog.h"
#include "Blueprint/UserWidget.h"
#include "Blueprint/WidgetBlueprintGeneratedClass.h"
#include "WidgetBlueprint.h"
#include "Kismet2/KismetEditorUtilities.h"
#include "Modules/ModuleManager.h"
#include "UMGEditorModule.h"

#include "Blueprint/WidgetTree.h"
#include "UMGEditorProjectSettings.h"
#include "ClassViewerModule.h"
#include "Kismet2/SClassPickerDialog.h"
#include "ClassViewerFilter.h"
#include "CommonUserWidget.h"
#include "CommonWidgetBlueprint.h"
#include "Components/CanvasPanel.h"

#define LOCTEXT_NAMESPACE "UCommonUserWidgetFactory"

/*------------------------------------------------------------------------------
	UCommonUserWidgetFactory implementation.
------------------------------------------------------------------------------*/

class FWidgetClassFilter : public IClassViewerFilter
{
public:
	/** All children of these classes will be included unless filtered out by another setting. */
	TSet <const UClass*> AllowedChildrenOfClasses;

	/** Disallowed class flags. */
	EClassFlags DisallowedClassFlags;

	virtual bool IsClassAllowed(const FClassViewerInitializationOptions& InInitOptions, const UClass* InClass, TSharedRef< FClassViewerFilterFuncs > InFilterFuncs) override
	{
		return !InClass->HasAnyClassFlags(DisallowedClassFlags)
			&& InFilterFuncs->IfInChildOfClassesSet(AllowedChildrenOfClasses, InClass) != EFilterReturn::Failed;
	}
	
	virtual bool IsUnloadedClassAllowed(const FClassViewerInitializationOptions& InInitOptions, const TSharedRef< const IUnloadedBlueprintData > InUnloadedClassData, TSharedRef< FClassViewerFilterFuncs > InFilterFuncs) override
	{
		return !InUnloadedClassData->HasAnyClassFlags(DisallowedClassFlags)
			&& InFilterFuncs->IfInChildOfClassesSet(AllowedChildrenOfClasses, InUnloadedClassData) != EFilterReturn::Failed;
	}

};

UCommonUserWidgetFactory::UCommonUserWidgetFactory(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
	bCreateNew = true;
	bEditAfterNew = true;
	SupportedClass = UCommonWidgetBlueprint::StaticClass();
	ParentClass = nullptr;
}

bool UCommonUserWidgetFactory::ConfigureProperties()
{
	if (GetDefault<UUMGEditorProjectSettings>()->bUseUserWidgetParentClassViewerSelector || GetDefault<UUMGEditorProjectSettings>()->bUseUserWidgetParentDefaultClassViewerSelector)
	{
		FClassViewerModule& ClassViewerModule = FModuleManager::LoadModuleChecked<FClassViewerModule>("ClassViewer");

		// Fill in options
		FClassViewerInitializationOptions Options;
		Options.DisplayMode = EClassViewerDisplayMode::Type::TreeView;
		Options.Mode = EClassViewerMode::ClassPicker;
		Options.bShowNoneOption = false;
		Options.bExpandAllNodes = true;
		Options.bShowDefaultClasses = GetDefault<UUMGEditorProjectSettings>()->bUseUserWidgetParentDefaultClassViewerSelector;
		Options.bShowClassesViewer = GetDefault<UUMGEditorProjectSettings>()->bUseUserWidgetParentClassViewerSelector;

		TSharedPtr<FWidgetClassFilter> Filter = MakeShareable(new FWidgetClassFilter);
		Options.ClassFilters.Add(Filter.ToSharedRef());

		const TArray<TSoftClassPtr<UUserWidget>>& FavoriteWidgetParentClasses = GetDefault<UUMGEditorProjectSettings>()->FavoriteWidgetParentClasses;
		for (int32 Index = 0; Index < FavoriteWidgetParentClasses.Num(); ++Index)
		{
			UClass* FavoriteWidgetParentClass = FavoriteWidgetParentClasses[Index].LoadSynchronous();
			if (FavoriteWidgetParentClass && FavoriteWidgetParentClass->IsChildOf(UCommonUserWidget::StaticClass()))
			{
				if (!Options.ExtraPickerCommonClasses.Contains(FavoriteWidgetParentClass))
				{
					Options.ExtraPickerCommonClasses.Add(FavoriteWidgetParentClass);
				}
			}
		}

		if (Options.ExtraPickerCommonClasses.Num() == 0)
		{
			Options.ExtraPickerCommonClasses.Add(UCommonUserWidget::StaticClass());
		}

		Filter->DisallowedClassFlags = CLASS_Deprecated | CLASS_NewerVersionExists | CLASS_Hidden | CLASS_HideDropDown;
		Filter->AllowedChildrenOfClasses.Add(UCommonUserWidget::StaticClass());

		const FText TitleText = LOCTEXT("CreateCommonWidgetBlueprint", "Pick Parent Class for New  Widget Blueprint");

		UClass* ChosenParentClass = nullptr;
		bool isSuccessful = SClassPickerDialog::PickClass(TitleText, Options, ChosenParentClass, UCommonUserWidget::StaticClass());
		ParentClass = ChosenParentClass ? ChosenParentClass : UCommonUserWidget::StaticClass();

		if (!isSuccessful)
		{
			return false;
		}
	}

	if (GetDefault<UUMGEditorProjectSettings>()->bUseWidgetTemplateSelector)
	{
		// Load the classviewer module to display a class picker
		FClassViewerModule& ClassViewerModule = FModuleManager::LoadModuleChecked<FClassViewerModule>("ClassViewer");

		// Fill in options
		FClassViewerInitializationOptions Options;
		Options.Mode = EClassViewerMode::ClassPicker;
		Options.bShowNoneOption = true;

		TArray<TSoftClassPtr<UPanelWidget>> CommonRootWidgetClasses = GetDefault <UUMGEditorProjectSettings>()->CommonRootWidgetClasses;
		for (int32 Index = 0; Index < CommonRootWidgetClasses.Num(); ++Index)
		{
			UClass* PanelWidgetClass = CommonRootWidgetClasses[Index].LoadSynchronous();
			if (PanelWidgetClass && PanelWidgetClass->IsChildOf(UPanelWidget::StaticClass()))
			{
				if (!Options.ExtraPickerCommonClasses.Contains(PanelWidgetClass))
				{
					Options.ExtraPickerCommonClasses.Add(PanelWidgetClass);
				}
			}
		}

		if (Options.ExtraPickerCommonClasses.Num() == 0)
		{
			Options.ExtraPickerCommonClasses.Add(UCanvasPanel::StaticClass());
		}

		TSharedPtr<FWidgetClassFilter> Filter = MakeShareable(new FWidgetClassFilter);
		Options.ClassFilters.Add(Filter.ToSharedRef());

		Filter->DisallowedClassFlags = CLASS_Abstract | CLASS_Deprecated | CLASS_NewerVersionExists;
		Filter->AllowedChildrenOfClasses.Add(UPanelWidget::StaticClass());

		const FText TitleText = LOCTEXT("CreateRootWidgetBlueprint", "Pick Root Widget for New Widget Blueprint");
		return SClassPickerDialog::PickClass(TitleText, Options, static_cast<UClass*&>(RootWidgetClass), UPanelWidget::StaticClass());

	}
	return true;
}

bool UCommonUserWidgetFactory::ShouldShowInNewMenu() const
{
	return true;
}

UObject* UCommonUserWidgetFactory::FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn, FName CallingContext)
{
	// Make sure we are trying to factory a Anim Blueprint, then create and init one
	check(Class->IsChildOf(UCommonWidgetBlueprint::StaticClass()));

	UClass* CurrentParentClass = ParentClass;
	if (CurrentParentClass == nullptr)
	{
		CurrentParentClass = UCommonUserWidget::StaticClass();
	}

	// If they selected an interface, force the parent class to be UInterface
	if (BlueprintType == BPTYPE_Interface)
	{
		CurrentParentClass = UInterface::StaticClass();
	}

	if ( (CurrentParentClass == nullptr) || !FKismetEditorUtilities::CanCreateBlueprintOfClass(CurrentParentClass) || !CurrentParentClass->IsChildOf(UCommonUserWidget::StaticClass()) )
	{
		FFormatNamedArguments Args;
		Args.Add( TEXT("ClassName"), CurrentParentClass ? FText::FromString( CurrentParentClass->GetName()) : LOCTEXT("Null", "(null)") );
		FMessageDialog::Open( EAppMsgType::Ok, FText::Format( LOCTEXT("CannotCreateCommonWidgetBlueprint", "Cannot create a Common Widget Blueprint based on the class '{ClassName}'."), Args ) );
		return nullptr;
	}
	else
	{
		if (!GetDefault<UUMGEditorProjectSettings>()->bUseWidgetTemplateSelector)
		{
			RootWidgetClass = GetDefault<UUMGEditorProjectSettings>()->DefaultRootWidget;
		}

		UCommonWidgetBlueprint* NewBP = CastChecked<UCommonWidgetBlueprint>(FKismetEditorUtilities::CreateBlueprint(CurrentParentClass, InParent, Name, BlueprintType, UCommonWidgetBlueprint::StaticClass(), UWidgetBlueprintGeneratedClass::StaticClass(), CallingContext));

		// Create the desired root widget specified by the project
		if ( NewBP->WidgetTree->RootWidget == nullptr )
		{
			if (TSubclassOf<UPanelWidget> RootWidgetPanel = RootWidgetClass)
			{
				UWidget* Root = NewBP->WidgetTree->ConstructWidget<UWidget>(RootWidgetPanel);
				NewBP->WidgetTree->RootWidget = Root;
			}
		}

		{
			IUMGEditorModule::FWidgetBlueprintCreatedArgs Args;
			Args.ParentClass = CurrentParentClass;
			Args.Blueprint = NewBP;

			IUMGEditorModule& UMGEditor = FModuleManager::LoadModuleChecked<IUMGEditorModule>("UMGEditor");
			UMGEditor.OnWidgetBlueprintCreated().Broadcast(Args);
		}

		return NewBP;
	}
}

UObject* UCommonUserWidgetFactory::FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn)
{
	return FactoryCreateNew(Class, InParent, Name, Flags, Context, Warn, NAME_None);
}

#undef LOCTEXT_NAMESPACE

这样运行起来之后,会发现右键内容已经有了但是,名字不对,类型也不对,但是已经能正常创建出来了,因为我们还需要创建一个UAssetDefinitionDefault
UE5 c++将自定义UserWdiget添加到对应菜单栏_第1张图片
UE5 c++将自定义UserWdiget添加到对应菜单栏_第2张图片
.h

// Copyright Epic Games, Inc. All Rights Reserved.

#pragma once
#include "CoreMinimal.h"
#include "AssetDefinitionDefault.h"

#include "AssetDefinition_CommonWidgetBlueprint.generated.h"

UCLASS()
class UAssetDefinition_CommonWidgetBlueprint : public UAssetDefinitionDefault
{
	GENERATED_BODY()

public:
	UAssetDefinition_CommonWidgetBlueprint();
	virtual ~UAssetDefinition_CommonWidgetBlueprint() override;
	
	// UAssetDefinition Begin
	virtual FText GetAssetDisplayName() const override;
	virtual FLinearColor GetAssetColor() const override;
	virtual TSoftClassPtr<UObject> GetAssetClass() const override;
	virtual TConstArrayView<FAssetCategoryPath> GetAssetCategories() const override;
	virtual EAssetCommandResult OpenAssets(const FAssetOpenArgs& OpenArgs) const override;
	virtual EAssetCommandResult PerformAssetDiff(const FAssetDiffArgs& DiffArgs) const override;
	virtual FText GetAssetDescription(const FAssetData& AssetData) const override;
	// UAssetDefinition End
};

.cpp

#include "AssetDefinition_CommonWidgetBlueprint.h"
#include "CommonWidgetBlueprint.h"
#include "WidgetBlueprintEditor.h"
#include "Misc/MessageDialog.h"
#include "SBlueprintDiff.h"

#define LOCTEXT_NAMESPACE "AssetTypeActions"

UAssetDefinition_CommonWidgetBlueprint::UAssetDefinition_CommonWidgetBlueprint() = default;

UAssetDefinition_CommonWidgetBlueprint::~UAssetDefinition_CommonWidgetBlueprint() = default;

FText UAssetDefinition_CommonWidgetBlueprint::GetAssetDisplayName() const
{
	return LOCTEXT("UAssetDefinition_CommonWidgetBlueprint", "Common Widget Blueprint");
}

FLinearColor UAssetDefinition_CommonWidgetBlueprint::GetAssetColor() const
{
	return FLinearColor(FColor(44, 89, 180));
}

TSoftClassPtr<> UAssetDefinition_CommonWidgetBlueprint::GetAssetClass() const
{
	return UCommonWidgetBlueprint::StaticClass();
}

TConstArrayView<FAssetCategoryPath> UAssetDefinition_CommonWidgetBlueprint::GetAssetCategories() const
{
	static const TArray<FAssetCategoryPath, TFixedAllocator<1>> Categories = { EAssetCategoryPaths::UI };
	return Categories;
}

EAssetCommandResult UAssetDefinition_CommonWidgetBlueprint::OpenAssets(const FAssetOpenArgs& OpenArgs) const
{
	EToolkitMode::Type Mode = OpenArgs.GetToolkitMode();

	EAssetCommandResult Result = EAssetCommandResult::Unhandled;

	for (UBlueprint* Blueprint : OpenArgs.LoadObjects<UBlueprint>())
	{
		if (Blueprint && Blueprint->SkeletonGeneratedClass && Blueprint->GeneratedClass)
		{
			TSharedRef<FWidgetBlueprintEditor> NewBlueprintEditor(new FWidgetBlueprintEditor);

			const bool bShouldOpenInDefaultsMode = false;
			TArray<UBlueprint*> Blueprints;
			Blueprints.Add(Blueprint);

			NewBlueprintEditor->InitWidgetBlueprintEditor(Mode, OpenArgs.ToolkitHost, Blueprints, bShouldOpenInDefaultsMode);
		}
		else
		{
			FMessageDialog::Open( EAppMsgType::Ok, LOCTEXT("FailedToLoadWidgetBlueprint", "Widget Blueprint could not be loaded because it derives from an invalid class.\nCheck to make sure the parent class for this blueprint hasn't been removed!"));
		}

		Result = EAssetCommandResult::Handled;
	}

	return Result;
}

EAssetCommandResult UAssetDefinition_CommonWidgetBlueprint::PerformAssetDiff(const FAssetDiffArgs& DiffArgs) const
{
	const UBlueprint* OldBlueprint = Cast<UBlueprint>(DiffArgs.OldAsset);
	const UBlueprint* NewBlueprint = Cast<UBlueprint>(DiffArgs.NewAsset);
	UClass* AssetClass = GetAssetClass().Get();
	SBlueprintDiff::CreateDiffWindow(OldBlueprint, NewBlueprint, DiffArgs.OldRevision, DiffArgs.NewRevision, AssetClass);
	return EAssetCommandResult::Handled;
}

FText UAssetDefinition_CommonWidgetBlueprint::GetAssetDescription(const FAssetData& AssetData) const
{
	FString Description = AssetData.GetTagValueRef<FString>( GET_MEMBER_NAME_CHECKED( UBlueprint, BlueprintDescription ) );
	if ( !Description.IsEmpty() )
	{
		Description.ReplaceInline( TEXT( "\\n" ), TEXT( "\n" ) );
		return FText::FromString( MoveTemp(Description) );
	}

	return FText::GetEmpty();
}

#undef LOCTEXT_NAMESPACE

这时候就得到了我们想要的结果:
UE5 c++将自定义UserWdiget添加到对应菜单栏_第3张图片

往自己的UserWidget添加测试代码,发现能正常反射处理,因此此篇幅完成

UE5 c++将自定义UserWdiget添加到对应菜单栏_第4张图片
UE5 c++将自定义UserWdiget添加到对应菜单栏_第5张图片
对,别忘记添加Editor模块:

        "UnrealEd",
        "UMGEditor", 
        "UMG",
        "AssetDefinition",
        "Kismet",

Runtime模块:

        "UMG",  

UE5 c++将自定义UserWdiget添加到对应菜单栏_第6张图片

当然完结了也没完全完,有人可能比较喜欢像我一样做一些明显的图标,便于选中,这里只需要添加Style就行:
Editor模块:

.h

// Copyright Epic Games, Inc. All Rights Reserved.

#pragma once

#include "Styling/SlateStyle.h"

/** Manages the style which provides resources for niagara editor widgets. */
class FDivinePrologueEditorStyle : public FSlateStyleSet
{
public:
	static  void Register();
	static  void Unregister();
	static  void Shutdown();

	/** reloads textures used by slate renderer */
	static  void ReloadTextures();

	/** @return The Slate style set for niagara editor widgets */
	static  const FDivinePrologueEditorStyle& Get();

	static  void ReinitializeStyle();
	virtual const FName& GetStyleSetName() const override;

private:	
	FDivinePrologueEditorStyle();
	void InitIcons();
	
	static TSharedPtr<FDivinePrologueEditorStyle> DivinePrologueEditorStyle;
};
.cpp
// Copyright Epic Games, Inc. All Rights Reserved.

#include "DivinePrologueEditorStyle.h"
#include "Styling/SlateStyleMacros.h"
#include "Styling/SlateStyleRegistry.h"

TSharedPtr<FDivinePrologueEditorStyle> FDivinePrologueEditorStyle::DivinePrologueEditorStyle = nullptr;

void FDivinePrologueEditorStyle::Register()
{
	FSlateStyleRegistry::RegisterSlateStyle(Get());
}

void FDivinePrologueEditorStyle::Unregister()
{
	FSlateStyleRegistry::UnRegisterSlateStyle(Get());
}

void FDivinePrologueEditorStyle::Shutdown()
{
	Unregister();
	DivinePrologueEditorStyle.Reset();
}

const FVector2D Icon8x8(8.0f, 8.0f);
const FVector2D Icon12x12(12.0f, 12.0f);
const FVector2D Icon16x16(16.0f, 16.0f);
const FVector2D Icon20x20(20.0f, 20.0f);
const FVector2D Icon32x32(32.0f, 32.0f);
const FVector2D Icon40x40(40.0f, 40.0f);
const FVector2D Icon64x64(64.0f, 64.0f);

FDivinePrologueEditorStyle::FDivinePrologueEditorStyle() : FSlateStyleSet("DivinePrologueEditorStyle")
{
	FSlateStyleSet::SetContentRoot(FPaths::ProjectContentDir() / TEXT("StyleTextures"));
	FSlateStyleSet::SetCoreContentRoot(FPaths::ProjectContentDir() / TEXT("StyleTextures"));
	
	InitIcons();
}

void FDivinePrologueEditorStyle::InitIcons()
{
	Set("CommonUserWidget.Icon", new IMAGE_BRUSH("Icon", Icon64x64));
}

void FDivinePrologueEditorStyle::ReloadTextures()
{
	FSlateApplication::Get().GetRenderer()->ReloadTextureResources();
}

const FDivinePrologueEditorStyle& FDivinePrologueEditorStyle::Get()
{
	if(!DivinePrologueEditorStyle.IsValid())
	{
		DivinePrologueEditorStyle = MakeShareable(new FDivinePrologueEditorStyle());
	}
	
	return *DivinePrologueEditorStyle;
}

void FDivinePrologueEditorStyle::ReinitializeStyle()
{
	Unregister();
	DivinePrologueEditorStyle.Reset();
	Register();	
}

const FName& FDivinePrologueEditorStyle::GetStyleSetName() const
{
	static FName StyleName("DivinePrologueEditorStyle");
	return StyleName;
}

这是我的路径:UE5 c++将自定义UserWdiget添加到对应菜单栏_第7张图片
当然创建之后需要在模块启动时候注册它:
UE5 c++将自定义UserWdiget添加到对应菜单栏_第8张图片
在UCommonUserWidgetFactory里面重载下面方法,返回我们Style里面对应的图片:

	virtual FName GetNewAssetThumbnailOverride() const override
	{
		return TEXT("CommonUserWidget.Icon");
	}

你可能感兴趣的:(ue5,ue4,unreal,engine)