使用自定义Slate笔刷

前言

使用slate制作界面的时候往往需要使用到笔刷, 本文简单介绍如何使用自定义的笔刷以及使用系统的笔刷

自定义笔刷

纯C++

参考引擎其他地方用到的获取笔刷的方式

主要思路是创建一个指针TSharedPtr< class FSlateStyleSet > StyleSet然后通过方法FSlateStyleRegistry::RegisterSlateStyle(*StyleSet.Get())注册笔刷


国际惯例(照着引擎代码), 定义几个宏

1
2
3
#define IMAGE_BRUSH(RelativePath, ... ) FSlateImageBrush( FVSM_Style::InResources( RelativePath, ".png" ), __VA_ARGS__ )
#define BOX_BRUSH(RelativePath, ... ) FSlateBoxBrush( FVSM_Style::InResources( RelativePath, ".png" ), __VA_ARGS__ )

先初始化

1
2
StyleSet = MakeShareable(new FSlateStyleSet(GetStyleSetName()));
StyleSet->SetContentRoot(IPluginManager::Get().FindPlugin(TEXT("VSM"))->GetContentDir());

然后从具体目录获取图片

1
2
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)));

第一个字符串就是获取笔刷的名称

获取方式

1
SNew(SBorder)			.BorderImage(FVSM_Style::GetBrush(TEXT("FrameworkGraph.NodeBody")))

自定义资源缩略图

缩略图的自定义困扰了我很久, 第一时间想到的就是Factory类里面的虚函数GetNewAssetThumbnailOverride(), 重写以后返回一个之前注册过的笔刷名称,比如上面的 "VSM.StateMachineSmall"

那么在右键菜单准备创建的时候确实是正确的图标, 但是创建资源以后就是默认UObject的原型图案了, 这个问题也查不到资料, 我甚至把蓝图资源创建的流程代码整个翻了个遍, 还是得不到有效信息

最后意外的发现, 原来问题出在了注册笔刷的地方

比如我们这个资源的蓝图类叫做VSM_Blueprint(去掉了前缀U), 那么于此对应的缩略图和Icon就这样注册

1
2
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类都需要注册笔刷

UE4Editor_3gT4XKdY99

PYM5GUua6f

WidgetSlateStyle

此方式方便在蓝图里配置笔刷,不用编译即可修改slate样式

我们创建此类的c++类

1
2
3
4
5
6
7
8
struct VSMEDITOR_API FVSM_SlateStyle : public FSlateWidgetStyle
{
//.................
//输入框的样式
UPROPERTY(EditAnywhere, Category = Appearance)
FInlineEditableTextBlockStyle EditTextBlockStyle;
}

其余均保持默认即可

为了区别c++的style, 我们在FVSM_Style中的变量命名成 static TSharedPtr<FSlateStyleSet> BlueprintStyleSet;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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里面使用

1
2
3
4
5
6
7
8
9
10
11
//.h
const FVSM_SlateStyle* Style;

//.cpp
//VSMStyle对应的是蓝图的名称,如果错误可能会导致崩溃问题
Style = &FVSM_Style::GetBPStyle().GetWidgetStyle<FVSM_SlateStyle>("VSMStyle");


//.................
SAssignNew(InlineEditableText, SInlineEditableTextBlock)
.Style(&Style->EditTextBlockStyle)

然后创建蓝图style

image-20210412183656909

效果

UE4Editor_txLE2n7qwp

每次修改以后需要重新打开对应的slate资源才能刷新

编译以后如果蓝图的style还没有创建, 那么创建以后如果提示找不到笔刷, 则需要再重新启动一次编辑器

系统笔刷

系统的笔刷文件多数在 *\Engine\Content\Editor\Slate

通过文件SlateEditorStyle.cpp中定义和获取

1
FEditorStyle::GetBrush(TEXT("Graph.StateNode.Pin.Background"));

当然引擎还有很多其他类也定义了自己的笔刷, 如VREditorStyle.cpp, GameMenuBuilderStyle.cpp,具体笔刷名称是啥就直接去看cpp

Style类完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
// 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;
};

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
// 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;
}

1