UE4运用C++和框架开发坦克大战教程笔记(十九)(第58~60集)完结
58. 弹窗显示与隐藏
这节课我们先来补全 TransferMask()
里对于 Overlay 布局类型面板的遮罩转移逻辑,大体上与 Canvas 布局类型的差不多。
接下来就是编写弹窗的隐藏和重新显示的逻辑。
在写重新显示弹窗的逻辑时我们发现 DoEnterUIPanel()
有一段代码可以复用,但是发现了一处逻辑上的错误,所以要调整一下代码。
DDFrameWidget.cpp
void UDDFrameWidget::DoEnterUIPanel(FName PanelName)
{
// ... 省略
// 此处作更改
if (!WorkLayout) {
if (UnActiveCanvas.Num() == 0) {
WorkLayout = WidgetTree->ConstructWidget<UCanvasPanel>(UCanvasPanel::StaticClass());
WorkLayout->SetVisibility(ESlateVisibility::SelfHitTestInvisible);
}
else
WorkLayout = UnActiveCanvas.Pop();
// 添加布局控件到界面最顶层
UCanvasPanelSlot* FrameCanvasSlot = RootCanvas->AddChildToCanvas(WorkLayout);
FrameCanvasSlot->SetAnchors(FAnchors(0.f, 0.f, 1.f, 1.f));
FrameCanvasSlot->SetOffsets(FMargin(0.f, 0.f, 0.f, 0.f));
// 添加到激活画布组
ActiveCanvas.Push(WorkLayout);
}
// ... 省略
// 此处作更改
if (!WorkLayout) {
if (UnActiveOverlay.Num() == 0) {
WorkLayout = WidgetTree->ConstructWidget<UOverlay>(UOverlay::StaticClass());
WorkLayout->SetVisibility(ESlateVisibility::SelfHitTestInvisible);
}
else
WorkLayout = UnActiveOverlay.Pop();
// 添加布局控件到界面最顶层
UCanvasPanelSlot* FrameCanvasSlot = RootCanvas->AddChildToCanvas(WorkLayout);
FrameCanvasSlot->SetAnchors(FAnchors(0.f, 0.f, 1.f, 1.f));
FrameCanvasSlot->SetOffsets(FMargin(0.f, 0.f, 0.f, 0.f));
// 添加到激活画布组
ActiveOverlay.Push(WorkLayout);
}
// ... 省略
}
void UDDFrameWidget::HidePanelReverse(UDDPanelWidget* PanelWidget)
{
// 获取弹窗栈到数组
TArray<UDDPanelWidget*> PopStack;
PopPanelStack.GenerateValueArray(PopStack);
// 如果不是最上层的弹窗,直接返回
if (PopStack[PopStack.Num() - 1] != PanelWidget) {
DDH::Debug() << PanelWidget->GetObjectName() << " Is Not Last Panel In PopPanelStack" << DDH::Endl();
return;
}
// 从栈中移除
PopPanelStack.Remove(PanelWidget->GetObjectName());
// 执行隐藏函数
PanelWidget->PanelHidden();
// 调整弹窗栈
PopStack.Pop();
if (PopStack.Num() > 0) {
UDDPanelWidget* PrePanelWidget = PopStack[PopStack.Num() - 1];
// 转移遮罩到新的最顶层的弹窗的下一层
TransferMask(PrePanelWidget);
// 恢复被冻结的最顶层的弹窗
PrePanelWidget->PanelResume();
}
// 如果没有弹窗就移除遮罩
else
RemoveMaskPanel();
}
void UDDFrameWidget::ShowPanelReverse(UDDPanelWidget* PanelWidget)
{
// 如果弹窗栈里有元素,冻结最顶层的弹窗
if (PopPanelStack.Num() > 0) {
TArray<UDDPanelWidget*> PanelStack;
PopPanelStack.GenerateValueArray(PanelStack);
PanelStack[PanelStack.Num() - 1]->PanelFreeze();
}
// 弹窗对象必须从当前父控件移除,再重新添加到最顶层的界面(因为弹窗只是隐藏而不是销毁了)
if (PanelWidget->UINature.LayoutType == ELayoutType::Canvas) {
UCanvasPanel* PreWorkLayout = Cast<UCanvasPanel>(PanelWidget->GetParent());
UCanvasPanelSlot* PrePanelSlot = Cast<UCanvasPanelSlot>(PanelWidget->Slot);
FAnchors PreAnchors = PrePanelSlot->GetAnchors();
FMargin PreOffsets = PrePanelSlot->GetOffsets();
// 将 PanelWidget 从当前父控件移除
PanelWidget->RemoveFromParent();
// 处理父控件
if (PreWorkLayout->GetChildrenCount() == 0) {
PreWorkLayout->RemoveFromParent();
ActiveCanvas.Remove(PreWorkLayout);
UnActiveCanvas.Push(PreWorkLayout);
}
// 寻找最顶层的 WorkLayout
UCanvasPanel* WorkLayout = NULL;
// 判断最底层的布局控件是否是 Canvas
if (RootCanvas->GetChildrenCount() > 0)
WorkLayout = Cast<UCanvasPanel>(RootCanvas->GetChildAt(RootCanvas->GetChildrenCount() - 1));
if (!WorkLayout) {
// 如果没有任何对象
if (UnActiveCanvas.Num() == 0) {
WorkLayout = WidgetTree->ConstructWidget<UCanvasPanel>(UCanvasPanel::StaticClass());
WorkLayout->SetVisibility(ESlateVisibility::SelfHitTestInvisible);
}
else
WorkLayout = UnActiveCanvas.Pop();
// 添加布局控件到界面最顶层
UCanvasPanelSlot* FrameCanvasSlot = RootCanvas->AddChildToCanvas(WorkLayout);
FrameCanvasSlot->SetAnchors