学习的视频
第一个独立程序_哔哩哔哩_bilibili
对于UE编译系统总结的文章推荐:
虚幻引擎编译系统总结
下载并编译源码版UE
去github下载源码版:https://github.com/EpicGames/UnrealEngine
第一个程序
处理UE源码版的文件的过程中需要注意的点
通过文章里的这些描述得知,如果我们需要新建第一个程序就需要全程再本地资源管理器中进行操作,不能够在VS里面进行操作。 当进行完新建文件的操作完以后,通过双击运行GenerateProjectFiles.bat文件即可更新UE的结构,同时VS里面也会同步更新。 当更改build.cs文件时也需要双击运行GenerateProjectFiles.bat文件。
过程
去源码的Source文件夹中的Programs目录里面找到BlankProgram文件夹(这个文件夹是一个空白程序文件夹,因此可以通过这个来创建自己的程序):UnrealEngine-release\Engine\Source\Programs<br />选中BlankProgram文件夹ctrl+c和ctrl+v创建一个副本 改名为自己想要的名字 以FirstProgram举例: 改文件夹的名字以后,更改文件夹里面的文件以及文件内容的名字,把BlankProgram都改为自己的名字(FirstProgram) 这四个文件除了文件名,文件的内容里的BlankProgram也都改为FirstProgram
更改完以后运行bat文件来重新生成工程文件。 生成以后打开slh文件,将我们的FirstProgram设置为启动项目并进行编译 编译完成之后进入cpp文件打上断点然后进行调试,然后就得到了打印helloworld的程序。
SWindow
主要参考程序:SlateViewer
首先可以参考一下自带的SlateViewer程序,将其设为启动项目然后进行调试即可查看内容结果。如下:
模块更改
两个模块的作用:
补充: 写代码时会include其他头文件,之所以不需要写全路径,只需要写public或者private后面的路径,应该就是因为在build.cs里面包括了那个模块名.如果没有就需要添加.这也就是模块由build.cs确定相互依赖关系的意思.
build模块:
新增三个
target模块:
更改三个:
cpp文件的内容:
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 #include "FirstProgram.h" #include "RequiredProgramMainCPPInclude.h" #include "StandaloneRenderer.h" #include "Framework/Application/SlateApplication.h" DEFINE_LOG_CATEGORY_STATIC (LogFirstProgram, Log, All);IMPLEMENT_APPLICATION (FirstProgram, "FirstProgram" );int WINAPI WinMain (_In_ HINSTANCE hInInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR, _In_ int nCmdShow) { GEngineLoop.PreInit (GetCommandLineW ()); FSlateApplication::InitializeAsStandaloneApplication (GetStandardStandaloneRenderer ()); TSharedPtr<SWindow> MainWindow = SNew (SWindow) .ClientSize (FVector2D (800 , 600 )) [ SNullWidget::NullWidget ]; FSlateApplication::Get ().AddWindow (MainWindow.ToSharedRef ()); while (!IsEngineExitRequested ()) { FSlateApplication::Get ().PumpMessages (); FSlateApplication::Get ().Tick (); } FSlateApplication::Shutdown (); return 0 ; }
其中只有下面这些是自己写的,其他的都是复制粘贴并删除部分代码的SlateViewerMainWindows.cpp和SlateViewerApp.cpp的内容 这里涉及到智能指针的使用
SButton 按钮
这节主要讲了如何给窗口添加一个按钮,并且给按钮添加一个点击事件. 参考的SButton路径:\UnrealEngine-release\Engine\Source\Runtime\Slate\Public\Widgets\Input\SButton.h
创建文件
首先通过在资源管理器上在第一个新建的程序旁新建按钮的头文件和源文件(因为虚幻引擎不是靠sln来管理文件架构的,所以不要在VS里面进行文件的添加和删除).
创建好按钮的头文件和源文件以后使用bat文件重新生成一下项目.
这里我自定义文件名字叫SMybutton.
声明了一个继承SButton的类,叫SMyButton,模仿SButton中的代码,声明一个Construct(初始化)和返回类型为FReply的按钮点击时执行的函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #pragma once #include "Widgets/Input/SButton.h" class SMyButton : public SButton{ public : void Construct (const FArguments& InArgs) ; private : FReply ButtonClicked () ; };
实现初始化方法,首先调用父类的初始化的方法,然后在此基础上设置点击事件,当点击时执行SMyButton的ButtonClicked函数来返回一个信息窗口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 #include "SMyButton.h" void SMyButton::Construct (const FArguments& InArgs) { SButton::Construct (InArgs); this ->SetOnClicked (FOnClicked::CreateRaw (this , &SMyButton::ButtonClicked)); } FReply SMyButton::ButtonClicked () { FMessageDialog::Open (EAppMsgType::Ok, FText::FromString ("Button is clicked!" )); return FReply::Handled (); }
FirstProgram.cpp
自定义的按钮已经完成了,现在将其放到我们的第一个程序里创建的窗口里面.
1 2 3 4 5 6 7 8 9 TSharedPtr<SMyButton> MyButton = SNew (SMyButton); TSharedPtr<SWindow> MainWindow = SNew (SWindow) .ClientSize (FVector2D (800 , 600 )) [ MyButton.ToSharedRef () ];
SCanvas 画布
SCanva翻译叫画布,学过UE的UMG就很好理解.
依然在本地新建一个头文件和源文件,新建完以后构建一下.我这里命名为SMyCanvas.h与SMycanvas.cpp
SMycanvas.h
通过继承SCanvas,和SButton一样定义一个初始化的函数
1 2 3 4 5 6 7 8 #pragma once #include "Widgets/SCanvas.h" class SMyCanvas : public SCanvas{ public : void Construct (const FArguments& InArgs) ; };
SMycanvas.cpp
除了执行父类的初始化函数之外,新增两个槽用来放上一章创建的SButton类.
这里的槽和Qt的槽还不是一个概念,Slate的槽是用来放控件的.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #include "SMyCanvas.h" #include "SMyButton.h" void SMyCanvas::Construct (const FArguments& InArgs) { SCanvas::Construct (InArgs); AddSlot () .Position (FVector2D (100 , 100 )) .Size (FVector2D (100 , 40 )) [ SNew (SMyButton) ]; AddSlot () .Position (FVector2d (300 , 100 )) .Size (FVector2D (100 , 40 )) [ SNew (SMyButton) ]; }
FirstProgram.cpp
在窗口下放刚才定义的SCanvas
1 2 3 4 5 6 7 8 TSharedPtr<SMyCanvas> MyCanvas = SNew (SMyCanvas); TSharedPtr<SWindow> MainWindow = SNew (SWindow) .ClientSize (FVector2D (800 , 600 )) [ MyCanvas.ToSharedRef () ];
SComboBox下拉框
在画布里面添加下拉框
SMyCanvas.h
1 2 3 4 5 6 7 8 9 10 11 #pragma once #include "Widgets/SCanvas.h" class SMyCanvas : public SCanvas{ public : void Construct (const FArguments& InArgs) ; TArray<TSharedPtr<FString>> Options; int32 CurrentSelected = -1 ; };
SMycanvas.cpp
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 #include "SMyCanvas.h" void SMyCanvas::Construct (const FArguments& InArgs) { SCanvas::Construct (InArgs); Options.Empty (); Options.Add (MakeShareable (new FString ("Apple" ))); Options.Add (MakeShareable (new FString ("Banana" ))); Options.Add (MakeShareable (new FString ("Orange" ))); AddSlot () .Position (FVector2D (500 , 100 )) .Size (FVector2D (100 , 40 )) [ SNew (SComboBox<TSharedPtr<FString>>) .OptionsSource (&Options) .OnGenerateWidget_Lambda ([](TSharedPtr<FString> InValue) { return SNew (STextBlock).Text (FText::FromString (*InValue)); }) .OnSelectionChanged_Lambda ([this ](TSharedPtr<FString> NewOption,ESelectInfo::Type SelectType) { CurrentSelected = Options.Find (NewOption); }) [ SNew (STextBlock).Text_Lambda ([this ]() { if (CurrentSelected < 0 || CurrentSelected > Options.Num () - 1 ) return FText::FromString ("" ); else return FText::FromString (*Options[CurrentSelected]); }) ] ]; }
SHorizontalBox,SVerticalBox 水平布局和垂直布局
在Scanvas(画布)上面直接添加一个水平布局和一个垂直布局来举例
最终结果:
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 #include "SMyCanvas.h" void SMyCanvas::Construct (const FArguments& InArgs) { SCanvas::Construct (InArgs); AddSlot () .Position (FVector2D (100 , 200 )) .Size (FVector2D (400 , 40 )) [ SNew (SHorizontalBox) +SHorizontalBox::Slot () [ SNew (SButton) ] +SHorizontalBox::Slot () .FillWidth (2.0f ) [ SNew (SButton) ] +SHorizontalBox::Slot () [ SNew (SButton) ] ]; AddSlot () .Position (FVector2D (100 , 300 )) .Size (FVector2D (100 , 160 )) [ SNew (SVerticalBox) +SVerticalBox::Slot () [ SNew (SButton) ] +SVerticalBox::Slot () .FillHeight (2.0 ) [ SNew (SButton) ] +SVerticalBox::Slot () [ SNew (SButton) ] ]; }
STreeView
新建SMyTreeView.cpp和SMyTreeView.h然后重新构建一下
生成结果:
SMyTreeView.h
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 #pragma once class UTreeItemData { public : FString MyName; float MyHeight = 10 ; TArray<TSharedPtr<UTreeItemData>> Children; }; class SMyTreeView : public STreeView<TSharedPtr<UTreeItemData>>{ public : void Construct (const FArguments& InArgs) ; TArray<TSharedPtr<UTreeItemData>> TreeItemDatas; TSharedRef<ITableRow> GenerateRowItem (TSharedPtr<UTreeItemData> InTreeItemData, const TSharedRef<STableViewBase>& OwnerTable) ; void GetChildrenForItem (TSharedPtr<UTreeItemData> InTreeItem, TArray<TSharedPtr<UTreeItemData>>& OutChildren) ; };
SMyTreeView.cpp
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 #include "SMyTreeView.h" void SMyTreeView::Construct (const FArguments& InArgs) { FArguments Arguments = InArgs; TSharedPtr<UTreeItemData> ZhangSan = MakeShareable (new UTreeItemData); ZhangSan->MyName = FString ("zhangSan" ); TSharedPtr<UTreeItemData> ZhangSan1 = MakeShareable (new UTreeItemData); ZhangSan1->MyName = FString ("ZhangSan1" ); TSharedPtr<UTreeItemData> ZhangSan2 = MakeShareable (new UTreeItemData); ZhangSan2->MyName = FString ("ZhangSan2" ); ZhangSan->Children.Add (ZhangSan1); ZhangSan->Children.Add (ZhangSan2); TSharedPtr<UTreeItemData> LiSi = MakeShareable (new UTreeItemData); LiSi->MyName = FString ("LiSi" ); TSharedPtr<UTreeItemData> LiSi1 = MakeShareable (new UTreeItemData); LiSi1->MyName = FString ("LiSi1" ); TSharedPtr<UTreeItemData> LiSi2 = MakeShareable (new UTreeItemData); LiSi2->MyName = FString ("LiSi2" ); TSharedPtr<UTreeItemData> LiSi3 = MakeShareable (new UTreeItemData); LiSi3->MyName = FString ("LiSi3" ); LiSi->Children.Add (LiSi1); LiSi->Children.Add (LiSi2); LiSi->Children.Add (LiSi3); TreeItemDatas.Add (ZhangSan); TreeItemDatas.Add (LiSi); Arguments.TreeItemsSource (&TreeItemDatas); Arguments.OnGenerateRow_Raw (this , &SMyTreeView::GenerateRowItem); Arguments.OnGetChildren_Raw (this , &SMyTreeView::GetChildrenForItem); STreeView::Construct (Arguments); } TSharedRef<ITableRow> SMyTreeView::GenerateRowItem (TSharedPtr<UTreeItemData> InTreeItemData, const TSharedRef<STableViewBase>& OwnerTable) { return SNew (STableRow<TSharedPtr<UTreeItemData>>, OwnerTable) [ SNew (SHorizontalBox) + SHorizontalBox::Slot () [ SNew (STextBlock) .Text (FText::FromString (InTreeItemData->MyName)) ] + SHorizontalBox::Slot () [ SNew (STextBlock) .Text (FText::FromString (FString::SanitizeFloat (InTreeItemData->MyHeight))) ] ]; } void SMyTreeView::GetChildrenForItem (TSharedPtr<UTreeItemData> InTreeItem, TArray<TSharedPtr<UTreeItemData>>& OutChildren) { OutChildren = InTreeItem->Children; }
SlistView
跟STreeView差不多 就不加注释了
SMylistView.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #pragma once class UListViewItemData { public : FString MyName = "ZhangSan" ; }; class SMyListView :public SListView<TSharedPtr<UListViewItemData>>{ public : void Construct (const FArguments& InArgs) ; TArray<TSharedPtr<UListViewItemData>> ListItemDatas; TSharedRef<ITableRow> GenerateRowItem (TSharedPtr<UListViewItemData> InListViewItemData, const TSharedRef<STableViewBase>& OwnerTable) ; };
SMyListView.cpp
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 #include "SMyListView.h" void SMyListView::Construct (const FArguments& InArgs) { FArguments Arguments = InArgs; ListItemDatas.Add (MakeShareable (new UListViewItemData)); ListItemDatas.Add (MakeShareable (new UListViewItemData)); ListItemDatas.Add (MakeShareable (new UListViewItemData)); Arguments.ListItemsSource (&ListItemDatas); Arguments.OnGenerateRow_Raw (this , &SMyListView::GenerateRowItem); SListView::Construct (Arguments); } TSharedRef<ITableRow> SMyListView::GenerateRowItem (TSharedPtr<UListViewItemData> InListViewItemData, const TSharedRef<STableViewBase>& OwnerTable) { return SNew (STableRow<TSharedPtr<UListViewItemData>>, OwnerTable) [ SNew (STextBlock) .Text (FText::FromString (InListViewItemData->MyName)) ]; }
SImage
在之前的SMyCanvas.cpp里面添加新的槽并加入SImage控件
其中Icons.Warning使用的是UE自带的图片
1 2 3 4 5 6 7 8 AddSlot () .Position (FVector2D (400 , 200 )) .Size (FVector2D (200 , 200 )) [ SNew (SImage) .Image (FCoreStyle::Get ().GetBrush ("Icons.Warning" )) ];
SGridPanel
在之前的SMyCanvas.cpp里面添加新的槽并加入SGridPanel布局
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 AddSlot () .Position (FVector2D (400 , 500 )) .Size (FVector2D (200 , 200 )) [ SNew (SGridPanel) .FillColumn (0 , 1 ) .FillColumn (1 , 1 ) .FillRow (0 , 1 ) .FillRow (1 , 1 ) + SGridPanel::Slot (0 , 0 ) [ SNew (SImage) .Image (FCoreStyle::Get ().GetBrush ("Icons.Warning" )) ] + SGridPanel::Slot (0 , 1 ) [ SNew (SImage) .Image (FCoreStyle::Get ().GetBrush ("Icons.Warning" )) ] + SGridPanel::Slot (1 , 0 ) [ SNew (SImage) .Image (FCoreStyle::Get ().GetBrush ("Icons.Warning" )) ] + SGridPanel::Slot (1 , 1 ) [ SNew (SImage) .Image (FCoreStyle::Get ().GetBrush ("Icons.Warning" )) ] ];
FTabManage
在FirstProgram.cpp里面直接添加FTabManage来体现FTabManage的使用方法,首先先把之前生成SWindow的代码注释掉.
生成的结果如下图所示
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 #include "FirstProgram.h" #include "RequiredProgramMainCPPInclude.h" #include "StandaloneRenderer.h" #include "Framework/Application/SlateApplication.h" #include "SMyCanvas.h" DEFINE_LOG_CATEGORY_STATIC (LogFirstProgram, Log, All);IMPLEMENT_APPLICATION (FirstProgram, "FirstProgram" );int WINAPI WinMain (_In_ HINSTANCE hInInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR, _In_ int nCmdShow) { GEngineLoop.PreInit (GetCommandLineW ()); FSlateApplication::InitializeAsStandaloneApplication (GetStandardStandaloneRenderer ()); TArray<FName> TabName = { "LeftTab1" ,"LeftTab2" ,"RightTopTab" ,"RightBottomTab" }; const TSharedRef<FTabManager::FLayout> Layout = FTabManager::NewLayout (TEXT ("Layout" )) ->AddArea ( FTabManager::NewArea (800 , 600 ) ->SetOrientation (EOrientation::Orient_Horizontal) ->Split ( FTabManager::NewStack () ->AddTab (TabName[0 ],ETabState::OpenedTab) ->AddTab (TabName[1 ], ETabState::OpenedTab) ) ->Split ( FTabManager::NewSplitter () ->SetOrientation (EOrientation::Orient_Vertical) ->Split ( FTabManager::NewStack () ->AddTab (TabName[2 ],ETabState::OpenedTab) ) ->Split ( FTabManager::NewStack () ->AddTab (TabName[3 ],ETabState::OpenedTab) ) ) ); FGlobalTabmanager::Get ()->RestoreFrom (Layout, TSharedPtr <SWindow>()); while (!IsEngineExitRequested ()) { FSlateApplication::Get ().PumpMessages (); FSlateApplication::Get ().Tick (); } FSlateApplication::Shutdown (); return 0 ; }
SDockTab(给Tab添加内容并令Tab能够进行拖拽与停靠)
要实现这样的Tab,需要通过FGlobalTabmanager::Get()->RegisterTabSpawner来注册SDockTab
这里解释一下工厂函数FOnSpawnTab::CreateLambda配合C++的lambda语法是怎么样的:
FOnSpawnTab::CreateLambda([&](const FSpawnTabArgs& Args)->TSharedRef {})
首先工厂函数FOnSpawnTab::CreateLambda的作用是接收一个lambda表达式来生成一个tab
[&](const FSpawnTabArgs& Args)->TSharedRef {}就是一个lambda语法,语法为:[捕获列表](参数列表)->返回类型{函数体}
捕获列表意思是函数体可以使用的外部的参数,&表示所有外部的参数都能使用,参数列表为传入函数体的参数,->后是返回的类型
通过以下代码将名字叫LeftTab1的Tab定义成DockTab并且定义Tab的内容
1 2 3 4 5 6 7 8 9 10 11 TArray<FName> TabName = { "LeftTab1" ,"LeftTab2" ,"RightTopTab" ,"RightBottomTab" }; FGlobalTabmanager::Get ()->RegisterTabSpawner (TabName[0 ], FOnSpawnTab::CreateLambda ( [&](const FSpawnTabArgs& Args)->TSharedRef<SDockTab> { return SNew (SDockTab) [ SNew (SButton) .Text (FText::FromName (TabName[0 ])) ]; } ));
制作菜单栏
制作如图所示菜单栏
其中MyMenu为菜单栏,OpenFile和CloseFile,SaveFile这些都是菜单栏下对应的命令.
MyEditor是负责控制总UI的,想要显示这个UI就需要在FirstProgram.cpp(自己创建的程序)里面创建一个MyEditor对象就可以了.
生成菜单栏也就是创建FMenuBarBuilder这个类,创建这个类有一个必须要的参数是FUICommandList类型的智能指针
菜单栏的类型为SWidget,因此有了FMenuBarBuilder的对象以后就可以通过MakeWidget函数来得到菜单栏.
TCommands 设置菜单栏下对应的命令
通过继承TCommands创建一个属于自己的命令,然后通过UI_COMMAND定义命令的信息
FUICommandList
通过FNewMenuDelegate::CreateLambda来将命令添加到菜单栏上,通过FUICommandList的对象的MapAction方法可以将菜单栏上的命令对应的执行功能.
总的代码
MyCommands.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 #pragma once #include "Framework/Commands/Commands.h" class MyCommands : public TCommands<MyCommands>{ public : MyCommands (); virtual void RegisterCommands () override ; TSharedPtr< FUICommandInfo > OpenFileCommand; TSharedPtr< FUICommandInfo > CloseFileCommand; TSharedPtr< FUICommandInfo > SaveFileCommand; };
MyCommands.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #include "MyCommands.h" #define LOCTEXT_NAMESPACE "MyCommands" MyCommands::MyCommands () :TCommands <MyCommands>( TEXT ("MyCommands" ), NSLOCTEXT ("Contexts" , "MyEditor" , "My Editor" ), NAME_None, FCoreStyle::Get ().GetStyleSetName () ) { } void MyCommands::RegisterCommands () { UI_COMMAND (OpenFileCommand, "OpenFile" , "This is OpenFile command." , EUserInterfaceActionType::Button, FInputChord (EModifierKey::Control,EKeys::O)); UI_COMMAND (CloseFileCommand, "CloseFile" , "This is CloseFile command." , EUserInterfaceActionType::Button, FInputChord (EModifierKey::Control, EKeys::C)); UI_COMMAND (SaveFileCommand, "SaveFile" , "This is SaveFile command." , EUserInterfaceActionType::Button, FInputChord (EModifierKey::Control, EKeys::S)); } #undef LOCTEXT_NAMESPACE
MyEditor.h
1 2 3 4 5 6 7 8 9 #pragma once class MyEditor { public : MyEditor (); TSharedRef<SWidget> MakeMenuBar () ; TSharedPtr<FUICommandList> CommandList; };
MyEditor.cpp
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 #include "MyEditor.h" #include "MyCommands.h" #define LOCTEXT_NAMESPACE "MyEditor" MyEditor::MyEditor () { CommandList = MakeShareable (new FUICommandList); MyCommands::Register (); CommandList->MapAction (MyCommands::Get ().OpenFileCommand, FExecuteAction::CreateLambda ([]() { FMessageDialog::Open (EAppMsgType::Ok, FText::FromString ("OpenFileCommand!" )); }) ); CommandList->MapAction (MyCommands::Get ().CloseFileCommand, FExecuteAction::CreateLambda ([]() { FMessageDialog::Open (EAppMsgType::Ok, FText::FromString ("CloseFileCommand!" )); }) ); CommandList->MapAction (MyCommands::Get ().SaveFileCommand, FExecuteAction::CreateLambda ([]() { FMessageDialog::Open (EAppMsgType::Ok, FText::FromString ("SaveFileCommand!" )); }) ); TArray<FName> TabName = { "LeftTab" ,"RightTopTab" ,"RightBottomTab" }; FGlobalTabmanager::Get ()->RegisterTabSpawner (TabName[0 ], FOnSpawnTab::CreateLambda ( [&](const FSpawnTabArgs& Args)->TSharedRef<SDockTab> { return SNew (SDockTab) [ SNew (SVerticalBox) + SVerticalBox::Slot () .AutoHeight () [ MakeMenuBar () ] +SVerticalBox::Slot () .FillHeight (1.f ) [ SNew (SButton) .Text (FText::FromName (TabName[0 ])) ] ]; } )); FGlobalTabmanager::Get ()->RegisterTabSpawner (TabName[1 ], FOnSpawnTab::CreateLambda ( [&](const FSpawnTabArgs& Args)->TSharedRef<SDockTab> { return SNew (SDockTab) [ SNew (SButton) .Text (FText::FromName (TabName[1 ])) ]; } )); FGlobalTabmanager::Get ()->RegisterTabSpawner (TabName[2 ], FOnSpawnTab::CreateLambda ( [&](const FSpawnTabArgs& Args)->TSharedRef<SDockTab> { return SNew (SDockTab) [ SNew (SButton) .Text (FText::FromName (TabName[2 ])) ]; } )); const TSharedRef<FTabManager::FLayout> Layout = FTabManager::NewLayout (TEXT ("Layout" )) ->AddArea ( FTabManager::NewArea (800 , 600 ) ->SetOrientation (EOrientation::Orient_Horizontal) ->Split ( FTabManager::NewStack () ->AddTab (TabName[0 ], ETabState::OpenedTab) ) ->Split ( FTabManager::NewSplitter () ->SetOrientation (EOrientation::Orient_Vertical) ->Split ( FTabManager::NewStack () ->AddTab (TabName[1 ], ETabState::OpenedTab) ) ->Split ( FTabManager::NewStack () ->AddTab (TabName[2 ], ETabState::OpenedTab) ) ) ); FGlobalTabmanager::Get ()->RestoreFrom (Layout, TSharedPtr <SWindow>()); } TSharedRef<SWidget> MyEditor::MakeMenuBar () { FMenuBarBuilder MenuBuilder (CommandList) ; MenuBuilder.AddPullDownMenu ( LOCTEXT ("MyMenu" , "MyMenu" ), LOCTEXT ("MyMenu" , "This is my menu" ), FNewMenuDelegate::CreateLambda ( [](FMenuBuilder& MenuBuilder){ MenuBuilder.AddMenuEntry (MyCommands::Get ().OpenFileCommand); MenuBuilder.AddMenuEntry (MyCommands::Get ().CloseFileCommand); MenuBuilder.AddMenuEntry (MyCommands::Get ().SaveFileCommand); } ) ); return MenuBuilder.MakeWidget (); } #undef LOCTEXT_NAMESPACE
FToolBarBuilder 创建工具架
这次通过上一节创建菜单栏需要的TCommand添加到工具架上.
思路就是仿照上一节的MakeMenuBar函数来创建一个MakeToolBar函数.然后通过这个函数返回一个SWidget,然后将这个函数放到布局里面就可以了.
函数代码如下:
1 2 3 4 5 6 7 8 9 10 11 TSharedRef<SWidget> MyEditor::MakeToolBar () { FToolBarBuilder ToolBarBuilder (CommandList,FMultiBoxCustomization::None) ; ToolBarBuilder.BeginSection ("MySection" ); ToolBarBuilder.AddToolBarButton (MyCommands::Get ().OpenFileCommand); ToolBarBuilder.AddToolBarButton (MyCommands::Get ().CloseFileCommand); ToolBarBuilder.AddToolBarButton (MyCommands::Get ().SaveFileCommand); ToolBarBuilder.EndSection (); return ToolBarBuilder.MakeWidget (); }
SEditableText 文本编辑框
添加文本编辑框
其中EditableText 是在头文件中声明的SEditableText类型的智能指针:TSharedPtr EditableText;
1 2 3 4 5 SNew (SVerticalBox)+SVerticalBox::Slot () [ SAssignNew (EditableText,SEditableText)]
获取文本编辑框中的文字
这里展示通过按钮点击弹出消息框,消息框内容是文本编辑框中的文字的演示:
主要代码为:EditableText->GetText() ,其中EditableText为添加文本编辑框时创建的SEditableText类型的智能指针.
1 2 3 4 5 6 7 SNew (SButton).Text (FText::FromName (TabName[1 ])) .OnClicked_Lambda ( [this ]() { FMessageDialog::Open (EAppMsgType::Ok, EditableText->GetText ()); return FReply::Handled (); })
SSplitter
添加分割左右两边的分割条,左边为一个button,右边为一个button,左边的button大小是右边的button大小的二倍
1 2 3 4 5 6 7 8 9 10 11 12 SNew (SSplitter)+SSplitter::Slot () .Value (2.0f ) [ SNew (SButton) .Text (FText::FromName (TabName[2 ])) ] +SSplitter::Slot () [ SNew (SButton) .Text (FText::FromName (TabName[2 ])) ]
#Sborder 填充
填充类型分为颜色填充和图像填充和九宫格填充
其中颜色填充需要新建一个static 成员变量
效果如下:
添加方法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 static FSlateColorBrush ColorBrush = FSlateColorBrush (FLinearColor (1.0f , 1.0f , 0.0f , 1.0f ));SNew (SVerticalBox)+SVerticalBox::Slot () .FillHeight (1.f ) [ SNew (SBorder) .BorderImage (&ColorBrush) ] +SVerticalBox::Slot () [ SNew (SBorder) .BorderImage (FCoreStyle::Get ().GetBrush ("Icons.Warning" )) ] +SVerticalBox::Slot () [ SNew (SBorder) .BorderImage (FCoreStyle::Get ().GetBrush ("Debug.Border" )) ]
SOverlay
SOverlay属于布局,用法也跟布局一样,这个布局的显示效果为:布局下的后创建的槽会覆盖前面创建的槽,就跟PS的图层一样,后创建的图层会覆盖之前的图层.
SLeafWidget
可以通过继承SLeafWidget来创建自己的较简单的控件(这个控件不能够作为容器来包含其他控件).
具体代码参考:
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 #pragma once #include "Widgets/SLeafWidget.h" class SMyLeafWidget : public SLeafWidget{ public : SLATE_BEGIN_ARGS (SMyLeafWidget) : _StartPoint(FVector2D (0 ,0 )) , _EndPoint(FVector2D (0 ,0 )) { } SLATE_ATTRIBUTE (FVector2D, StartPoint); SLATE_ATTRIBUTE (FVector2D, EndPoint); SLATE_END_ARGS () void Construct (const FArguments& InArgs) ; private : TArray<FVector2D> Points; virtual FVector2D ComputeDesiredSize (float ) const override ; virtual int32 OnPaint ( const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const override ;};
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 #include "SMyLeafWidget.h" void SMyLeafWidget::Construct (const FArguments& InArgs) { Points.Add (InArgs._StartPoint.Get ()); Points.Add (InArgs._EndPoint.Get ()); } FVector2D SMyLeafWidget::ComputeDesiredSize (float ) const { return FVector2D (50 , 50 ); } int32 SMyLeafWidget::OnPaint ( const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const { FSlateDrawElement::MakeLines (OutDrawElements, LayerId, AllottedGeometry.ToPaintGeometry (), Points); return LayerId++; }
FirstProgram.cpp(最终生成窗口使用控件的程序)
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 #include "FirstProgram.h" #include "RequiredProgramMainCPPInclude.h" #include "StandaloneRenderer.h" #include "Framework/Application/SlateApplication.h" #include "SMyleafWidget.h" DEFINE_LOG_CATEGORY_STATIC (LogFirstProgram, Log, All);IMPLEMENT_APPLICATION (FirstProgram, "FirstProgram" );int WINAPI WinMain (_In_ HINSTANCE hInInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR, _In_ int nCmdShow) { GEngineLoop.PreInit (GetCommandLineW ()); FSlateApplication::InitializeAsStandaloneApplication (GetStandardStandaloneRenderer ()); TSharedPtr<SMyLeafWidget> MyLeafWidget = SNew (SMyLeafWidget) .StartPoint (FVector2D (0.f ,0.f )) .EndPoint (FVector2D (200.f ,200.f )); TSharedPtr<SWindow> MainWindow = SNew (SWindow) .ClientSize (FVector2D (800 , 600 )) [ MyLeafWidget.ToSharedRef () ]; FSlateApplication::Get ().AddWindow (MainWindow.ToSharedRef ()); while (!IsEngineExitRequested ()) { FSlateApplication::Get ().PumpMessages (); FSlateApplication::Get ().Tick (); } FSlateApplication::Shutdown (); return 0 ; }
SCompoundWidget
可以通过继承SCompoundWidget来创建自己的较复杂的控件(这个控件能够作为容器来包含其他控件,这是它与SLeafWidget的不同).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #pragma once class SMyCompoundWidget : public SCompoundWidget{ public : SLATE_BEGIN_ARGS (SMyCompoundWidget) : _Content() , _HAlign(HAlign_Fill) , _VAlign(VAlign_Fill) {} SLATE_DEFAULT_SLOT (FArguments, Content) SLATE_ARGUMENT (EHorizontalAlignment, HAlign) SLATE_ARGUMENT (EVerticalAlignment, VAlign) SLATE_END_ARGS () void Construct (const FArguments& InArgs) ; };
1 2 3 4 5 6 7 8 9 10 11 12 #include "SMyCompoundWidget.h" void SMyCompoundWidget::Construct (const FArguments& InArgs) { ChildSlot .HAlign (InArgs._HAlign) .VAlign (InArgs._VAlign) [ InArgs._Content.Widget ]; }
SCustomDialog
由于CustomDialog是编辑器使用的模块,因此不能够直接include来使用,所以找到其源文件复制到程序文件夹中进行使用.
将其复制到自己程序的文件夹中后改一下自己想要定义的名字,然后将其内容修改一下使其能够自己使用.
SMyCustomDialog.h
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 #pragma once #include "Widgets/SWindow.h" class SMyCustomDialog : public SWindow{ public : struct FButton { FButton (const FText& InButtonText, const FSimpleDelegate& InOnClicked = FSimpleDelegate ()) : ButtonText (InButtonText), OnClicked (InOnClicked) { } FText ButtonText; FSimpleDelegate OnClicked; }; SLATE_BEGIN_ARGS (SMyCustomDialog) : _UseScrollBox(true ) , _ScrollBoxMaxHeight(300 ) { _AccessibleParams = FAccessibleWidgetData (EAccessibleBehavior::Auto); } SLATE_ARGUMENT (FText, Title) SLATE_ARGUMENT (FName, IconBrush) SLATE_ARGUMENT (bool , UseScrollBox) SLATE_ARGUMENT (int32, ScrollBoxMaxHeight) SLATE_ARGUMENT (TArray<FButton>, Buttons) SLATE_ARGUMENT (TSharedPtr<SWidget>, DialogContent) SLATE_EVENT (FSimpleDelegate, OnClosed) SLATE_END_ARGS () void Construct (const FArguments& InArgs) ; void Show () ; int32 ShowModal () ; private : FReply OnButtonClicked (FSimpleDelegate OnClicked, int32 ButtonIndex) ; int32 LastPressedButton = -1 ; FSimpleDelegate OnClosed; };
SMyCustomDialog.cpp
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 #include "SMyCustomDialog.h" #include "HAL/PlatformApplicationMisc.h" #include "Framework/Application/SlateApplication.h" #include "Framework/Docking/TabManager.h" #include "Logging/LogMacros.h" #include "Styling/SlateBrush.h" #include "Widgets/Images/SImage.h" #include "Widgets/Input/SButton.h" #include "Widgets/Text/STextBlock.h" #include "Widgets/Layout/SSpacer.h" #include "Widgets/Layout/SBox.h" #include "Widgets/Layout/SScrollBox.h" #include "Widgets/Layout/SUniformGridPanel.h" #include "Widgets/SBoxPanel.h" DEFINE_LOG_CATEGORY_STATIC (LogCustomDialog, Log, All);void SMyCustomDialog::Construct (const FArguments& InArgs) { UE_LOG (LogCustomDialog, Log, TEXT ("Dialog displayed:" ), *InArgs._Title.ToString ()); check (InArgs._Buttons.Num () > 0 ); OnClosed = InArgs._OnClosed; TSharedPtr<SHorizontalBox> ContentBox; TSharedPtr<SHorizontalBox> ButtonBox; SWindow::Construct ( SWindow::FArguments () .Title (InArgs._Title) .SizingRule (ESizingRule::Autosized) .SupportsMaximize (false ) .SupportsMinimize (false ) [ SNew (SBorder) .Padding (4.f ) .BorderImage (FCoreStyle::Get ().GetBrush ( "ToolPanel.GroupBorder" )) [ SNew (SVerticalBox) + SVerticalBox::Slot () .FillHeight (1.0f ) [ SAssignNew (ContentBox, SHorizontalBox) ] + SVerticalBox::Slot () .VAlign (VAlign_Center) .AutoHeight () [ SAssignNew (ButtonBox, SHorizontalBox) ] ] ] ); if (InArgs._IconBrush.IsValid ()) { const FSlateBrush* ImageBrush = FCoreStyle::Get ().GetBrush (InArgs._IconBrush); if (ImageBrush != nullptr ) { ContentBox->AddSlot () .AutoWidth () .VAlign (VAlign_Center) .HAlign (HAlign_Left) .Padding (0 , 0 , 8 , 0 ) [ SNew (SImage) .Image (ImageBrush) ]; } } if (InArgs._UseScrollBox) { ContentBox->AddSlot () [ SNew (SBox) .MaxDesiredHeight (InArgs._ScrollBoxMaxHeight) [ SNew (SScrollBox) +SScrollBox::Slot () [ InArgs._DialogContent.ToSharedRef () ] ] ]; } else { ContentBox->AddSlot () .FillWidth (1.0f ) .VAlign (VAlign_Center) .HAlign (HAlign_Left) [ InArgs._DialogContent.ToSharedRef () ]; } ButtonBox->AddSlot () .AutoWidth () [ SNew (SSpacer) .Size (FVector2D (20.0f , 1.0f )) ]; TSharedPtr<SUniformGridPanel> ButtonPanel; ButtonBox->AddSlot () .FillWidth (1.0f ) .VAlign (VAlign_Center) .HAlign (HAlign_Right) [ SAssignNew (ButtonPanel, SUniformGridPanel) .SlotPadding (0 ) .MinDesiredSlotWidth (100 ) .MinDesiredSlotHeight (30 ) ]; for (int32 i = 0 ; i < InArgs._Buttons.Num (); ++i) { const FButton& Button = InArgs._Buttons[i]; ButtonPanel->AddSlot (ButtonPanel->GetChildren ()->Num (), 0 ) [ SNew (SButton) .OnClicked (FOnClicked::CreateSP (this , &SMyCustomDialog::OnButtonClicked, Button.OnClicked, i)) [ SNew (SHorizontalBox) + SHorizontalBox::Slot () .VAlign (VAlign_Center) .HAlign (HAlign_Center) [ SNew (STextBlock) .Text (Button.ButtonText) ] ] ]; } } int32 SMyCustomDialog::ShowModal () { FSlateApplication::Get ().AddModalWindow (StaticCastSharedRef <SWindow>(this ->AsShared ()), FGlobalTabmanager::Get ()->GetRootWindow ()); return LastPressedButton; } void SMyCustomDialog::Show () { TSharedRef<SWindow> Window = FSlateApplication::Get ().AddWindow (StaticCastSharedRef <SWindow>(this ->AsShared ()), true ); if (OnClosed.IsBound ()) { Window->GetOnWindowClosedEvent ().AddLambda ([this ](const TSharedRef<SWindow>& Window) { OnClosed.Execute (); }); } } FReply SMyCustomDialog::OnButtonClicked (FSimpleDelegate OnClicked, int32 ButtonIndex) { LastPressedButton = ButtonIndex; FSlateApplication::Get ().RequestDestroyWindow (StaticCastSharedRef <SWindow>(this ->AsShared ())); OnClicked.ExecuteIfBound (); return FReply::Handled (); }
FirstProgram.cpp
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 #include "FirstProgram.h" #include "RequiredProgramMainCPPInclude.h" #include "StandaloneRenderer.h" #include "Framework/Application/SlateApplication.h" #include "SMyCanvas.h" #include "MyEditor.h" #include "SMyleafWidget.h" #include "SMyCompoundWidget.h" #include "SMyCustomDialog.h" #include "Widgets/Input/SButton.h" DEFINE_LOG_CATEGORY_STATIC (LogFirstProgram, Log, All);IMPLEMENT_APPLICATION (FirstProgram, "FirstProgram" );int WINAPI WinMain (_In_ HINSTANCE hInInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR, _In_ int nCmdShow) { GEngineLoop.PreInit (GetCommandLineW ()); FSlateApplication::InitializeAsStandaloneApplication (GetStandardStandaloneRenderer ()); TSharedPtr<SMyCompoundWidget> MyCompoundWidget = SNew (SMyCompoundWidget) .HAlign (EHorizontalAlignment::HAlign_Center) .VAlign (EVerticalAlignment::VAlign_Bottom) [ SNew (SButton) .Text (FText::FromString (FString ("ShowDialog" ))) .OnClicked_Lambda ([]() { TSharedRef<SMyCustomDialog> MyCustomDialog = SNew (SMyCustomDialog) .Title (FText::FromString (FString ("DialogTitle" ))) .DialogContent ( SNew (SVerticalBox) +SVerticalBox::Slot () [ SNew (STextBlock).Text (FText::FromString (FString ("FirstRowText" ))) ] +SVerticalBox::Slot () [ SNew (STextBlock).Text (FText::FromString (FString ("SecondRowText" ))) ] ) .Buttons ({ SMyCustomDialog::FButton (FText::FromString (FString ("Ok" ))) }); MyCustomDialog->ShowModal (); return FReply::Handled (); }) ]; TSharedPtr<SWindow> MainWindow = SNew (SWindow) .ClientSize (FVector2D (800 , 600 )) [ MyCompoundWidget.ToSharedRef () ]; FSlateApplication::Get ().AddWindow (MainWindow.ToSharedRef ()); while (!IsEngineExitRequested ()) { FSlateApplication::Get ().PumpMessages (); FSlateApplication::Get ().Tick (); } FSlateApplication::Shutdown (); return 0 ; }
SMultiLineEditableText
SMultiLineEditableText是可以直接用的,它的作用是多行文本编辑器,这一节的教程中并没有介绍其所具有的其他方法(下一节会将为其添加滚动条).只介绍了通过继承前面所讲的SCompoundWidget,然后把它放里面,然后加了个SBorder,给SBorder设置颜色填充并添加SMultiLineEditableText的例子.这里展示一下.
SMyMultiLineEditableText.h
1 2 3 4 5 6 7 8 9 10 #pragma once #include "Widgets/Text/SMultiLineEditableText.h" class SMyMultiLineEditableText : public SCompoundWidget{ public : SLATE_BEGIN_ARGS (SMyMultiLineEditableText) {} SLATE_END_ARGS () void Construct (const FArguments& InArgs) ; };
SMyMultiLineEditableText.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 #include "SMyMultiLineEditableText.h" void SMyMultiLineEditableText::Construct (const FArguments& InArgs) { static FSlateColorBrush ColorBrush = FSlateColorBrush (FLinearColor (0.25f , 0.25f , 0.25f , 0.25f )); ChildSlot [ SNew (SBorder) .BorderImage (&ColorBrush) [ SNew (SMultiLineEditableText) ] ]; }
FirstProgram.cpp
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 #include "FirstProgram.h" #include "RequiredProgramMainCPPInclude.h" #include "StandaloneRenderer.h" #include "Framework/Application/SlateApplication.h" #include "SMyMultiLineEditableText.h" DEFINE_LOG_CATEGORY_STATIC (LogFirstProgram, Log, All);IMPLEMENT_APPLICATION (FirstProgram, "FirstProgram" );int WINAPI WinMain (_In_ HINSTANCE hInInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR, _In_ int nCmdShow) { GEngineLoop.PreInit (GetCommandLineW ()); FSlateApplication::InitializeAsStandaloneApplication (GetStandardStandaloneRenderer ()); TSharedPtr<SMyMultiLineEditableText> MyMultiLineEditableText = SNew (SMyMultiLineEditableText); TSharedPtr<SWindow> MainWindow = SNew (SWindow) .ClientSize (FVector2D (800 , 600 )) [ MyMultiLineEditableText.ToSharedRef () ]; FSlateApplication::Get ().AddWindow (MainWindow.ToSharedRef ()); while (!IsEngineExitRequested ()) { FSlateApplication::Get ().PumpMessages (); FSlateApplication::Get ().Tick (); } FSlateApplication::Shutdown (); return 0 ; }
SScrollBar 滚动条
在上一节SMyMultiLineEditableText.cpp的基础上为文本编辑器添加滚动条
SMyMultiLineEditableText.cpp
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 #include "SMyMultiLineEditableText.h" void SMyMultiLineEditableText::Construct (const FArguments& InArgs) { static FSlateColorBrush ColorBrush = FSlateColorBrush (FLinearColor (0.25f , 0.25f , 0.25f , 0.25f )); TSharedPtr<SScrollBar> HorizontalScrollBar = SNew (SScrollBar).Orientation (EOrientation::Orient_Horizontal); TSharedPtr<SScrollBar> VerticalScrollBar = SNew (SScrollBar).Orientation (EOrientation::Orient_Vertical); ChildSlot [ SNew (SBorder) .BorderImage (&ColorBrush) [ SNew (SHorizontalBox) + SHorizontalBox::Slot () .FillWidth (1.0f ) [ SNew (SVerticalBox) + SVerticalBox::Slot () .FillHeight (1.0f ) [ SNew (SMultiLineEditableText) .HScrollBar (HorizontalScrollBar) .VScrollBar (VerticalScrollBar) ] + SVerticalBox::Slot () .AutoHeight () [ HorizontalScrollBar.ToSharedRef () ] ] + SHorizontalBox::Slot () .AutoWidth () [ VerticalScrollBar.ToSharedRef () ] ] ]; }
FNotificationInfo 通知框
这里实现一个功能,当点击按钮时屏幕右下角会弹出通知框然后消失
由于不需要额外创建类,这里只贴出关键代码
1 2 3 4 5 6 7 8 9 10 11 12 13 #include "Widgets/Notifications/SNotificationList.h" #include "Framework/Notifications/NotificationManager.h" SNew (SButton).Text (FText::FromName (TabName[1 ])) .OnClicked_Lambda ( [&]() { FNotificationInfo Info (NSLOCTEXT ("MainFrame" , "RecompileInProgress" , "Compiling C++ Code" )); Info.ExpireDuration = 5.0f ; Info.bFireAndForget = false ; FSlateNotificationManager::Get ().AddNotification (Info).Get ()->Fadeout (); return FReply::Handled (); })