--- title: Slate学习笔记(一):Slate动态控制与其他技巧 date: 2021-02-19 9:22:10        tags: Slate rating: ⭐️ --- ## 前言 最近在写MessageBox控件,想兼容UMG与c++中调用Slate。略有心得,遂有此文。 ## 动态Slate 首先Slate控件树中创建占位用的NullWidget并使用.Expose()绑定Slot指针,使得可以进行后续操作。 ```c++ ChildSlot .VAlign(VAlign_Fill) .HAlign(HAlign_Fill) [ SNew(SOverlay) + SOverlay::Slot() .HAlign(HAlign_Center) .VAlign(VAlign_Center) .Expose(UseMenuSlot) // Expose it to a pointer so we can change the widget at runtime [ SNullWidget::NullWidget ] ]; ``` 之后就可以通过函数对Slot进行操作了,下面演示如何动态添加或者删除Button: ```c++ UseMenuSlot->AttachWidget( InText.Get().IsEmpty() ? SNullWidget::NullWidget : SNew(SButton) .VAlign(VAlign_Center) .HAlign(HAlign_Center) [ SNew(STextBlock) ] ); ``` ### 支持Expose()的容器 常用的: SHorizontalBox SVerticalBox SOverlay 其他还有若干也支持的 ## 设置指定长度的Widget 有的时候需要让控件保持指定的大小,使用SBox的WidthOverride与HeightOverride即可。其他的控件都不支持这2个属性。 ```c++ ChildSlot [ SNew(SBox) .WidthOverride(256) .HeightOverride(100) [ SNew(SButton) ] ]; ``` ## 指定大小控件的UMG与Slate兼容问题 UMG的根节点是一个CanvasPanel(UCanvasPanelSlot)。我认为直接给控件加一个SBox来控制大小的方式不太好。这会使得在UMG中的调整大小变得麻烦。所以我的方法是:使用c++调用Slate控件时,在外面再套一层SConstraintCanvas。 UCanvasPanelSlot本质上是SConstraintCanvas。UMG编辑器中显示的Size实际上是Offset。所以想要指定大小就需要设置Offset的Right与Bottom。 ```c++ CurrentWidget = SNew(SConstraintCanvas) + SConstraintCanvas::Slot() .Anchors(0.5) .Alignment(FVector2D(0.5,0.5)) .Offset(FMargin(0,0,500,400)) [ SNew(SCustomWidget) ]; GetGameInstance()->GetGameViewportClient()->AddViewportWidgetContent(CurrentWidget.ToSharedRef()); ``` ## c++获取UMG控件 ```c++ UPROPERTY(Meta = (BindWidget)) //直接获取蓝图中的按钮1 UButton* ButtonOne; ``` ```c++ bool UFWAffectWidget::Initialize() { if (!Super::Initialize()) return false; //方法1 RootPanel = Cast(GetRootWidget()); //获取根控件 if (RootPanel) { BGImage = Cast(RootPanel->GetChildAt(0)); //下标1的BGImage控件 } //方法2 UButton* ButtonTwo = (UButton*)GetWidgetFromName(TEXT("ButtonTwo")); //绑定按钮事件方法1: ButtonOne->OnClicked.__Internal_AddDynamic(this,&UFWAffectWidget::ButtonOneEvent,FName("ButtonOneEvent")); //绑定事件方法2:委托FScriptDelegate FScriptDelegate ButTwoDel; ButTwoDel.BindUFunction(this, "ButtonTwoEvent"); ButtonTwo->OnReleased.Add(ButTwoDel); return true; } ``` 参考文章:https://blog.csdn.net/weixin_44200074/article/details/109100521 ## 从UMG控件中获取Slate ```c++ TSharedRef UWidget::TakeWidget() ``` ## 反射 ### 遍历UProperty ```c++ for(TFiledIterator It(/*UClass**/);It;++It) { UProperty *newProperty=It; } ``` ### 根据名称寻找UProperty ```c++ UProperty *foundProperty = FindField(/*UClass**/,TEXT("PropertyName")); ``` ### Get/Set UProperty ```c++ //根据PropertyPath获取属性值 FCachedPropertyPath PropertyPath(FString(TEXT("PropertyName"))); T Value; ProperthPathHelpers::GetPropertyValue(Object,PropertyPath,Value); //有获取就有设置 ProperthPathHelpers::SetPropertyValue(Object,PropertyPath,Value); //还可以获取成String FString PropertyValueStr; ProperthPathHelpers::GetPropertyValueAsString(Object,PropertyPath,PropertyValueStr); //同样,反向也可以 ProperthPathHelpers::SetPropertyValueFromString(Object,PropertyPath,PropertyValueStr); //实际上,我们平时在UE引擎中复制粘贴属性值就是将属性值获取为String以及从String这只属性值的整个过程。 ``` 引用文章:https://zhuanlan.zhihu.com/p/121006155 更多反射技巧:https://bebylon.dev/ue4guide/engine-programming/uobject-reflection/uobject-reflection/ ## SCompoundWidget、SPanel、SLeafWidget的区别 ### SCompoundWidget SCompoundWidget对应于UMG中的“WidgetBlueprint(控件蓝图)”,用来作为控件容器。当我们在C++类向导中创建Slate类时,创建的就是SCompoundWidget。 可以通过成员ChildSlot结合重载的[]操作符往控件里面添加其他控件 ```c++ void SStandardSlateWidget::Construct(const FArguments& InArgs) { ChildSlot [ SNew(STextBlock) .Font(FSlateFontInfo("Veranda", 100)) .Text(NSLOCTEXT("HelloSlate", "HelloSlateText", "Hello, Slate!")) ]; } ``` ### SPanel 即为SPanel子布局的属性,包括Padding、Size、Horizontal Align、Vertical Align。 使用Slot添加子控件可以使用“+ Slot()”,也可以调用AddSlot函数,还是以教程1中的SStandardSlateWidget为例 ```c++ void SStandardSlateWidget::Construct(const FArguments& InArgs) { ChildSlot [ SAssignNew(VerticalBoxPtr, SVerticalBox) + SVerticalBox::Slot() .Padding(1.0) .FillHeight(0.3f) .HAlign(HAlign_Fill) .VAlign(VAlign_Top) [ SNew(SButton) ] + SVerticalBox::Slot() .FillHeight(0.5f) .HAlign(HAlign_Center) [ SNew(SButton) ] ]; VerticalBoxPtr->AddSlot() .FillHeight(0.2f) [ SNew(SButton) ]; } ``` ### SLeafWidget 叶子控件,故名思意,该控件不能添加子控件。常用的为STextBlock与SImage。