diff --git a/Source/Integration/PlayerControllerBase.cpp b/Source/Integration/PlayerControllerBase.cpp index 104c1924..e865b20a 100644 --- a/Source/Integration/PlayerControllerBase.cpp +++ b/Source/Integration/PlayerControllerBase.cpp @@ -164,7 +164,7 @@ void AlxPlayerControllerBase::AddWidgetToRoot(UUserWidget *Widget) if (Widget->GetParent() == PC->RootCanvas) return; UlxRootCanvasSlot *Slot = PC->RootCanvas->AddChildToRootCanvas(Widget); - Slot->SetZOrder(0); + Slot->SlotUnderConstruction = false; } void AlxPlayerControllerBase::BuildInputStack(TArray& InputStack) @@ -256,10 +256,6 @@ void AlxPlayerControllerBase::BuildInputStack(TArray& InputSta void AlxPlayerControllerBase::UpdateInputMode() { - // Drain any deferred ZOrder writes from SetWidgetWindowManagement - // before we read the front-most slot. - RootCanvas->PropagateZOrderToSlate(); - // Get all the various objects we need to be able to manipulate // the input mode. UGameViewportClient *GameViewportClient = GetWorld()->GetGameViewport(); diff --git a/Source/Integration/RootCanvas.cpp b/Source/Integration/RootCanvas.cpp index 7aadd4f7..9fd1cb5b 100644 --- a/Source/Integration/RootCanvas.cpp +++ b/Source/Integration/RootCanvas.cpp @@ -18,6 +18,34 @@ UlxRootCanvasSlot::UlxRootCanvasSlot(const FObjectInitializer& ObjectInitializer SetOffsets(FMargin(0.0f, 0.0f, 0.0f, 0.0f)); } +void UlxRootCanvasSlot::SetZOrderReliable(int32 Order) +{ + if (SlotUnderConstruction) + { + PRAGMA_DISABLE_DEPRECATION_WARNINGS + ZOrder = Order; + PRAGMA_ENABLE_DEPRECATION_WARNINGS + } + else + { + SetZOrder(Order); + } +} + +int32 UlxRootCanvasSlot::GetZOrderReliable() +{ + if (SlotUnderConstruction) + { + PRAGMA_DISABLE_DEPRECATION_WARNINGS + return ZOrder; + PRAGMA_ENABLE_DEPRECATION_WARNINGS + } + else + { + return GetZOrder(); + } +} + UClass* UlxRootCanvasPanel::GetSlotClass() const { return UlxRootCanvasSlot::StaticClass(); @@ -49,23 +77,11 @@ int32 UlxRootCanvasPanel::GetMaxZOrder() const { UlxRootCanvasSlot *TypedSlot = Cast(PanelSlot); check(TypedSlot); - MaxZOrder = FMath::Max(MaxZOrder, TypedSlot->GetZOrder()); + MaxZOrder = FMath::Max(MaxZOrder, TypedSlot->GetZOrderReliable()); } return MaxZOrder; } -void UlxRootCanvasPanel::PropagateZOrderToSlate() -{ - if (!MustPropagateZOrderToSlate) return; - MustPropagateZOrderToSlate = false; - for (UPanelSlot *PanelSlot : Slots) - { - UlxRootCanvasSlot *TypedSlot = Cast(PanelSlot); - check(TypedSlot); - TypedSlot->SetZOrder(TypedSlot->GetZOrder()); - } -} - TArray UlxRootCanvasPanel::GetSortedUserWidgets() { TArray Result; @@ -105,17 +121,6 @@ void UlxRootCanvasPanel::SetWidgetWindowManagement(class UUserWidget *Widget, Slot->BlockInput = BlockInput; Slot->EnableEnhancedInput = EnableEnhancedInput; Widget->SetDesiredFocusWidget(DesiredFocusWidget); - if (BringToFront) - { - // Write the int32 ZOrder UPROPERTY directly and defer the - // Slate-side push to PropagateZOrderToSlate. Going through SetZOrder - // here is unsafe when called from Event Construct: the FSlot - // has been Exposed but its Owner isn't set yet, which fires - // an ensure in SConstraintCanvas::FSlot::SetZOrder. - PRAGMA_DISABLE_DEPRECATION_WARNINGS - Slot->ZOrder = Panel->GetMaxZOrder() + 1; - PRAGMA_ENABLE_DEPRECATION_WARNINGS - Panel->MustPropagateZOrderToSlate = true; - } + if (BringToFront) Slot->SetZOrderReliable(Panel->GetMaxZOrder() + 1); } diff --git a/Source/Integration/RootCanvas.h b/Source/Integration/RootCanvas.h index 232a6382..6953782a 100644 --- a/Source/Integration/RootCanvas.h +++ b/Source/Integration/RootCanvas.h @@ -59,6 +59,22 @@ public: // events in the Event Graph are deactivated. UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Luprex|Input Mode") bool EnableEnhancedInput = true; + + // Knowing whether the slot is under construction helps us to work + // around some bugs in Unreal. See below. + bool SlotUnderConstruction = true; + + // Reliable version of SetZOrder. There is a bug in the normal version of + // SetZOrder: it crashes if you use it during OnConstruct. We have a + // workaround, but it requires using a different getter and setter. + UFUNCTION(BlueprintCallable, Category = "Luprex|Input Mode") + void SetZOrderReliable(int32 Order); + + // Reliable version of GetZOrder. There is a bug in the normal version of + // SetZOrder: it crashes if you use it during OnConstruct. We have a + // workaround, but it requires using a different getter and setter. + UFUNCTION(BlueprintCallable, Category = "Luprex|Input Mode") + int32 GetZOrderReliable(); }; //////////////////////////////////////////////////////////// @@ -91,13 +107,6 @@ public: // Used as the basis for placing new widgets on top. int32 GetMaxZOrder() const; - // SetZOrder is buggy: if you call it inside Event Construct, - // it crashes. So we've developed a hack to make it reliable. - // To set the zorder, you store the value in the slot property, - // without calling SetZorder. This routine, which is used later, - // propagates that value into the slate widgets. - void PropagateZOrderToSlate(); - // This function updates several window-management-related properties // which are stored in the UserWidget and the lxRootCanvasSlot. Note that // it is perfectly legal to edit these properties by other means: this @@ -122,10 +131,6 @@ protected: // UPanelWidget virtual UClass* GetSlotClass() const override; - -public: - - bool MustPropagateZOrderToSlate = false; };