前言:
为了实现与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
.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
这时候就得到了我们想要的结果:
往自己的UserWidget添加测试代码,发现能正常反射处理,因此此篇幅完成
对,别忘记添加Editor模块:
"UnrealEd",
"UMGEditor",
"UMG",
"AssetDefinition",
"Kismet",
Runtime模块:
"UMG",
当然完结了也没完全完,有人可能比较喜欢像我一样做一些明显的图标,便于选中,这里只需要添加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;
}
这是我的路径:
当然创建之后需要在模块启动时候注册它:
在UCommonUserWidgetFactory里面重载下面方法,返回我们Style里面对应的图片:
virtual FName GetNewAssetThumbnailOverride() const override
{
return TEXT("CommonUserWidget.Icon");
}