- Published on
使用自定义Slate笔刷
- Authors

- Name
- 东哥
前言
使用slate制作界面的时候往往需要使用到笔刷, 本文简单介绍如何使用自定义的笔刷以及使用系统的笔刷
自定义笔刷
纯C++
参考引擎其他地方用到的获取笔刷的方式
主要思路是创建一个指针TSharedPtr< class FSlateStyleSet > StyleSet然后通过方法FSlateStyleRegistry::RegisterSlateStyle(*StyleSet.Get())注册笔刷
国际惯例(照着引擎代码), 定义几个宏
#define IMAGE_BRUSH(RelativePath, ... ) FSlateImageBrush( FVSM_Style::InResources( RelativePath, ".png" ), __VA_ARGS__ )
#define BOX_BRUSH(RelativePath, ... ) FSlateBoxBrush( FVSM_Style::InResources( RelativePath, ".png" ), __VA_ARGS__ )
先初始化
StyleSet = MakeShareable(new FSlateStyleSet(GetStyleSetName()));
StyleSet->SetContentRoot(IPluginManager::Get().FindPlugin(TEXT("VSM"))->GetContentDir());
然后从具体目录获取图片
StyleSet->Set("VSM.StateMachineSmall", new IMAGE_BRUSH("Icons/StateMachine-ToolbarStateMachine-20", Icon20x20));
StyleSet->Set("VSM.NodeBody", new BOX_BRUSH(TEXT("Graph/NodeBody"), FMargin(4.f / 64.f, 4.f / 64.f, 4.f / 64.f, 4.f / 64.f)));
第一个字符串就是获取笔刷的名称
获取方式
SNew(SBorder) .BorderImage(FVSM_Style::GetBrush(TEXT("FrameworkGraph.NodeBody")))
自定义资源缩略图
缩略图的自定义困扰了我很久, 第一时间想到的就是Factory类里面的虚函数GetNewAssetThumbnailOverride(), 重写以后返回一个之前注册过的笔刷名称,比如上面的 "VSM.StateMachineSmall"
那么在右键菜单准备创建的时候确实是正确的图标, 但是创建资源以后就是默认UObject的原型图案了, 这个问题也查不到资料, 我甚至把蓝图资源创建的流程代码整个翻了个遍, 还是得不到有效信息
最后意外的发现, 原来问题出在了注册笔刷的地方
比如我们这个资源的蓝图类叫做VSM_Blueprint(去掉了前缀U), 那么于此对应的缩略图和Icon就这样注册
StyleSet->Set("ClassIcon.VSM_Blueprint", new IMAGE_BRUSH(TEXT("Icons/StateMachine"), Icon16x16));
StyleSet->Set("ClassThumbnail.VSM_Blueprint", new IMAGE_BRUSH(TEXT("Icons/StateMachine"), Icon128x128));
同时, 我们也不需要重写Factory类里面的相关缩略图方法了;
还要注意一个问题, 蓝图类与它的ParentClass类都需要注册笔刷


WidgetSlateStyle
此方式方便在蓝图里配置笔刷,不用编译即可修改slate样式
我们创建此类的c++类
struct VSMEDITOR_API FVSM_SlateStyle : public FSlateWidgetStyle
{
//.................
//输入框的样式
UPROPERTY(EditAnywhere, Category = Appearance)
FInlineEditableTextBlockStyle EditTextBlockStyle;
}
其余均保持默认即可
为了区别c++的style, 我们在FVSM_Style中的变量命名成 static TSharedPtr BlueprintStyleSet;
void FVSM_Style::Initialize()
{
if (!BlueprintStyleSet)
{
FString styleDir = TEXT("/VSM/Style/");
BlueprintStyleSet = FSlateGameResources::New(FVSM_Style::GetBPStyleSetName(),*styleDir, *styleDir);
if (BlueprintStyleSet)
{
FSlateStyleRegistry::RegisterSlateStyle(*BlueprintStyleSet);
}
}
}
这里需要注意的是, 这里指定的目录是相对目录, 一开始我指定了绝对目录以后一路排查发现在
EngineUtils::FindOrLoadAssetsByPath()函数里找不到对应的文件
然后在的slate里面使用
//.h
const FVSM_SlateStyle* Style;
//.cpp
//VSMStyle对应的是蓝图的名称,如果错误可能会导致崩溃问题
Style = &FVSM_Style::GetBPStyle().`GetWidgetStyle<FVSM_SlateStyle>`("VSMStyle");
//.................
SAssignNew(InlineEditableText, SInlineEditableTextBlock)
.Style(&Style->EditTextBlockStyle)
然后创建蓝图style

