From 4420c52b7486a8ac8708e4a8f24254f6e9694181 Mon Sep 17 00:00:00 2001 From: jyelon Date: Tue, 21 Apr 2026 22:28:22 -0400 Subject: [PATCH] Working on new root canvas stuff --- Source/Integration/PlayerControllerBase.cpp | 4 ++++ Source/Integration/RootCanvas.cpp | 22 ++++++++++++++++++++- Source/Integration/RootCanvas.h | 11 +++++++++++ tools/UEDataFormatter.py | 1 + 4 files changed, 37 insertions(+), 1 deletion(-) diff --git a/Source/Integration/PlayerControllerBase.cpp b/Source/Integration/PlayerControllerBase.cpp index 6b04f6c7..104c1924 100644 --- a/Source/Integration/PlayerControllerBase.cpp +++ b/Source/Integration/PlayerControllerBase.cpp @@ -256,6 +256,10 @@ 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 d7ea4fe8..7aadd4f7 100644 --- a/Source/Integration/RootCanvas.cpp +++ b/Source/Integration/RootCanvas.cpp @@ -54,6 +54,18 @@ int32 UlxRootCanvasPanel::GetMaxZOrder() const 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; @@ -95,7 +107,15 @@ void UlxRootCanvasPanel::SetWidgetWindowManagement(class UUserWidget *Widget, Widget->SetDesiredFocusWidget(DesiredFocusWidget); if (BringToFront) { - Slot->SetZOrder(Panel->GetMaxZOrder() + 1); + // 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; } } diff --git a/Source/Integration/RootCanvas.h b/Source/Integration/RootCanvas.h index f2759cbd..232a6382 100644 --- a/Source/Integration/RootCanvas.h +++ b/Source/Integration/RootCanvas.h @@ -91,6 +91,13 @@ 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 @@ -115,6 +122,10 @@ protected: // UPanelWidget virtual UClass* GetSlotClass() const override; + +public: + + bool MustPropagateZOrderToSlate = false; }; diff --git a/tools/UEDataFormatter.py b/tools/UEDataFormatter.py index ab20cdc3..2e1dfcb6 100644 --- a/tools/UEDataFormatter.py +++ b/tools/UEDataFormatter.py @@ -168,6 +168,7 @@ def UEFStringSummaryProvider(valobj, dict): ############################################################ def UEFNameSummaryProvider(valobj, dict): + valobj = valobj.GetNonSyntheticValue() target = valobj.GetTarget() process = target.GetProcess() entry_id = valobj.GetChildMemberWithName('DisplayIndex')