效果

每次修改以后需要重新打开对应的slate资源才能刷新
编译以后如果蓝图的style还没有创建, 那么创建以后如果提示找不到笔刷, 则需要再重新启动一次编辑器
系统笔刷
系统的笔刷文件多数在 *\Engine\Content\Editor\Slate中
通过文件SlateEditorStyle.cpp中定义和获取
如
FEditorStyle::GetBrush(TEXT("Graph.StateNode.Pin.Background"));
当然引擎还有很多其他类也定义了自己的笔刷, 如VREditorStyle.cpp, GameMenuBuilderStyle.cpp,具体笔刷名称是啥就直接去看cpp
Style类完整代码
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "VSM_SlateWidgetStyle.h"
/**
*
*/
class VSMEDITOR_API FVSM_Style
{
/************************************************************************/
/* FUNCTION */
/************************************************************************/
public:
static FLinearColor TransitionNodeHorverColor;
static FLinearColor TransitionNodeBaseColor;
static FLinearColor NodeBodyBaseColor;
static void Initialize();
static void Shutdown();
static `TSharedPtr< class ISlateStyle >` Get();
static FName GetStyleSetName();
static FName GetBPStyleSetName();
static const FSlateBrush* GetBrush(FName PropertyName, const ANSICHAR* Specifier = NULL);
static ISlateStyle& GetBPStyle();
private:
static FString InResources(const FString& RelativePath, const ANSICHAR* Extension);
/************************************************************************/
/* PROPERTIES */
/************************************************************************/
public:
private:
static `TSharedPtr< class FSlateStyleSet >` StyleSet;
static `TSharedPtr<FSlateStyleSet>` BlueprintStyleSet;
};
// Fill out your copyright notice in the Description page of Project Settings.
#include "Style/VSM_Style.h"
#include "Styling/ISlateStyle.h"
#include "Styling/SlateStyle.h"
#include "Interfaces/IPluginManager.h"
#include "Styling/SlateStyleRegistry.h"
#include "SlateOptMacros.h"
#include "Slate/SlateGameResources.h"
#define IMAGE_BRUSH(RelativePath, ... ) FSlateImageBrush( FVSM_Style::InResources( RelativePath, ".png" ), __VA_ARGS__ )
#define BOX_BRUSH(RelativePath, ... ) FSlateBoxBrush( FVSM_Style::InResources( RelativePath, ".png" ), __VA_ARGS__ )
`TSharedPtr< FSlateStyleSet >` FVSM_Style::StyleSet = nullptr;
`TSharedPtr< FSlateStyleSet >` FVSM_Style::BlueprintStyleSet = nullptr;
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
void FVSM_Style::Initialize()
{
// Const icon sizes
const FVector2D Icon8x8(8.0f, 8.0f);
const FVector2D Icon16x16(16.0f, 16.0f);
const FVector2D Icon20x20(20.0f, 20.0f);
const FVector2D Icon40x40(40.0f, 40.0f);
const FVector2D Generic10x10(10.0f, 10.0f);
const FVector2D NodeBody64x64(64.0f, 64.0f);
const FVector2D Icon128x128(64.0f, 64.0f);
if (!BlueprintStyleSet)
{
FString styleDir = TEXT("/VSM/Style/");
BlueprintStyleSet = FSlateGameResources::New(FVSM_Style::GetBPStyleSetName(),*styleDir, *styleDir);
if (BlueprintStyleSet)
{
FSlateStyleRegistry::RegisterSlateStyle(*BlueprintStyleSet);
}
}
// Only register once
if (StyleSet.IsValid())
return;
StyleSet = MakeShareable(new FSlateStyleSet(GetStyleSetName()));
//StyleSet->SetContentRoot(IPluginManager::Get().FindPlugin(TEXT("VSM"))->GetBaseDir() / TEXT("Resources"));
StyleSet->SetContentRoot(FPaths::EngineContentDir() / TEXT("Editor/Slate"));
StyleSet->SetCoreContentRoot(FPaths::EngineContentDir() / TEXT("Slate"));
// Icons
StyleSet->Set("ClassIcon.VSM_Blueprint", new IMAGE_BRUSH(TEXT("Icons/StateMachine_16"), Icon16x16));
StyleSet->Set("ClassThumbnail.VSM_Blueprint", new IMAGE_BRUSH(TEXT("Icons/StateMachine_128"), Icon128x128));
StyleSet->Set("ClassIcon.VSM_StateMachine", new IMAGE_BRUSH(TEXT("Icons/StateMachine_16"), Icon16x16));
StyleSet->Set("ClassThumbnail.VSM_StateMachine", new IMAGE_BRUSH(TEXT("Icons/StateMachine_128"), Icon128x128));
StyleSet->Set("ClassIcon.VSM_Blueprint_Task", new IMAGE_BRUSH(TEXT("Icons/Task_16"), Icon16x16));
StyleSet->Set("ClassThumbnail.VSM_Blueprint_Task", new IMAGE_BRUSH(TEXT("Icons/Task_128"), Icon128x128));
StyleSet->Set("ClassIcon.VSM_Task_Base", new IMAGE_BRUSH(TEXT("Icons/Task_16"), Icon16x16));
StyleSet->Set("ClassThumbnail.VSM_Task_Base", new IMAGE_BRUSH(TEXT("Icons/Task_128"), Icon128x128));
// Toolbar
StyleSet->Set("VSM.StateMachine", new IMAGE_BRUSH("Icons/StateMachine", Icon40x40));
StyleSet->Set("VSM.StateMachineSmall", new IMAGE_BRUSH("Icons/StateMachine", Icon20x20));
StyleSet->Set("VSM.StateMachineGraphSmall", new IMAGE_BRUSH("Icons/GraphIconSmall", Icon20x20));
StyleSet->Set("VSM.Task", new IMAGE_BRUSH("Icons/Task", Icon40x40));
// Graph
StyleSet->Set("VSM.NodeBody", new BOX_BRUSH(TEXT("Graph/NodeBody"), FMargin(4.f / 64.f, 4.f / 64.f, 4.f / 64.f, 4.f / 64.f)));
//StyleSet->Set("FrameworkGraph.NodeBodySelected", new BOX_BRUSH(TEXT("Graph/NodeBodySelected"), FMargin(18.0f / 64.0f)));
//StyleSet->Set("FrameworkGraph.NodePin", new IMAGE_BRUSH(TEXT("Graph/NodePin"), Generic10x10));
StyleSet->Set("VSM.NodePinHoverCue", new IMAGE_BRUSH(TEXT("Graph/NodePinHoverCue"), Generic10x10));
//默认阴影
StyleSet->Set("VSM.RegularNodeShadow", new BOX_BRUSH(TEXT("Graph/RegularNodeShadow"), FMargin(18.0f / 64.0f)));
/*StyleSet->Set("FrameworkGraph.RegularNodeTitleNormal", new BOX_BRUSH(TEXT("Graph/RegularNodeTitleNormal"), FMargin(12.0f / 64.0f)));
StyleSet->Set("FrameworkGraph.RegularNodeTitleHighlight", new BOX_BRUSH(TEXT("Graph/RegularNodeTitleHighlight"), FMargin(16.0f / 64.0f, 1.0f, 16.0f / 64.0f, 0.0f)));*/
// State Machine
//默认边框部分
StyleSet->Set("VSM.StateMachine.NodeBody", new BOX_BRUSH(TEXT("Graph/StateMachine/NodeBody"), FMargin(16.f / 64.f, 25.f / 64.f, 16.f / 64.f, 16.f / 64.f)));
//节点选中阴影
StyleSet->Set("VSM.StateMachine.NodeBodySelected", new BOX_BRUSH(TEXT("Graph/StateMachine/NodeBodySelected"), FMargin(18.0f / 64.0f)));
//节点主体内部
StyleSet->Set("VSM.StateMachine.NodeBodyColorSpill", new BOX_BRUSH(TEXT("Graph/StateMachine/NodeBodyColorSpill"), FMargin(4.0f / 64.0f, 4.0f / 32.0f)));
//鼠标移动到Node上的背景颜色
StyleSet->Set("VSM.StateMachine.NodePinHoverCue", new BOX_BRUSH(TEXT("Graph/StateMachine/NodePinHoverCue"), FMargin(12.0f / 64.0f, 12.0f / 64.0f, 12.0f / 64.0f, 12.0f / 64.0f)));
StyleSet->Set("VSM.StateMachine.Icon", new IMAGE_BRUSH("Graph/StateMachine/NodeIcon", Icon16x16));
//过渡节点主体,已废弃
StyleSet->Set("vsm.StateMachine.TransitionNodeBody", new BOX_BRUSH("Graph/StateMachine/TransNodeBody", FMargin(16.f / 64.f, 12.f / 28.f)));
//过渡节点的底色
StyleSet->Set("VSM.StateMachine.TransitionNodeColorSpill", new BOX_BRUSH("Graph/StateMachine/TransNodeColorSpill", FMargin(0.25f, 0.25f, 0.25f, 0.25f))); //16.f / 64.f, 16.f / 28.f, 16.f / 64.f, 4.f / 28.f)
//过渡节点的图标
StyleSet->Set("VSM.StateMachine.TransitionNodeIcon", new IMAGE_BRUSH("Graph/StateMachine/TransNodeIcon", FVector2D(25, 25)));
FSlateStyleRegistry::RegisterSlateStyle(*StyleSet.Get());
}
const FSlateBrush* FVSM_Style::GetBrush(FName PropertyName, const ANSICHAR* Specifier /*= NULL*/)
{
return FVSM_Style::Get()->GetBrush(PropertyName, Specifier);
}
ISlateStyle& FVSM_Style::GetBPStyle()
{
return *BlueprintStyleSet;
}
END_SLATE_FUNCTION_BUILD_OPTIMIZATION
FLinearColor FVSM_Style::TransitionNodeHorverColor(0.22f, 1.0f, 1.0f, 1.0f);
FLinearColor FVSM_Style::TransitionNodeBaseColor(0.8f, 0.8f, 0.8f, 0.8f);
FLinearColor FVSM_Style::NodeBodyBaseColor(0.8f, 0.8f, 0.8f, 0.8f);
#undef IMAGE_BRUSH
#undef BOX_BRUSH
void FVSM_Style::Shutdown()
{
if (StyleSet.IsValid())
{
FSlateStyleRegistry::UnRegisterSlateStyle(*StyleSet.Get());
ensure(StyleSet.IsUnique());
StyleSet.Reset();
}
if (BlueprintStyleSet.IsValid())
{
FSlateStyleRegistry::UnRegisterSlateStyle(*BlueprintStyleSet.Get());
ensure(BlueprintStyleSet.IsUnique());
BlueprintStyleSet.Reset();
}
}
`TSharedPtr< class ISlateStyle >` FVSM_Style::Get()
{
return StyleSet;
}
FName FVSM_Style::GetStyleSetName()
{
static FName StyleName(TEXT("FVSM_Style"));
return StyleName;
}
FName FVSM_Style::GetBPStyleSetName()
{
FName BPStyleName(TEXT("FVSM_BlueprintStyle"));
return BPStyleName;
}
FString FVSM_Style::InResources(const FString& RelativePath, const ANSICHAR* Extension)
{
static FString ContentDir = IPluginManager::Get().FindPlugin(TEXT("VSM"))->GetBaseDir() / TEXT("Resources");
return (ContentDir / RelativePath) + Extension;
}